diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0d89bdf18ef..c72cf994640 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,6 +11,15 @@ updates: open-pull-requests-limit: 6 ignore: - dependency-name: "cbindgen" +- package-ecosystem: npm + directory: "/libraries/type-length-value/js" + schedule: + interval: daily + time: "02:00" + timezone: America/Los_Angeles + open-pull-requests-limit: 3 + labels: + - "automerge" - package-ecosystem: npm directory: "/token/js" schedule: @@ -65,3 +74,21 @@ updates: open-pull-requests-limit: 3 labels: - "automerge" +- package-ecosystem: npm + directory: "/single-pool/js/packages/classic" + schedule: + interval: daily + time: "05:00" + timezone: America/Los_Angeles + open-pull-requests-limit: 3 + labels: + - "automerge" +- package-ecosystem: npm + directory: "/single-pool/js/packages/modern" + schedule: + interval: daily + time: "05:00" + timezone: America/Los_Angeles + open-pull-requests-limit: 3 + labels: + - "automerge" diff --git a/.github/workflows/manage-stale-issues-and-prs.yml b/.github/workflows/manage-stale-issues-and-prs.yml new file mode 100644 index 00000000000..15e9a9e9e93 --- /dev/null +++ b/.github/workflows/manage-stale-issues-and-prs.yml @@ -0,0 +1,39 @@ +name: "Manage stale issues and PRs" +on: + # Chosen to be just before London wakes up and way past San Francisco's bedtime. + schedule: + - cron: "0 8 * * 1-5" # This is in UTC. + # Do a dry-run (debug-only: true) whenever this workflow itself is changed. + pull_request: + paths: + - .github/workflows/manage-stale-issues-and-prs.yml + types: + - opened + - synchronize + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v6 + with: + ascending: true # Spend API operations budget on older, more-likely-to-get-closed issues first + close-issue-message: "" # Leave no comment when closing + close-pr-message: "" # Leave no comment when closing + days-before-issue-stale: 365 + days-before-pr-stale: 14 + days-before-close: 7 + debug-only: ${{ github.event_name == 'pull_request' }} # Dry-run when true. + exempt-all-milestones: true # Milestones can sometimes last a month, so exempt issues attached to a milestone. + exempt-issue-labels: blocked,do-not-close,security + exempt-pr-labels: blocked,do-not-close,security + # No actual changes get made in debug-only mode, so we can raise the operations ceiling. + operations-per-run: ${{ github.event_name == 'pull_request' && 1000 || 900}} + stale-issue-label: stale + stale-issue-message: "" # Leave no comment when marking as stale + stale-pr-label: stale + stale-pr-message: "" # Leave no comment when marking as stale diff --git a/.github/workflows/pull-request-account-compression.yml b/.github/workflows/pull-request-account-compression.yml new file mode 100644 index 00000000000..b91a0b65a6a --- /dev/null +++ b/.github/workflows/pull-request-account-compression.yml @@ -0,0 +1,97 @@ +name: Account Compression Pull Request + +on: + pull_request: + paths: + - "account-compression/**" + - "libraries/concurrent-merkle-tree/**" + - "ci/*-version.sh" + - "ci/install-anchor.sh" + - ".github/workflows/pull-request-account-compression.yml" + push: + branches: [master] + paths: + - "account-compression/**" + - "libraries/concurrent-merkle-tree/**" + - "ci/*-version.sh" + - "ci/install-anchor.sh" + - ".github/workflows/pull-request-account-compression.yml" + +jobs: + anchor-build-account-compression: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + source ci/install-anchor.sh + echo "ANCHOR_CLI_VERSION=$anchor_cli_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE }} + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/rustfilt + key: cargo-sbf-bins-${{ runner.os }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Build and test programs + run: ./ci/cargo-test-sbf.sh account-compression + + - name: Upload programs + uses: actions/upload-artifact@v2 + with: + name: account-compression-programs + path: "account-compression/target/deploy/*.so" + if-no-files-found: error + + js-test-account-compression: + runs-on: ubuntu-latest + env: + NODE_VERSION: 16.x + needs: anchor-build-account-compression + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v1 + with: + node-version: ${{ env.NODE_VERSION }} + - uses: actions/cache@v2 + with: + path: ~/.cache/yarn + key: node-${{ hashFiles('account-compression/sdk/yarn.lock') }} + restore-keys: | + node- + - name: Download programs + uses: actions/download-artifact@v2 + with: + name: account-compression-programs + path: account-compression/target/deploy + - run: ./ci/js-test-account-compression.sh diff --git a/.github/workflows/pull-request-feature-gate.yml b/.github/workflows/pull-request-feature-gate.yml new file mode 100644 index 00000000000..dbe807348aa --- /dev/null +++ b/.github/workflows/pull-request-feature-gate.yml @@ -0,0 +1,60 @@ +name: Feature Gate Pull Request + +on: + pull_request: + paths: + - 'feature-gate/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-feature-gate.yml' + push: + branches: [master] + paths: + - 'feature-gate/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-feature-gate.yml' + +jobs: + cargo-test-sbf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE}} + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/rustfilt + key: cargo-sbf-bins-${{ runner.os }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Build and test + run: ./ci/cargo-test-sbf.sh feature-gate diff --git a/.github/workflows/pull-request-libraries.yml b/.github/workflows/pull-request-libraries.yml index 57dfa68b420..f0672abdb33 100644 --- a/.github/workflows/pull-request-libraries.yml +++ b/.github/workflows/pull-request-libraries.yml @@ -56,3 +56,21 @@ jobs: - name: Build and test run: ./ci/cargo-test-sbf.sh libraries + + js-test-tlv: + runs-on: ubuntu-latest + env: + NODE_VERSION: 16.x + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v1 + with: + node-version: ${{ env.NODE_VERSION }} + - uses: actions/cache@v2 + with: + path: ~/.npm + key: node-${{ hashFiles('libraries/type-length-value/js/package-lock.json') }} + restore-keys: | + node- + - run: ./ci/js-test-tlv.sh diff --git a/.github/workflows/pull-request-single-pool.yml b/.github/workflows/pull-request-single-pool.yml index 15da2722aac..38f6d32c058 100644 --- a/.github/workflows/pull-request-single-pool.yml +++ b/.github/workflows/pull-request-single-pool.yml @@ -3,15 +3,19 @@ name: Single-Validator Stake Pool Pull Request on: pull_request: paths: - - 'stake-pool/single-pool/**' + - 'single-pool/**' - 'token/**' + - 'associated-token-account/**' - 'ci/*-version.sh' + - '.github/workflows/pull-request-single-pool.yml' push: branches: [master] paths: - - 'stake-pool/single-pool/**' + - 'single-pool/**' - 'token/**' + - 'associated-token-account/**' - 'ci/*-version.sh' + - '.github/workflows/pull-request-single-pool.yml' jobs: cargo-test-sbf: @@ -57,7 +61,7 @@ jobs: echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH - name: Build and test - run: ./ci/cargo-test-sbf.sh stake-pool/single-pool + run: ./ci/cargo-test-sbf.sh single-pool/program - name: Upload programs uses: actions/upload-artifact@v2 @@ -65,3 +69,69 @@ jobs: name: single-pool-programs path: "target/deploy/*.so" if-no-files-found: error + + cargo-build-test-cli: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Build dependent programs + run: | + cargo build-sbf --manifest-path=single-pool/program/Cargo.toml + + - name: Build and test + run: | + cargo build --manifest-path ./single-pool/cli/Cargo.toml + cargo test --manifest-path ./single-pool/cli/Cargo.toml + + js-test: + runs-on: ubuntu-latest + env: + NODE_VERSION: 20 + needs: cargo-test-sbf + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + - uses: pnpm/action-setup@v2 + with: + version: 8 + - name: Download programs + uses: actions/download-artifact@v2 + with: + name: single-pool-programs + path: target/deploy + - run: ./ci/js-test-single-pool.sh diff --git a/.github/workflows/pull-request-stake-pool.yml b/.github/workflows/pull-request-stake-pool.yml index 16408049e2f..1f25061eba2 100644 --- a/.github/workflows/pull-request-stake-pool.yml +++ b/.github/workflows/pull-request-stake-pool.yml @@ -6,6 +6,7 @@ on: - 'stake-pool/**' - 'token/**' - 'ci/*-version.sh' + - 'ci/warning/purge-ubuntu-runner.sh' - '.github/workflows/pull-request-stake-pool.yml' push: branches: [master] @@ -13,6 +14,7 @@ on: - 'stake-pool/**' - 'token/**' - 'ci/*-version.sh' + - 'ci/warning/purge-ubuntu-runner.sh' - '.github/workflows/pull-request-stake-pool.yml' jobs: diff --git a/.github/workflows/pull-request-token-collection.yml b/.github/workflows/pull-request-token-collection.yml new file mode 100644 index 00000000000..6e305446c72 --- /dev/null +++ b/.github/workflows/pull-request-token-collection.yml @@ -0,0 +1,66 @@ +name: Token Collection Pull Request + +on: + pull_request: + paths: + - 'token-collection/**' + - 'token/**' + - 'token-group/**' + - 'token-metadata/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-token-collection.yml' + push: + branches: [master] + paths: + - 'token-collection/**' + - 'token/**' + - 'token-group/**' + - 'token-metadata/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-token-collection.yml' + +jobs: + cargo-test-sbf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE}} + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/rustfilt + key: cargo-sbf-bins-${{ runner.os }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Build and test + run: ./ci/cargo-test-sbf.sh token-collection diff --git a/.github/workflows/pull-request-token-group.yml b/.github/workflows/pull-request-token-group.yml new file mode 100644 index 00000000000..fce03fe5d22 --- /dev/null +++ b/.github/workflows/pull-request-token-group.yml @@ -0,0 +1,68 @@ +name: Token-Group Pull Request + +on: + pull_request: + paths: + - 'token-group/**' + - 'token/program-2022/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-token-group.yml' + push: + branches: [master] + paths: + - 'token-group/**' + - 'token/program-2022/**' + - 'ci/*-version.sh' + - '.github/workflows/pull-request-token-group.yml' + +jobs: + cargo-test-sbf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE}} + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/rustfilt + key: cargo-sbf-bins-${{ runner.os }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Test token-group interface + run: | + cargo test \ + --manifest-path=token-group/interface/Cargo.toml \ + -- --nocapture + + - name: Build and test example + run: ./ci/cargo-test-sbf.sh token-group/example diff --git a/.github/workflows/pull-request-token-lending.yml b/.github/workflows/pull-request-token-lending.yml index b8190c63f9f..67dd86d5904 100644 --- a/.github/workflows/pull-request-token-lending.yml +++ b/.github/workflows/pull-request-token-lending.yml @@ -69,7 +69,7 @@ jobs: js-test: runs-on: ubuntu-latest env: - NODE_VERSION: 16.x + NODE_VERSION: 18.x needs: cargo-test-sbf steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/pull-request-token-metadata.yml b/.github/workflows/pull-request-token-metadata.yml index e3508a7c099..32570f4df53 100644 --- a/.github/workflows/pull-request-token-metadata.yml +++ b/.github/workflows/pull-request-token-metadata.yml @@ -58,5 +58,30 @@ jobs: ./ci/install-program-deps.sh echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + - name: Test token-metadata with "serde" activated + run: | + cargo test \ + --manifest-path=token-metadata/interface/Cargo.toml \ + --features serde-traits \ + -- --nocapture + - name: Build and test example run: ./ci/cargo-test-sbf.sh token-metadata/example + + js-test: + runs-on: ubuntu-latest + env: + NODE_VERSION: 16.x + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v1 + with: + node-version: ${{ env.NODE_VERSION }} + - uses: actions/cache@v2 + with: + path: ~/.npm + key: node-${{ hashFiles('token-metadata/js/package-lock.json') }} + restore-keys: | + node- + - run: ./ci/js-test-token-metadata.sh diff --git a/.github/workflows/pull-request-token-upgrade.yml b/.github/workflows/pull-request-token-upgrade.yml index 2795c2e88b9..f1b321a4d17 100644 --- a/.github/workflows/pull-request-token-upgrade.yml +++ b/.github/workflows/pull-request-token-upgrade.yml @@ -99,9 +99,6 @@ jobs: - name: Build dependent programs run: | - cargo build-sbf --manifest-path ./token/program/Cargo.toml - cargo build-sbf --manifest-path ./token/program-2022/Cargo.toml - cargo build-sbf --manifest-path ./associated-token-account/program/Cargo.toml cargo build-sbf --manifest-path ./token-upgrade/program/Cargo.toml - name: Run CLI tests diff --git a/.github/workflows/pull-request-token.yml b/.github/workflows/pull-request-token.yml index 225e5adace4..335aebc04b3 100644 --- a/.github/workflows/pull-request-token.yml +++ b/.github/workflows/pull-request-token.yml @@ -21,6 +21,9 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Remove unneeded packages for more space + run: bash ./ci/warning/purge-ubuntu-runner.sh + - name: Set env vars run: | source ci/rust-version.sh @@ -143,7 +146,14 @@ jobs: echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH - name: Build and test transfer hook example - run: ./ci/cargo-test-sbf.sh token/transfer-hook-example + run: ./ci/cargo-test-sbf.sh token/transfer-hook/example + + - name: Upload program + uses: actions/upload-artifact@v2 + with: + name: spl-transfer-hook-example + path: "target/deploy/*.so" + if-no-files-found: error cargo-test-sbf-associated-token-account: runs-on: ubuntu-latest @@ -202,9 +212,12 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Remove unneeded packages for more space + run: bash ./ci/warning/purge-ubuntu-runner.sh + - name: Set env vars run: | - echo "RUST_STABLE_VERSION=1.69.0" >> $GITHUB_ENV + echo "RUST_STABLE_VERSION=1.72.0" >> $GITHUB_ENV source ci/rust-version.sh echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV source ci/solana-version.sh @@ -263,6 +276,11 @@ jobs: with: name: associated-token-account-program path: target/deploy + - name: Download spl-transfer-hook-example program + uses: actions/download-artifact@v2 + with: + name: spl-transfer-hook-example + path: target/deploy - run: ./ci/js-test-token.sh cargo-build-test-cli: @@ -305,3 +323,50 @@ jobs: run: | BUILD_DEPENDENT_PROGRAMS=1 cargo build --manifest-path ./token/cli/Cargo.toml cargo test --manifest-path ./token/cli/Cargo.toml + + cargo-build-test-transfer-hook-cli: + runs-on: ubuntu-latest + needs: [cargo-test-sbf] + steps: + - uses: actions/checkout@v2 + + - name: Set env vars + run: | + source ci/rust-version.sh + echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV + source ci/solana-version.sh + echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV + + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_STABLE }} + override: true + profile: minimal + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE }} + + - uses: actions/cache@v2 + with: + path: ~/.cache/solana + key: solana-${{ env.SOLANA_VERSION }} + + - name: Install dependencies + run: | + ./ci/install-build-deps.sh + ./ci/install-program-deps.sh + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Download spl-transfer-hook-example program + uses: actions/download-artifact@v2 + with: + name: spl-transfer-hook-example + path: target/deploy + + - name: Build and test + run: | + cargo test --manifest-path ./token/transfer-hook/cli/Cargo.toml diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index db186508bfc..1211692fa38 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -77,7 +77,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -Zunstable-options --workspace --all-targets --features test-sbf -- --deny=warnings --deny=clippy::integer_arithmetic + args: -Zunstable-options --workspace --all-targets --features test-sbf -- --deny=warnings --deny=clippy::arithmetic_side_effects audit: runs-on: ubuntu-latest @@ -109,6 +109,9 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Remove unneeded packages for more space + run: bash ./ci/warning/purge-ubuntu-runner.sh + - name: Set env vars run: | source ci/rust-version.sh diff --git a/Anchor.toml b/Anchor.toml index 82d5d98059e..9fa0a764bdf 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,5 +1,5 @@ -anchor_version = "0.24.2" -solana_version = "1.16.1" +anchor_version = "0.29.0" +solana_version = "1.17.2" [workspace] members = [ diff --git a/Cargo.lock b/Cargo.lock index 6a65f8d476f..b5bb3fac752 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ "regex", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -140,6 +149,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + [[package]] name = "anyhow" version = "1.0.58" @@ -157,9 +172,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.0.3" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" dependencies = [ "derive_arbitrary", ] @@ -211,7 +226,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", "paste", "rustc_version", @@ -224,7 +239,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.26", + "quote", "syn 1.0.107", ] @@ -234,10 +249,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -263,7 +278,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint 0.4.3", + "num-bigint 0.4.4", ] [[package]] @@ -272,8 +287,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -287,12 +302,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "array-bytes" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad284aeb45c13f2fb4f084de4a420ebf447423bdf9386c0540ce33cb3ef4b8c" - [[package]] name = "arrayref" version = "0.3.7" @@ -301,9 +310,9 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "ascii" @@ -324,7 +333,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.22", + "time", ] [[package]] @@ -333,8 +342,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", "synstructure", ] @@ -345,18 +354,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "assert_cmd" -version = "2.0.5" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5c2ca00549910ec251e3bd15f87aeeb206c9456b9a77b43ff6c97c54042a472" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ - "bstr", + "anstyle", + "bstr 1.6.0", "doc-comment", "predicates", "predicates-core", @@ -372,9 +382,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", @@ -383,9 +393,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.3.14" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "f658e2baef915ba0f26f1f7c42bfb8e12f532a01f449a090ded75ae7a07e9ba2" dependencies = [ "brotli", "flate2", @@ -420,20 +430,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "async-trait" -version = "0.1.69" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -464,13 +474,13 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.7" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -486,7 +496,6 @@ dependencies = [ "serde", "sync_wrapper", "tower", - "tower-http", "tower-layer", "tower-service", ] @@ -522,6 +531,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.12.3" @@ -536,9 +560,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -573,19 +597,19 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", "lazycell", "peeking_take_while", "prettyplease 0.2.4", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] @@ -609,6 +633,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + [[package]] name = "bitmaps" version = "2.1.0" @@ -620,9 +653,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", @@ -695,7 +728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.12.3", + "hashbrown 0.13.2", ] [[package]] @@ -707,7 +740,7 @@ dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.60", + "proc-macro2", "syn 1.0.107", ] @@ -720,7 +753,7 @@ dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.60", + "proc-macro2", "syn 1.0.107", ] @@ -730,8 +763,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -741,8 +774,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -752,8 +785,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -763,8 +796,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -801,9 +834,18 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ - "lazy_static", "memchr", - "regex-automata", +] + +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata 0.3.0", + "serde", ] [[package]] @@ -830,9 +872,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" dependencies = [ "bytemuck_derive", ] @@ -843,8 +885,8 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -856,9 +898,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bzip2" @@ -899,10 +941,10 @@ checksum = "9344318b9c787667b95cd2c5124f5eaf2bde35e959dd01ea04fc5b234c542c11" dependencies = [ "clap 2.34.0", "heck 0.3.3", - "indexmap", + "indexmap 1.9.3", "log", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "serde", "serde_json", "syn 1.0.107", @@ -912,11 +954,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -949,25 +992,24 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.44", "wasm-bindgen", - "winapi 0.3.9", + "windows-targets 0.48.0", ] [[package]] name = "chrono-humanize" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" dependencies = [ "chrono", ] @@ -1000,7 +1042,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", "textwrap 0.11.0", "unicode-width", @@ -1014,15 +1056,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", + "clap_derive", "clap_lex", - "indexmap", + "indexmap 1.9.3", "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.16.0", ] +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -1105,9 +1161,9 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "constant_time_eq" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "convert_case" @@ -1275,8 +1331,8 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "scratch", "syn 1.0.107", ] @@ -1293,16 +1349,16 @@ version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "darling" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -1310,27 +1366,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "strsim 0.10.0", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] name = "darling_macro" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", - "quote 1.0.26", - "syn 2.0.15", + "quote", + "syn 2.0.28", ] [[package]] @@ -1379,7 +1435,7 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", "rusticata-macros", ] @@ -1396,20 +1452,20 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "derive_arbitrary" -version = "1.0.2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b24629208e87a2d8b396ff43b15c4afb0a69cea3fbbaa9ed9b92b7c02f0aed73" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -1419,8 +1475,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "rustc_version", "syn 1.0.107", ] @@ -1508,32 +1564,32 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] -name = "dlopen" -version = "0.1.8" +name = "dlopen2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" dependencies = [ - "dlopen_derive", - "lazy_static", + "dlopen2_derive", "libc", + "once_cell", "winapi 0.3.9", ] [[package]] -name = "dlopen_derive" -version = "0.1.4" +name = "dlopen2_derive" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ - "libc", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -1590,16 +1646,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -1631,9 +1687,9 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -1642,23 +1698,23 @@ version = "3.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" dependencies = [ - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "enum_dispatch" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -1674,6 +1730,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -1697,16 +1759,17 @@ dependencies = [ [[package]] name = "etcd-client" -version = "0.8.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bfae4cb9cd8c3c2a552d45e155cafd079f385a3b9421b9a010878f44531f1e" +checksum = "f4b0ea5ef6dc2388a4b1669fa32097249bc03a15417b97cb75e38afb309e4a89" dependencies = [ "http", - "prost 0.9.0", + "prost", "tokio", "tokio-stream", - "tonic 0.6.2", - "tonic-build 0.6.2", + "tonic", + "tonic-build", + "tower", "tower-service", ] @@ -1733,12 +1796,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "feature-probe" @@ -1775,9 +1835,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1813,6 +1873,12 @@ dependencies = [ "percent-encoding 2.3.0", ] +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + [[package]] name = "fs_extra" version = "1.3.0" @@ -1827,9 +1893,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -1842,9 +1908,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1852,15 +1918,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1870,38 +1936,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures 0.1.31", "futures-channel", @@ -1972,6 +2038,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "glob" version = "0.3.0" @@ -1985,7 +2057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" dependencies = [ "aho-corasick 0.7.18", - "bstr", + "bstr 0.2.17", "fnv", "log", "regex", @@ -1998,7 +2070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8af59a261bcf42f45d1b261232847b9b850ba0a1419d6100698246fb66e9240" dependencies = [ "arc-swap", - "futures 0.3.28", + "futures 0.3.29", "log", "reqwest", "serde", @@ -2006,7 +2078,7 @@ dependencies = [ "serde_json", "simpl", "smpl_jwt", - "time 0.3.22", + "time", "tokio", ] @@ -2033,7 +2105,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util 0.7.1", @@ -2076,6 +2148,12 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + [[package]] name = "headers" version = "0.3.7" @@ -2083,7 +2161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64 0.13.0", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -2127,18 +2205,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -2148,9 +2217,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hidapi" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f841dbb77615e116fb2ca38044b42310370f0d093c774a72361670ff2ae431b" +checksum = "723777263b0dcc5730aec947496bd8c3940ba63c15f5633b288cc615f4f6af79" dependencies = [ "cc", "libc", @@ -2228,12 +2297,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -2254,9 +2317,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2269,7 +2332,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2283,7 +2346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" dependencies = [ "bytes", - "futures 0.3.28", + "futures 0.3.29", "headers", "http", "hyper", @@ -2296,15 +2359,16 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.20.8", + "rustls", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls", ] [[package]] @@ -2419,15 +2483,26 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", "rayon", "serde", ] [[package]] name = "indicatif" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" dependencies = [ "console", "instant", @@ -2445,17 +2520,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.3.1" @@ -2473,9 +2537,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -2513,7 +2577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.28", + "futures 0.3.29", "jsonrpc-core", "jsonrpc-pubsub", "log", @@ -2528,7 +2592,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.28", + "futures 0.3.29", "futures-executor", "futures-util", "log", @@ -2543,7 +2607,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.28", + "futures 0.3.29", "jsonrpc-client-transports", ] @@ -2554,8 +2618,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -2565,7 +2629,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures 0.3.28", + "futures 0.3.29", "hyper", "jsonrpc-core", "jsonrpc-server-utils", @@ -2581,7 +2645,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.28", + "futures 0.3.29", "jsonrpc-core", "lazy_static", "log", @@ -2597,7 +2661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes", - "futures 0.3.28", + "futures 0.3.29", "globset", "jsonrpc-core", "lazy_static", @@ -2641,9 +2705,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" @@ -2657,9 +2721,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "librocksdb-sys" @@ -2735,6 +2799,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "light-poseidon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949bdd22e4ed93481d45e9a6badb34b99132bcad0c8a8d4f05c42f7dcc7b90bc" +dependencies = [ + "ark-bn254", + "ark-ff", + "thiserror", +] + [[package]] name = "link-cplusplus" version = "1.0.7" @@ -2746,27 +2821,25 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" @@ -2817,9 +2890,9 @@ checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -2898,24 +2971,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.14" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", - "miow", - "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] @@ -2934,8 +2996,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -2976,16 +3038,15 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset 0.7.1", "pin-utils", - "static_assertions", ] [[package]] @@ -2998,14 +3059,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "ntapi" -version = "0.3.7" -source = "git+https://github.com/solana-labs/ntapi?rev=97ede981a1777883ff86d142b75024b023f04fad#97ede981a1777883ff86d142b75024b023f04fad" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "num" version = "0.2.1" @@ -3033,9 +3086,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -3058,20 +3111,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "num-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -3109,9 +3162,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -3119,54 +3172,54 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.3", "libc", ] [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ - "num_enum_derive 0.5.11", + "num_enum_derive 0.6.1", ] [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive 0.6.1", + "num_enum_derive 0.7.1", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.1.0", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro-crate 1.1.0", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -3175,6 +3228,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.6.0" @@ -3186,9 +3248,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -3208,7 +3270,7 @@ version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "foreign-types", "libc", @@ -3223,8 +3285,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -3299,8 +3361,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -3439,8 +3501,8 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -3462,7 +3524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -3480,16 +3542,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -3546,10 +3608,11 @@ checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "predicates" -version = "2.1.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ + "anstyle", "difflib", "itertools", "predicates-core", @@ -3557,9 +3620,9 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" @@ -3583,7 +3646,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" dependencies = [ - "proc-macro2 1.0.60", + "proc-macro2", "syn 1.0.107", ] @@ -3593,8 +3656,8 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ - "proc-macro2 1.0.60", - "syn 2.0.15", + "proc-macro2", + "syn 2.0.28", ] [[package]] @@ -3623,8 +3686,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", "version_check", ] @@ -3635,59 +3698,40 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "version_check", ] [[package]] name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.60" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", - "bitflags", - "byteorder", + "bit-vec", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.27", + "regex-syntax 0.7.2", "rusty-fork", "tempfile", "unarray", ] -[[package]] -name = "prost" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" -dependencies = [ - "bytes", - "prost-derive 0.9.0", -] - [[package]] name = "prost" version = "0.11.9" @@ -3695,27 +3739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost-build" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" -dependencies = [ - "bytes", - "heck 0.3.3", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prost 0.9.0", - "prost-types 0.9.0", - "regex", - "tempfile", - "which", + "prost-derive", ] [[package]] @@ -3732,8 +3756,8 @@ dependencies = [ "multimap", "petgraph", "prettyplease 0.1.9", - "prost 0.11.9", - "prost-types 0.11.9", + "prost", + "prost-types", "regex", "syn 1.0.107", "tempfile", @@ -3742,65 +3766,53 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.9.0" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] -name = "prost-derive" +name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "anyhow", - "itertools", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "prost", ] [[package]] -name = "prost-types" -version = "0.9.0" +name = "protobuf-src" +version = "1.1.0+21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" dependencies = [ - "bytes", - "prost 0.9.0", + "autotools", ] [[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "protobuf-src" -version = "1.1.0+21.5" +name = "qstring" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "autotools", + "percent-encoding 2.3.0", ] [[package]] -name = "qstring" -version = "0.7.2" +name = "qualifier_attr" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ - "percent-encoding 2.3.0", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -3811,70 +3823,59 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.20.8", + "rustls", "thiserror", "tokio", "tracing", - "webpki 0.22.0", ] [[package]] name = "quinn-proto" -version = "0.9.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +checksum = "2c78e758510582acc40acb90458401172d41f1016f8c9dde89e49677afb7eec1" dependencies = [ "bytes", "rand 0.8.5", "ring", "rustc-hash", - "rustls 0.20.8", + "rustls", "rustls-native-certs", "slab", "thiserror", "tinyvec", "tracing", - "webpki 0.22.0", ] [[package]] name = "quinn-udp" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ + "bytes", "libc", - "quinn-proto", - "socket2", + "socket2 0.5.4", "tracing", - "windows-sys 0.42.0", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", + "windows-sys 0.48.0", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ - "proc-macro2 1.0.60", + "proc-macro2", ] [[package]] @@ -4006,7 +4007,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.22", + "time", "yasna", ] @@ -4016,16 +4017,16 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4055,26 +4056,32 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick 1.0.2", "memchr", - "regex-syntax 0.7.2", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick 1.0.2", + "memchr", + "regex-syntax 0.8.2", +] [[package]] name = "regex-syntax" @@ -4082,14 +4089,20 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "async-compression", - "base64 0.21.2", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -4108,21 +4121,22 @@ dependencies = [ "once_cell", "percent-encoding 2.3.0", "pin-project-lite", - "rustls 0.20.8", + "rustls", "rustls-pemfile 1.0.1", "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.2", + "tokio-rustls", "tokio-util 0.7.1", "tower-service", - "url 2.4.0", + "url 2.4.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.2", "winreg", ] @@ -4219,13 +4233,12 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys 0.48.0", @@ -4233,27 +4246,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.0", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.8" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", - "sct 0.7.0", - "webpki 0.22.0", + "rustls-webpki", + "sct", ] [[package]] @@ -4286,11 +4286,21 @@ dependencies = [ "base64 0.13.0", ] +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rusty-fork" @@ -4331,9 +4341,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" @@ -4356,21 +4366,11 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sct" version = "0.7.0" @@ -4387,7 +4387,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4406,44 +4406,44 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.99" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -4468,14 +4468,25 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ - "base64 0.13.0", + "serde", + "serde_with_macros 2.3.3", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", + "indexmap 2.0.2", "serde", "serde_json", - "serde_with_macros", - "time 0.3.22", + "serde_with_macros 3.4.0", + "time", ] [[package]] @@ -4485,18 +4496,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "serde_yaml" -version = "0.9.21" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ - "indexmap", + "indexmap 2.0.2", "itoa", "ryu", "serde", @@ -4510,7 +4533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ "dashmap 5.2.0", - "futures 0.3.28", + "futures 0.3.29", "lazy_static", "log", "parking_lot 0.12.0", @@ -4523,9 +4546,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -4564,6 +4587,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.9.8" @@ -4612,38 +4646,50 @@ dependencies = [ [[package]] name = "shank" -version = "0.0.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9986b297e6f42bcbe41f95ca2a881e7a75bd091549e95ecf495fa8178c0c1b" +checksum = "4bc647510fc0d94a936f7367a9e3e30edbefd410068747ce4d4084b19b5fdbab" dependencies = [ "shank_macro", ] [[package]] name = "shank_macro" -version = "0.0.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd835154aa943dfc958f5a208f2e82b7d2a2c52b024229168c211ecc2d2bfd1" +checksum = "aa9ec31d18dcca23f2270ccf69766a99156792da3aed050dc5713af0e778b4f2" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "shank_macro_impl", + "shank_render", "syn 1.0.107", ] [[package]] name = "shank_macro_impl" -version = "0.0.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99ad9d5137704e86e2e4a54a121b9c443c37b60ce6638a7723ab700dbd2232a" +checksum = "ccaeb1359cf545507b5d18a637cc5cf192c7dcc8c737dff466013ca2d9e792c7" dependencies = [ "anyhow", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "serde", "syn 1.0.107", ] +[[package]] +name = "shank_render" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63f0b763c4c767dfb462a884d5f5b9b99fdd0a214c910d95792eb02a13b3d69" +dependencies = [ + "proc-macro2", + "quote", + "shank_macro_impl", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -4686,6 +4732,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a30f10c911c0355f80f1c2faa8096efc4a58cdf8590b954d5b395efa071c711" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -4724,7 +4776,7 @@ dependencies = [ "serde_derive", "serde_json", "simpl", - "time 0.3.22", + "time", ] [[package]] @@ -4737,6 +4789,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -4745,7 +4807,7 @@ checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ "base64 0.13.0", "bytes", - "futures 0.3.28", + "futures 0.3.29", "httparse", "log", "rand 0.8.5", @@ -4754,12 +4816,12 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380584cb468a1eb2ea79495c0c574ca09a11bf6796093b97a48a94cd98fe5b30" +checksum = "ea108fb41a4d6c3bafaf7d78fa94a6c2c6e863dc4e773c8e57dec22161be42b0" dependencies = [ "Inflector", - "base64 0.21.2", + "base64 0.21.5", "bincode", "bs58", "bv", @@ -4767,20 +4829,79 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "solana-address-lookup-table-program", "solana-config-program", "solana-sdk", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-metadata-interface 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", "zstd", ] +[[package]] +name = "solana-accounts-db" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1667ba2a54bf033667d17eaf3b12a7aee362b7b55725a6b594b67cb8e8fc7f" +dependencies = [ + "arrayref", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap 4.0.2", + "flate2", + "fnv", + "fs-err", + "im", + "index_list", + "itertools", + "lazy_static", + "log", + "lz4", + "memmap2", + "modular-bitfield", + "num-derive 0.3.3", + "num-traits", + "num_cpus", + "num_enum 0.6.1", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "serde", + "serde_derive", + "solana-bucket-map", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", + "static_assertions", + "strum 0.24.1", + "strum_macros 0.24.3", + "tar", + "tempfile", + "thiserror", +] + [[package]] name = "solana-address-lookup-table-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcdcbe2a61849b7692ac9eb6ad567ea4f333c36272d5d12de554aa2f60fa792" +checksum = "d2ee56e31d9b1733f874bfe5b54abef43fb49810f7291deccbdf713527b44b6f" dependencies = [ "bincode", "bytemuck", @@ -4799,12 +4920,12 @@ dependencies = [ [[package]] name = "solana-banks-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a0261cec6c6b32cfa7a533f3c03af7cd5f272300e408ca383637b9652dce1" +checksum = "4118d1d75cecd79defb0f8de61e428d186fb873d17641e817f80caed202a5647" dependencies = [ "borsh 0.10.3", - "futures 0.3.28", + "futures 0.3.29", "solana-banks-interface", "solana-program", "solana-sdk", @@ -4816,9 +4937,9 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cd8b0b4e8ce8523b00416fda178faa1067f905271b834ff55884f79a9b892e" +checksum = "1fdea91839646ec597a1dd6f5e884cb5c30d574db21970fcc20b04deb4747d96" dependencies = [ "serde", "solana-sdk", @@ -4827,13 +4948,14 @@ dependencies = [ [[package]] name = "solana-banks-server" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0e042e6727d08efb7e3860694510c9623e77e0afb59236c71d103503f4d905" +checksum = "faa79d64545dcb21680e6939ed63bd9e88425f55a098492d96c9407c7a5b4990" dependencies = [ "bincode", "crossbeam-channel", - "futures 0.3.28", + "futures 0.3.29", + "solana-accounts-db", "solana-banks-interface", "solana-client", "solana-runtime", @@ -4846,14 +4968,14 @@ dependencies = [ [[package]] name = "solana-bloom" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91509fff4d59db93a0b128fbcbd36553f6b2a138883b076e935986b1665315b" +checksum = "427cfd51bc19173aed04ea9d9f47861dc3bdc0bd6c1a06177823f6085ff94b6e" dependencies = [ "bv", "fnv", "log", - "rand 0.7.3", + "rand 0.8.5", "rayon", "rustc_version", "serde", @@ -4865,15 +4987,15 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c29c3a91b5ec61b2dc3b944f85cd94fb67dab6a0f255e61c4655422587c6789d" +checksum = "91dc36edd5b4d37b852accbed3bf41dde302954d7716565700e0fd81e0f5dced" dependencies = [ "bincode", "byteorder", "libsecp256k1", "log", - "rand 0.7.3", + "scopeguard", "solana-measure", "solana-program-runtime", "solana-sdk", @@ -4884,16 +5006,17 @@ dependencies = [ [[package]] name = "solana-bucket-map" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29185879e30def3ae9eae8802267737eaf11fa663732dab0587bec1423145878" +checksum = "36bf733cdfccac6bf2cb1e74f3bd247bd59c2024fc72760cf3ac4cb334e6b86d" dependencies = [ "bv", + "bytemuck", "log", "memmap2", "modular-bitfield", "num_enum 0.6.1", - "rand 0.7.3", + "rand 0.8.5", "solana-measure", "solana-sdk", "tempfile", @@ -4901,46 +5024,44 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe8a1443f3efac9c351c0b55efa39df488e73bcdd512272cb39e58198dcf9cc" +checksum = "1114079301af35bed62dba298beaad478a3788cbc376def9a56b8e9d26fc1b8f" dependencies = [ "chrono", "clap 2.34.0", "rpassword", - "solana-perf", "solana-remote-wallet", "solana-sdk", "thiserror", "tiny-bip39", "uriparse", - "url 2.4.0", + "url 2.4.1", ] [[package]] name = "solana-clap-v3-utils" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c8036dd4e178b0b8e5f68ed14171eabf6ea8d11513cece8624d5250d978ed0" +checksum = "fb1a68ec80502bc7793b3f715934a8d93a092fbc5926e0f48fb5f7e90aa5df96" dependencies = [ "chrono", "clap 3.2.25", "rpassword", - "solana-perf", "solana-remote-wallet", "solana-sdk", "solana-zk-token-sdk", "thiserror", "tiny-bip39", "uriparse", - "url 2.4.0", + "url 2.4.1", ] [[package]] name = "solana-cli-config" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1514a6f6d8728cd8654f5bc04922a9c226c45289f5baf81f5ae79cf4fe51c53c" +checksum = "7967e8d158c82b99a3eb3775ff695ee0f87a0abc7df169dbe3e054777e08e72a" dependencies = [ "dirs-next", "lazy_static", @@ -4949,17 +5070,17 @@ dependencies = [ "serde_yaml", "solana-clap-utils", "solana-sdk", - "url 2.4.0", + "url 2.4.1", ] [[package]] name = "solana-cli-output" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2f10f5658ed127e55eb7c2863f7c434fd162d61ed77c548fe59ce597fd02a2" +checksum = "c3b3e410b5665a44951cc726a4b61f38912de6c10ce47861c24d5bb1f9775de6" dependencies = [ "Inflector", - "base64 0.21.2", + "base64 0.21.5", "chrono", "clap 2.34.0", "console", @@ -4976,24 +5097,24 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-vote-program", - "spl-memo 3.0.1", + "spl-memo 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "solana-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6294e67101c8df185e9c371293ff2502bf5b35d4d70eb68472eae135692e5" +checksum = "fcc2d00016dc7975d866dd9ef3ffdd7c6dffec972495645953c07e587330e135" dependencies = [ "async-trait", "bincode", - "futures 0.3.28", + "dashmap 4.0.2", + "futures 0.3.29", "futures-util", - "indexmap", + "indexmap 2.0.2", "indicatif", "log", "quinn", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -5014,9 +5135,9 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8063d91848e305210143dd61d23a87d2a6f7a74db0ccca1b25dae3e1800bcd" +checksum = "33e650787669f715616444f32bafabf1fe17240d9154e182f83c3949bdd3d627" dependencies = [ "solana-program-runtime", "solana-sdk", @@ -5024,9 +5145,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44cd8bbbc468503ca8d337c91a6f6049ced080be31854d866a25974f1b90619" +checksum = "96a1f220474e7b9ed04cf450b13fa5ff4d03e7b7acefd36edfbe3d4c8d3f4ebb" dependencies = [ "bincode", "chrono", @@ -5038,16 +5159,17 @@ dependencies = [ [[package]] name = "solana-connection-cache" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e953284f00e9b706032fe36b30054f3c804f37a363fac7d8cb1753a1737cf20b" +checksum = "9ac0b83f1486f474424f77d2af10608305cac2e1184fde39a13c2a4a8e4c7cc8" dependencies = [ "async-trait", "bincode", + "crossbeam-channel", "futures-util", - "indexmap", + "indexmap 2.0.2", "log", - "rand 0.7.3", + "rand 0.8.5", "rayon", "rcgen", "solana-measure", @@ -5059,18 +5181,20 @@ dependencies = [ [[package]] name = "solana-core" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3211ec7e94cdcad2a3f9963b68f27c799d214f0a81668fac5b01a9c04afcc9" +checksum = "7d6780f9f71ad19116242ef8c5a992742fe91308e6a6ea031eba77e4528c8c6c" dependencies = [ - "base64 0.21.2", + "base64 0.21.5", "bincode", "bs58", + "bytes", "chrono", "crossbeam-channel", "dashmap 4.0.2", "eager", "etcd-client", + "futures 0.3.29", "histogram", "itertools", "lazy_static", @@ -5078,16 +5202,21 @@ dependencies = [ "lru", "min-max-heap", "num_enum 0.6.1", - "rand 0.7.3", - "rand_chacha 0.2.2", + "quinn", + "rand 0.8.5", + "rand_chacha 0.3.1", "rayon", + "rcgen", "rolling-file", "rustc_version", + "rustls", "serde", + "serde_bytes", "serde_derive", - "solana-address-lookup-table-program", + "solana-accounts-db", "solana-bloom", "solana-client", + "solana-cost-model", "solana-entry", "solana-frozen-abi", "solana-frozen-abi-macro", @@ -5100,6 +5229,7 @@ dependencies = [ "solana-perf", "solana-poh", "solana-program-runtime", + "solana-quic-client", "solana-rayon-threadlimit", "solana-rpc", "solana-rpc-client-api", @@ -5109,7 +5239,9 @@ dependencies = [ "solana-streamer", "solana-tpu-client", "solana-transaction-status", + "solana-turbine", "solana-version", + "solana-vote", "solana-vote-program", "strum 0.24.1", "strum_macros 0.24.3", @@ -5121,19 +5253,42 @@ dependencies = [ "trees", ] +[[package]] +name = "solana-cost-model" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77597dfb4f40a753218811c06647d6bf63d31060f67336212acff6c1282efb8e" +dependencies = [ + "lazy_static", + "log", + "rustc_version", + "solana-address-lookup-table-program", + "solana-bpf-loader-program", + "solana-compute-budget-program", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-loader-v4-program", + "solana-metrics", + "solana-program-runtime", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", +] + [[package]] name = "solana-entry" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f31e39222dd230fd2d202728ddf3ee23d626115faeb91defc8eb5eb5ed69c5" +checksum = "6a5d41f20906f2d20724356981ca3ba9778748c1f7f2051cf01f6ff9d0378770" dependencies = [ "bincode", "crossbeam-channel", - "dlopen", - "dlopen_derive", + "dlopen2", "lazy_static", "log", - "rand 0.7.3", + "rand 0.8.5", "rayon", "serde", "solana-measure", @@ -5146,9 +5301,9 @@ dependencies = [ [[package]] name = "solana-faucet" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a2d6ff5012926899420e610338bd26f4490b3e6be211cb5dc7e2c330208e2ef" +checksum = "34943471edf229a2f159ae62bf59825298a0fc5980e38108c578c40cb008f90f" dependencies = [ "bincode", "byteorder", @@ -5163,16 +5318,16 @@ dependencies = [ "solana-metrics", "solana-sdk", "solana-version", - "spl-memo 3.0.1", + "spl-memo 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", "tokio", ] [[package]] name = "solana-frozen-abi" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea8be57163366de9ffee3652cd42ea02fd5cca672408722f1426eb0d234a330" +checksum = "098378043a888c680de07eee987bf927e23e0720b472ba61c753d7b3757e6b3e" dependencies = [ "ahash 0.8.3", "blake3", @@ -5183,13 +5338,10 @@ dependencies = [ "cc", "either", "generic-array 0.14.7", - "getrandom 0.1.16", "im", "lazy_static", "log", "memmap2", - "once_cell", - "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", @@ -5203,21 +5355,21 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4121f91307234cec8c8d8cd2d7ec171881c07db2222335e8c840d555ebe89cc" +checksum = "c5f48c89a8a3a12f6a409a45fef50dae642211eae0207a91f01211aebb270026" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "rustc_version", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] name = "solana-geyser-plugin-interface" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8bcaf5e09940bebbc0342257ac695e4bb6e4a996c68f223de16d2649c7b32a" +checksum = "db839ede8d1a65d091d8aafe5488945d864465cd283dfd0cf7d916d7732dfd3d" dependencies = [ "log", "solana-sdk", @@ -5227,9 +5379,9 @@ dependencies = [ [[package]] name = "solana-geyser-plugin-manager" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b9ffe9e4655acac82e6b720f6b42b8a0f84930c17a01a50754ff46e6fcd35" +checksum = "14eafd17b39bfc98cfa39f03ba19fd707e8d9adbe5a28f1cea13824eda946a7b" dependencies = [ "bs58", "crossbeam-channel", @@ -5239,6 +5391,7 @@ dependencies = [ "libloading", "log", "serde_json", + "solana-accounts-db", "solana-entry", "solana-geyser-plugin-interface", "solana-ledger", @@ -5253,25 +5406,26 @@ dependencies = [ [[package]] name = "solana-gossip" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d09279da24361e2a1997df585d08b682e311c96c81705cadfeb448cf6829f06" +checksum = "08c69bbc5f324a351ac2865bb5ace113f5030ce794de51a367581a7d00c17c97" dependencies = [ + "assert_matches", "bincode", "bv", "clap 2.34.0", "crossbeam-channel", "flate2", - "indexmap", + "indexmap 2.0.2", "itertools", "log", "lru", - "matches", "num-traits", - "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", + "rand_chacha 0.3.1", "rayon", "rustc_version", + "rustversion", "serde", "serde_bytes", "serde_derive", @@ -5294,6 +5448,7 @@ dependencies = [ "solana-thin-client", "solana-tpu-client", "solana-version", + "solana-vote", "solana-vote-program", "static_assertions", "thiserror", @@ -5301,20 +5456,20 @@ dependencies = [ [[package]] name = "solana-ledger" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15762f70be414cbf9f49b6564e5e30a2c87a4bd4734df8128380682252ee78c" +checksum = "3a3b30f13fd36aca0ba09274a75c56ad36fd61808758964a9ab55a82993f139a" dependencies = [ "assert_matches", "bincode", - "bitflags", + "bitflags 2.4.1", "byteorder", "chrono", "chrono-humanize", "crossbeam-channel", "dashmap 4.0.2", "fs_extra", - "futures 0.3.28", + "futures 0.3.29", "itertools", "lazy_static", "libc", @@ -5322,9 +5477,9 @@ dependencies = [ "lru", "num_cpus", "num_enum 0.6.1", - "prost 0.11.9", - "rand 0.7.3", - "rand_chacha 0.2.2", + "prost", + "rand 0.8.5", + "rand_chacha 0.3.1", "rayon", "reed-solomon-erasure", "rocksdb", @@ -5334,7 +5489,9 @@ dependencies = [ "serde_bytes", "sha2 0.10.7", "solana-account-decoder", + "solana-accounts-db", "solana-bpf-loader-program", + "solana-cost-model", "solana-entry", "solana-frozen-abi", "solana-frozen-abi-macro", @@ -5349,10 +5506,13 @@ dependencies = [ "solana-storage-bigtable", "solana-storage-proto", "solana-transaction-status", + "solana-vote", "solana-vote-program", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions", + "strum 0.24.1", + "strum_macros 0.24.3", "tempfile", "thiserror", "tokio", @@ -5362,12 +5522,11 @@ dependencies = [ [[package]] name = "solana-loader-v4-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ce633092f454f38f3bebf4e0af8bd4af391a2fb4fd07538a2932addc2b2f36" +checksum = "a86dfb559013436f20bbf6ae56323630319d853c82836e1a9a3dcae1180b9497" dependencies = [ "log", - "rand 0.7.3", "solana-measure", "solana-program-runtime", "solana-sdk", @@ -5376,9 +5535,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f590904a129707c5bf6b9f2e198e49ce8984e802dad26babd2c406a4e898b0ce" +checksum = "d7fa5daba09e7c2bc579b0d3151f649496f148a0ac2054bb042915fbfe9c1678" dependencies = [ "env_logger", "lazy_static", @@ -5387,9 +5546,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26976359ec46d158ce2880d5a31bca4b08e971d29d03c25e9ba80f9a3d42b28e" +checksum = "e87403b9fb2abd9f2f2b2879ca9e56e14b5ae58f145466e3e893a7810bcbceff" dependencies = [ "log", "solana-sdk", @@ -5397,20 +5556,19 @@ dependencies = [ [[package]] name = "solana-merkle-tree" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb2670529a0395d359efb9b2673a73358b67ecd37bc6b2f60698b6c0d9a38d" +checksum = "9ae716e57241958a1a2162099a7405c377fe7f192425259604ce61d3e93bbf8e" dependencies = [ "fast-math", - "matches", "solana-program", ] [[package]] name = "solana-metrics" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d97a86ca9bc1c33b77f98502ef0ec133823ebbb813fc60e10c6cf6377bbbdf2" +checksum = "e4b504a682911c4bf85b21278720870555f5996e2991daf54ea4ce300012e93e" dependencies = [ "crossbeam-channel", "gethostname", @@ -5418,49 +5576,49 @@ dependencies = [ "log", "reqwest", "solana-sdk", + "thiserror", ] [[package]] name = "solana-net-utils" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a63f59abfa214c43b2b0d0360f7a50c180a0e86ea9a16ad7598ca0c9d8843483" +checksum = "a573d966fd61609501401dc1350b6ba1d0dee91c166453ec77b2fb312e8072b1" dependencies = [ "bincode", "clap 3.2.25", "crossbeam-channel", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_derive", - "socket2", + "socket2 0.5.4", "solana-logger", "solana-sdk", "solana-version", "tokio", - "url 2.4.0", + "url 2.4.1", ] [[package]] name = "solana-perf" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7219016f9a81863decafd5256ec0d32d57a8c4e9529f9134fd8727684cc0936" +checksum = "fcffede3b37ccdddbd57f93e4228b268f05c0a5cb44cb6e382ed8a31a15f5573" dependencies = [ "ahash 0.8.3", "bincode", "bv", "caps", "curve25519-dalek", - "dlopen", - "dlopen_derive", + "dlopen2", "fnv", "lazy_static", "libc", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "rayon", "serde", "solana-metrics", @@ -5471,9 +5629,9 @@ dependencies = [ [[package]] name = "solana-poh" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444bdf086cc03ccea3e1fcdb2d9fc1db5a5660afad26dbc7dd5c444b5f4cd213" +checksum = "85dcacf7c768ff43bea34ba4eb9fc11bfc5e8f1b4c2134ef5047f2a1a9e8ce87" dependencies = [ "core_affinity", "crossbeam-channel", @@ -5489,20 +5647,20 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ac28d05adeff2212cdec76dfacc6eb631d69d065b1b83c063a1fab505d5e62" +checksum = "6e8834ffcd3773375e7fce4b261efefebbb64409da78e1627cbc4c2632139921" dependencies = [ "ark-bn254", "ark-ec", "ark-ff", "ark-serialize", - "array-bytes", - "base64 0.21.2", + "base64 0.21.5", "bincode", - "bitflags", + "bitflags 2.4.1", "blake3", "borsh 0.10.3", + "borsh 0.9.3", "bs58", "bv", "bytemuck", @@ -5516,14 +5674,14 @@ dependencies = [ "lazy_static", "libc", "libsecp256k1", + "light-poseidon", "log", "memoffset 0.9.0", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-derive 0.3.3", "num-traits", "parking_lot 0.12.0", - "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", @@ -5543,11 +5701,11 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03cb661d9fe32ec33ba8df554827becd5bcc1758392194420899771344177fb1" +checksum = "99127d74ad7a383b7f3dae2ecaebfc7926250fb7cef19b67fb344811d020dc14" dependencies = [ - "base64 0.21.2", + "base64 0.21.5", "bincode", "eager", "enum-iterator", @@ -5557,7 +5715,7 @@ dependencies = [ "num-derive 0.3.3", "num-traits", "percentage", - "rand 0.7.3", + "rand 0.8.5", "rustc_version", "serde", "solana-frozen-abi", @@ -5571,18 +5729,19 @@ dependencies = [ [[package]] name = "solana-program-test" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba60a7d9af681da6fc3330e24ad61e39a48f81d31595621ea95b9a12b0a044f" +checksum = "444889fcaf4588b2aa7733aa2f726b80c126026d60d79f882064b06f70fea05b" dependencies = [ "assert_matches", "async-trait", - "base64 0.21.2", + "base64 0.21.5", "bincode", "chrono-humanize", "crossbeam-channel", "log", "serde", + "solana-accounts-db", "solana-banks-client", "solana-banks-interface", "solana-banks-server", @@ -5592,15 +5751,16 @@ dependencies = [ "solana-runtime", "solana-sdk", "solana-vote-program", + "test-case", "thiserror", "tokio", ] [[package]] name = "solana-pubsub-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a503785ba377f33dd5b1fac75cb60c086ba625c5305f0bf3e687240cdcc7381f" +checksum = "f271c0f60d010af98f03a595552891ca3f497e7074e2225131c5bb24fadca9b8" dependencies = [ "crossbeam-channel", "futures-util", @@ -5618,26 +5778,25 @@ dependencies = [ "tokio-stream", "tokio-tungstenite", "tungstenite", - "url 2.4.0", + "url 2.4.1", ] [[package]] name = "solana-quic-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ede431a588575037db080835f09e9436bb9d5f3b3e19a5592eca3af13da369" +checksum = "09e9e5f5bea29716b4c14d319058b4e01c5837d68bd29acbdfaccbeebddac68d" dependencies = [ "async-mutex", "async-trait", - "futures 0.3.28", + "futures 0.3.29", "itertools", "lazy_static", "log", "quinn", "quinn-proto", - "quinn-udp", "rcgen", - "rustls 0.20.8", + "rustls", "solana-connection-cache", "solana-measure", "solana-metrics", @@ -5651,9 +5810,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4131723cc61f9981c3fa792567c658cf8fa64366331647835c3b88ba0685bad9" +checksum = "e864b2647831d5cad75bafa9234590821a97eba780c131fc12dc3bc45c1d9916" dependencies = [ "lazy_static", "num_cpus", @@ -5661,9 +5820,9 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c74894d82e6adcd95398dd4d190e6f303e424d47841b20f822a5ec27d5b77473" +checksum = "8d2ac9610026a977828af8b73984fdb2bac21f48bcf6b1f37767df31ca1cb0db" dependencies = [ "console", "dialoguer", @@ -5681,11 +5840,11 @@ dependencies = [ [[package]] name = "solana-rpc" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2708fbefa7c4e5c18168c90ef184b7223c12a33b5ee7c29f902e539f71d6ef" +checksum = "8185f58b553e2eff2ba6edc71cfbc5e24c7a7a989cd3da780bcbb37a3e212eb9" dependencies = [ - "base64 0.21.2", + "base64 0.21.5", "bincode", "bs58", "crossbeam-channel", @@ -5705,6 +5864,7 @@ dependencies = [ "serde_json", "soketto", "solana-account-decoder", + "solana-accounts-db", "solana-client", "solana-entry", "solana-faucet", @@ -5725,9 +5885,10 @@ dependencies = [ "solana-tpu-client", "solana-transaction-status", "solana-version", + "solana-vote", "solana-vote-program", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "stream-cancel", "thiserror", "tokio", @@ -5736,12 +5897,12 @@ dependencies = [ [[package]] name = "solana-rpc-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e9519b92e003d763ab2123ac099c32a56f66951cd810d4fa3615a800300860" +checksum = "54b9bdf8cf0b55be95bd4198d2706a6536ff30957b8c939a2fdee0627343793b" dependencies = [ "async-trait", - "base64 0.21.2", + "base64 0.21.5", "bincode", "bs58", "indicatif", @@ -5762,11 +5923,11 @@ dependencies = [ [[package]] name = "solana-rpc-client-api" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5556080cb9a1c70b782de622ee5840d6e1a256c65ab35a3b8542656ffe69a0" +checksum = "d219157e9a0cf86a96b1d5601751df75340e2add88eca5ab1b0941a42307f2f1" dependencies = [ - "base64 0.21.2", + "base64 0.21.5", "bs58", "jsonrpc-core", "reqwest", @@ -5778,15 +5939,15 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-version", - "spl-token-2022 0.6.1", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] [[package]] name = "solana-rpc-client-nonce-utils" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe69e5bab1a8afc475c1aab69ce53360e76fafa0f5efcc5ce151298be763653" +checksum = "01995b2fea7b6d2623fd3ad00a4af6426f7eb225f8d529acf2397ea8cb6d3f10" dependencies = [ "clap 2.34.0", "solana-clap-utils", @@ -5797,11 +5958,12 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef476c80639abf19cc061db686976195eb3fe87536239fb44d5754fb576e870d" +checksum = "ebf7b2d9c9ae6301651ad601219e9c9ca879d9f11f86673c2eea6197aca4aafa" dependencies = [ "arrayref", + "base64 0.21.5", "bincode", "blake3", "bv", @@ -5813,6 +5975,7 @@ dependencies = [ "dir-diff", "flate2", "fnv", + "fs-err", "im", "index_list", "itertools", @@ -5826,20 +5989,24 @@ dependencies = [ "num-traits", "num_cpus", "num_enum 0.6.1", - "once_cell", "ouroboros", "percentage", - "rand 0.7.3", + "qualifier_attr", + "rand 0.8.5", "rayon", "regex", "rustc_version", "serde", "serde_derive", + "serde_json", + "siphasher", + "solana-accounts-db", "solana-address-lookup-table-program", "solana-bpf-loader-program", "solana-bucket-map", "solana-compute-budget-program", "solana-config-program", + "solana-cost-model", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-loader-v4-program", @@ -5851,6 +6018,8 @@ dependencies = [ "solana-sdk", "solana-stake-program", "solana-system-program", + "solana-version", + "solana-vote", "solana-vote-program", "solana-zk-token-proof-program", "solana-zk-token-sdk", @@ -5866,14 +6035,14 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa2d54c0e9109d1bf9edafbd86d645217776df783814397f79c3929cf4316ba" +checksum = "dd1d4bf7b8f6a0350b8a1a81346a5819d9bd5c93096c2bf079f01b905dfe0443" dependencies = [ "assert_matches", - "base64 0.21.2", + "base64 0.21.5", "bincode", - "bitflags", + "bitflags 2.4.1", "borsh 0.10.3", "bs58", "bytemuck", @@ -5896,15 +6065,16 @@ dependencies = [ "num_enum 0.6.1", "pbkdf2 0.11.0", "qstring", + "qualifier_attr", "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", "serde_json", - "serde_with", + "serde_with 2.3.3", "sha2 0.10.7", "sha3 0.10.8", "solana-frozen-abi", @@ -5919,22 +6089,22 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa991e6d6ae7c57ef9fc56f964b22f3bae6ba6c144ccb07b0fe07e06a3efc47c" +checksum = "170f3c87d862ba5c74c03fa37a2867c10aa68e7b210bdf004c7a8b330284c789" dependencies = [ "bs58", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "rustversion", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] name = "solana-send-transaction-service" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93a05ff5747c5d96ff71d657cd32254b4ce22f638dd5b0cc32e33053289038d" +checksum = "4d4d5c756cd87641dd720dc7874182149d106f6753b0ba758023f1f1523fb8d9" dependencies = [ "crossbeam-channel", "log", @@ -5948,9 +6118,9 @@ dependencies = [ [[package]] name = "solana-stake-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdbf2edb60790e16d07022a00c1ec42db867d07c4bbb7ba4a9bc46b8755d4ba" +checksum = "8ea1b3f9e4a288970a308f47f25308ebba071100eae72f374fdcd777647315c9" dependencies = [ "bincode", "log", @@ -5963,9 +6133,9 @@ dependencies = [ [[package]] name = "solana-storage-bigtable" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd7adcef6cab02081d22b89c8f4f3a9a571c6333ee72130d22bde288a99c96c" +checksum = "eceb1c529665e3dfa3ce5aeabc36a58f51eb042df9ecdc35a12fb67f492205a4" dependencies = [ "backoff", "bincode", @@ -5973,15 +6143,15 @@ dependencies = [ "bzip2", "enum-iterator", "flate2", - "futures 0.3.28", + "futures 0.3.29", "goauth", "http", "hyper", "hyper-proxy", "log", "openssl", - "prost 0.11.9", - "prost-types 0.11.9", + "prost", + "prost-types", "serde", "serde_derive", "smpl_jwt", @@ -5991,39 +6161,39 @@ dependencies = [ "solana-transaction-status", "thiserror", "tokio", - "tonic 0.8.3", + "tonic", "zstd", ] [[package]] name = "solana-storage-proto" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e196a087a16b78c0d61f51c8792ae1aafe237929051eb6ee29a21c05c3ea2f" +checksum = "1d99c8dbcec5045ebd53707b8f90f4126cf2cd6f2d1c393be24a0bc0572f02f6" dependencies = [ "bincode", "bs58", - "prost 0.11.9", + "prost", "protobuf-src", "serde", "solana-account-decoder", "solana-sdk", "solana-transaction-status", - "tonic-build 0.8.4", + "tonic-build", ] [[package]] name = "solana-streamer" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2500570c1fe541d3e36c3ce318f25dab24dfd6aeab216afafa5c7c161d4979c8" +checksum = "ca08a69ae2ac103925bf367020e646dcdbb778ead2ce8685d962ca15616f0fc5" dependencies = [ "async-channel", "bytes", "crossbeam-channel", "futures-util", "histogram", - "indexmap", + "indexmap 2.0.2", "itertools", "libc", "log", @@ -6033,10 +6203,9 @@ dependencies = [ "pkcs8", "quinn", "quinn-proto", - "quinn-udp", - "rand 0.7.3", + "rand 0.8.5", "rcgen", - "rustls 0.20.8", + "rustls", "solana-metrics", "solana-perf", "solana-sdk", @@ -6047,9 +6216,9 @@ dependencies = [ [[package]] name = "solana-system-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd3687e150ea6ecacc9d1620890312f7b9e6bc042071ccdfc22580039c2b27ea" +checksum = "dcda9bf5b7e7b8726c6ddc7f06fc9110875e688e197e0602721b1b425cfc5cc2" dependencies = [ "bincode", "log", @@ -6061,16 +6230,17 @@ dependencies = [ [[package]] name = "solana-test-validator" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0a63937924d161dbf66f2bdfd9ad37808ed705e58f056244b111a19cef5bac" +checksum = "2c18b08b7611e029d31ec91cd0177501330a91ab9e58d41ecce099ac6629638f" dependencies = [ - "base64 0.21.2", + "base64 0.21.5", "bincode", "crossbeam-channel", "log", "serde_derive", "serde_json", + "solana-accounts-db", "solana-cli-output", "solana-client", "solana-core", @@ -6092,9 +6262,9 @@ dependencies = [ [[package]] name = "solana-thin-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2fd4ae4d27790ae211f16a6e85a02585b7cae7057f8d555bad3762b4901c" +checksum = "1d2c780ce4d234abbde57ed94342e882c0170c6ebe7d8f1f02763f6cb88a925d" dependencies = [ "bincode", "log", @@ -6107,17 +6277,16 @@ dependencies = [ [[package]] name = "solana-tpu-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56bfe97fb1ab0eb44abb623272648bea404d309e584ba6f8e981e6626cfb0cb" +checksum = "e00eb9926169f8d4f9f6af95410decc01545a9ce991e12781cb8ae1515ab833f" dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap", + "indexmap 2.0.2", "indicatif", "log", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -6132,14 +6301,14 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5331f3dfa4a8228dd69b6954859d6aafdd3234b24802690c1f7446bc37df6d" +checksum = "0f8af464b440c000a3e5306509f02240150b5311fffc4534738e1bdfdb604604" dependencies = [ "Inflector", - "base64 0.21.2", + "base64 0.21.5", "bincode", - "borsh 0.9.3", + "borsh 0.10.3", "bs58", "lazy_static", "log", @@ -6147,20 +6316,56 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", - "solana-address-lookup-table-program", "solana-sdk", - "spl-associated-token-account 1.1.3", - "spl-memo 3.0.1", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-associated-token-account 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-memo 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] +[[package]] +name = "solana-turbine" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519c0a695f79328a1654e8a8defdfeb0a9852c18bc186f640465d65c539fdaf1" +dependencies = [ + "bincode", + "bytes", + "crossbeam-channel", + "futures 0.3.29", + "itertools", + "log", + "lru", + "quinn", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rayon", + "rcgen", + "rustls", + "solana-entry", + "solana-gossip", + "solana-ledger", + "solana-measure", + "solana-metrics", + "solana-perf", + "solana-poh", + "solana-quic-client", + "solana-rayon-threadlimit", + "solana-rpc", + "solana-rpc-client-api", + "solana-runtime", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + [[package]] name = "solana-udp-client" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "371e23c982638eeabab1a62bf637c70648b730fc807378042214382c3b550000" +checksum = "f2763ee4af6d7a92cd5b70679a8b46ea13f167e59a8f5f294041905b5258d9ae" dependencies = [ "async-trait", "solana-connection-cache", @@ -6173,9 +6378,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbad2a5e055b53a7d41e52f98963bcef1ee6ce782f90f1d4ea9237cae1ee87ee" +checksum = "9d133bdd437accc92171e40d76cd5cd76cba59d58f6a2f89b02c8621ffab3b5d" dependencies = [ "log", "rustc_version", @@ -6187,11 +6392,30 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-vote" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840a57075b446db53d5591e107b32b46afeff7c5e1172c2ff90ed30067b8650a" +dependencies = [ + "crossbeam-channel", + "itertools", + "log", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", + "solana-vote-program", + "thiserror", +] + [[package]] name = "solana-vote-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27089add49014dc9a917ddb95d5af0ab31bd15308adb4bdce78fd72b52d787b" +checksum = "733573c6e800f04d8721bd4b861165c17db94026e2c9da28d6a3553e095efacb" dependencies = [ "bincode", "log", @@ -6211,12 +6435,11 @@ dependencies = [ [[package]] name = "solana-zk-token-proof-program" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569689360796644baad94ffa37e12f5e757e2600115b8f27b72a687ec047a435" +checksum = "4d2aa189efb2af7b1cf2339808b9bc236addeb776e62e5b5527947d2a68c699d" dependencies = [ "bytemuck", - "getrandom 0.1.16", "num-derive 0.3.3", "num-traits", "solana-program-runtime", @@ -6226,13 +6449,12 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.16.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aa69c3f19df466a68a26f009e9dc39de671bf022c66fbbfab2d1c021b62a97" +checksum = "34f2a9ce880cac8d1f7f0cbfaaac07fb84e163e7f616125f4980c13a21b17727" dependencies = [ "aes-gcm-siv", - "arrayref", - "base64 0.21.2", + "base64 0.21.5", "bincode", "bytemuck", "byteorder", @@ -6256,9 +6478,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ae90c406f0a2d4a15f08d16c8b64f37997a57611fec0a89f1277854166996e8" +checksum = "103318aa365ff7caa8cf534f2246b5eb7e5b34668736d52b1266b143f7a21196" dependencies = [ "byteorder", "combine", @@ -6297,31 +6519,31 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" +version = "2.2.0" dependencies = [ "assert_matches", - "borsh 0.9.3", - "num-derive 0.3.3", + "borsh 0.10.3", + "num-derive 0.4.1", "num-traits", "solana-program", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token 4.0.0", + "spl-token-2022 0.9.0", "thiserror", ] [[package]] name = "spl-associated-token-account" -version = "2.0.0" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3" dependencies = [ "assert_matches", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "solana-program", - "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-2022 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] @@ -6332,9 +6554,9 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", ] [[package]] @@ -6342,7 +6564,7 @@ name = "spl-binary-oracle-pair" version = "0.1.0" dependencies = [ "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "solana-program", "solana-program-test", @@ -6354,7 +6576,7 @@ dependencies = [ [[package]] name = "spl-concurrent-merkle-tree" -version = "0.1.3" +version = "0.2.0" dependencies = [ "bytemuck", "rand 0.8.5", @@ -6372,26 +6594,61 @@ dependencies = [ "borsh 0.10.3", "bytemuck", "solana-program", - "spl-discriminator-derive", + "spl-discriminator-derive 0.1.1", ] [[package]] -name = "spl-discriminator-derive" +name = "spl-discriminator" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" dependencies = [ - "quote 1.0.26", - "spl-discriminator-syn", - "syn 2.0.15", + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.1.1" +dependencies = [ + "quote", + "spl-discriminator-syn 0.1.1", + "syn 2.0.28", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" +dependencies = [ + "quote", + "spl-discriminator-syn 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.28", ] [[package]] name = "spl-discriminator-syn" -version = "0.1.0" +version = "0.1.1" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "solana-program", - "syn 2.0.15", + "proc-macro2", + "quote", + "sha2 0.10.7", + "syn 2.0.28", + "thiserror", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.7", + "syn 2.0.28", "thiserror", ] @@ -6450,6 +6707,17 @@ dependencies = [ "spl-token 4.0.0", ] +[[package]] +name = "spl-feature-gate" +version = "0.1.0" +dependencies = [ + "num_enum 0.7.1", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-program-error 0.3.0", +] + [[package]] name = "spl-feature-proposal" version = "1.0.0" @@ -6481,10 +6749,10 @@ version = "3.1.1" dependencies = [ "arrayref", "assert_matches", - "base64 0.21.2", + "base64 0.21.5", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "serde", @@ -6517,7 +6785,7 @@ dependencies = [ "assert_matches", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "serde", @@ -6534,13 +6802,13 @@ dependencies = [ [[package]] name = "spl-governance-chat" -version = "0.2.7" +version = "0.2.8" dependencies = [ "arrayref", "assert_matches", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "serde", @@ -6565,7 +6833,7 @@ dependencies = [ "bincode", "borsh 0.10.3", "lazy_static", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serde", "serde_derive", @@ -6583,7 +6851,7 @@ dependencies = [ "arrayref", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serde", "serde_derive", @@ -6596,7 +6864,7 @@ dependencies = [ name = "spl-instruction-padding" version = "0.1.0" dependencies = [ - "num_enum 0.6.1", + "num_enum 0.7.1", "solana-program", "solana-program-test", "solana-sdk", @@ -6611,7 +6879,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", "thiserror", ] @@ -6622,7 +6890,7 @@ version = "0.2.0" dependencies = [ "borsh 0.10.3", "libm", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "solana-program", @@ -6634,20 +6902,20 @@ dependencies = [ [[package]] name = "spl-memo" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +version = "4.0.0" dependencies = [ "solana-program", + "solana-program-test", + "solana-sdk", ] [[package]] name = "spl-memo" version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" dependencies = [ "solana-program", - "solana-program-test", - "solana-sdk", ] [[package]] @@ -6660,10 +6928,10 @@ dependencies = [ [[package]] name = "spl-name-service" -version = "0.2.0" +version = "0.3.0" dependencies = [ "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "solana-program", "solana-program-test", @@ -6671,27 +6939,80 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-pod" +version = "0.1.0" +dependencies = [ + "base64 0.21.5", + "borsh 0.10.3", + "bytemuck", + "serde", + "serde_json", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-pod" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" +dependencies = [ + "borsh 0.10.3", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "spl-program-error" -version = "0.2.0" +version = "0.3.0" dependencies = [ "lazy_static", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "serial_test", "solana-program", "solana-sdk", - "spl-program-error-derive", + "spl-program-error-derive 0.3.1", + "thiserror", +] + +[[package]] +name = "spl-program-error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" +dependencies = [ + "num-derive 0.4.1", + "num-traits", + "solana-program", + "spl-program-error-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] [[package]] name = "spl-program-error-derive" -version = "0.2.0" +version = "0.3.1" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "sha2 0.10.7", + "syn 2.0.28", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.7", + "syn 2.0.28", ] [[package]] @@ -6699,7 +7020,7 @@ name = "spl-record" version = "0.1.0" dependencies = [ "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "solana-program", "solana-program-test", @@ -6718,37 +7039,72 @@ dependencies = [ ] [[package]] -name = "spl-single-validator-pool" +name = "spl-single-pool" version = "1.0.0" dependencies = [ "approx", "arrayref", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.1", "rand 0.8.5", "solana-program", "solana-program-test", "solana-sdk", "solana-vote-program", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", "test-case", "thiserror", ] +[[package]] +name = "spl-single-pool-cli" +version = "1.0.0" +dependencies = [ + "bincode", + "borsh 0.10.3", + "clap 3.2.25", + "console", + "serde", + "serde_derive", + "serde_json", + "serde_with 3.4.0", + "serial_test", + "solana-account-decoder", + "solana-clap-v3-utils", + "solana-cli-config", + "solana-cli-output", + "solana-client", + "solana-logger", + "solana-remote-wallet", + "solana-sdk", + "solana-test-validator", + "solana-transaction-status", + "solana-vote-program", + "spl-associated-token-account 2.2.0", + "spl-single-pool", + "spl-token 4.0.0", + "spl-token-client", + "tempfile", + "test-case", + "tokio", +] + [[package]] name = "spl-stake-pool" version = "0.7.0" dependencies = [ "arrayref", + "assert_matches", "bincode", "borsh 0.10.3", - "num-derive 0.4.0", + "bytemuck", + "num-derive 0.4.1", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.1", "proptest", "serde", "serde_derive", @@ -6757,8 +7113,9 @@ dependencies = [ "solana-sdk", "solana-vote-program", "spl-math", + "spl-pod 0.1.0", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "test-case", "thiserror", ] @@ -6783,37 +7140,40 @@ dependencies = [ "solana-program", "solana-remote-wallet", "solana-sdk", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-stake-pool", "spl-token 4.0.0", ] [[package]] name = "spl-tlv-account-resolution" -version = "0.2.0" +version = "0.4.0" dependencies = [ "bytemuck", + "futures 0.3.29", + "futures-util", + "solana-client", "solana-program", "solana-program-test", "solana-sdk", - "spl-discriminator", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] -name = "spl-token" -version = "3.5.0" +name = "spl-tlv-account-resolution" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" +checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" dependencies = [ - "arrayref", "bytemuck", - "num-derive 0.3.3", - "num-traits", - "num_enum 0.5.11", "solana-program", - "thiserror", + "spl-discriminator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-program-error 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-type-length-value 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6823,9 +7183,9 @@ dependencies = [ "arrayref", "bytemuck", "lazy_static", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.1", "proptest", "serial_test", "solana-program", @@ -6835,45 +7195,68 @@ dependencies = [ ] [[package]] -name = "spl-token-2022" -version = "0.6.1" +name = "spl-token" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" dependencies = [ "arrayref", "bytemuck", "num-derive 0.3.3", "num-traits", - "num_enum 0.5.11", + "num_enum 0.6.1", "solana-program", - "solana-zk-token-sdk", - "spl-memo 3.0.1", - "spl-token 3.5.0", "thiserror", ] [[package]] name = "spl-token-2022" -version = "0.7.0" +version = "0.9.0" dependencies = [ "arrayref", + "base64 0.21.5", "bytemuck", "lazy_static", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.1", "proptest", "serde", "serde_json", - "serde_with", + "serde_with 3.4.0", "serial_test", "solana-program", "solana-program-test", "solana-sdk", "solana-zk-token-sdk", "spl-memo 4.0.0", + "spl-pod 0.1.0", "spl-token 4.0.0", - "spl-transfer-hook-interface", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.3.0", + "spl-type-length-value 0.3.0", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.1", + "num-traits", + "num_enum 0.7.1", + "solana-program", + "solana-zk-token-sdk", + "spl-memo 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-metadata-interface 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-transfer-hook-interface 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-type-length-value 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror", ] @@ -6882,28 +7265,33 @@ name = "spl-token-2022-test" version = "0.0.1" dependencies = [ "async-trait", + "borsh 0.10.3", "futures-util", "solana-program", "solana-program-test", "solana-sdk", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-instruction-padding", "spl-memo 4.0.0", - "spl-token-2022 0.7.0", + "spl-pod 0.1.0", + "spl-token-2022 0.9.0", "spl-token-client", + "spl-token-metadata-interface 0.2.0", "spl-transfer-hook-example", - "spl-transfer-hook-interface", + "spl-transfer-hook-interface 0.3.0", "test-case", "walkdir", ] [[package]] name = "spl-token-cli" -version = "3.0.0" +version = "3.3.0" dependencies = [ "assert_cmd", + "base64 0.21.5", "clap 2.34.0", "console", + "futures 0.3.29", "serde", "serde_derive", "serde_json", @@ -6918,34 +7306,86 @@ dependencies = [ "solana-sdk", "solana-test-validator", "solana-transaction-status", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-memo 4.0.0", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "spl-token-client", + "spl-token-metadata-interface 0.2.0", "strum 0.25.0", - "strum_macros 0.25.0", + "strum_macros 0.25.3", "tempfile", "tokio", "walkdir", ] [[package]] -name = "spl-token-client" -version = "0.5.0" +name = "spl-token-client" +version = "0.8.0" +dependencies = [ + "async-trait", + "curve25519-dalek", + "futures 0.3.29", + "futures-util", + "solana-banks-interface", + "solana-cli-output", + "solana-program-test", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", + "spl-associated-token-account 2.2.0", + "spl-memo 4.0.0", + "spl-token 4.0.0", + "spl-token-2022 0.9.0", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.3.0", + "thiserror", +] + +[[package]] +name = "spl-token-collection" +version = "0.1.0" +dependencies = [ + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-token-2022 0.9.0", + "spl-token-client", + "spl-token-group-example", + "spl-token-group-interface", + "spl-token-metadata-interface 0.2.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-group-example" +version = "0.1.0" +dependencies = [ + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-token-2022 0.9.0", + "spl-token-client", + "spl-token-group-interface", + "spl-token-metadata-interface 0.2.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.1.0" dependencies = [ - "async-trait", - "futures-util", - "solana-cli-output", - "solana-client", - "solana-program-test", - "solana-sdk", - "spl-associated-token-account 2.0.0", - "spl-memo 4.0.0", - "spl-token 4.0.0", - "spl-token-2022 0.7.0", - "spl-transfer-hook-interface", - "thiserror", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] @@ -6955,7 +7395,7 @@ dependencies = [ "arrayref", "assert_matches", "bytemuck", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "solana-program", @@ -6983,27 +7423,45 @@ dependencies = [ [[package]] name = "spl-token-metadata-example" -version = "0.1.0" +version = "0.2.0" dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-token-2022 0.7.0", + "spl-pod 0.1.0", + "spl-token-2022 0.9.0", "spl-token-client", - "spl-token-metadata-interface", - "spl-type-length-value", + "spl-token-metadata-interface 0.2.0", + "spl-type-length-value 0.3.0", "test-case", ] [[package]] name = "spl-token-metadata-interface" -version = "0.1.0" +version = "0.2.0" +dependencies = [ + "borsh 0.10.3", + "serde", + "serde_json", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" dependencies = [ "borsh 0.10.3", "solana-program", - "spl-discriminator", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-program-error 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-type-length-value 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -7013,7 +7471,7 @@ dependencies = [ "arbitrary", "arrayref", "enum_dispatch", - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", "proptest", "roots", @@ -7021,7 +7479,7 @@ dependencies = [ "solana-sdk", "spl-math", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "test-case", "thiserror", ] @@ -7042,14 +7500,14 @@ dependencies = [ name = "spl-token-upgrade" version = "0.1.1" dependencies = [ - "num-derive 0.4.0", + "num-derive 0.4.1", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.1", "solana-program", "solana-program-test", "solana-sdk", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "spl-token-client", "test-case", "thiserror", @@ -7068,9 +7526,9 @@ dependencies = [ "solana-remote-wallet", "solana-sdk", "solana-test-validator", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "spl-token-client", "spl-token-upgrade", "tokio", @@ -7082,53 +7540,122 @@ name = "spl-token-wrap" version = "0.1.0" dependencies = [ "bytemuck", - "num_enum 0.6.1", + "num_enum 0.7.1", "solana-program", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", - "spl-token-2022 0.7.0", + "spl-token-2022 0.9.0", "thiserror", ] [[package]] -name = "spl-transfer-hook-example" +name = "spl-transfer-hook-cli" version = "0.1.0" +dependencies = [ + "clap 3.2.25", + "futures-util", + "solana-clap-v3-utils", + "solana-cli-config", + "solana-client", + "solana-logger", + "solana-remote-wallet", + "solana-sdk", + "solana-test-validator", + "spl-tlv-account-resolution 0.4.0", + "spl-token-2022 0.9.0", + "spl-token-client", + "spl-transfer-hook-interface 0.3.0", + "strum 0.25.0", + "strum_macros 0.25.3", + "tokio", +] + +[[package]] +name = "spl-transfer-hook-example" +version = "0.3.0" dependencies = [ "arrayref", "solana-program", "solana-program-test", "solana-sdk", - "spl-tlv-account-resolution", - "spl-token-2022 0.7.0", - "spl-transfer-hook-interface", - "spl-type-length-value", + "spl-tlv-account-resolution 0.4.0", + "spl-token-2022 0.9.0", + "spl-transfer-hook-interface 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] name = "spl-transfer-hook-interface" -version = "0.1.0" +version = "0.3.0" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.0", - "num-traits", - "num_enum 0.6.1", "solana-program", - "spl-discriminator", - "spl-tlv-account-resolution", - "spl-type-length-value", - "thiserror", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.4.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051d31803f873cabe71aec3c1b849f35248beae5d19a347d93a5c9cccc5d5a9b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-program-error 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-tlv-account-resolution 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-type-length-value 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "spl-type-length-value" -version = "0.2.0" +version = "0.3.0" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value-derive", +] + +[[package]] +name = "spl-type-length-value" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" dependencies = [ - "borsh 0.10.3", "bytemuck", "solana-program", - "spl-discriminator", - "spl-program-error", + "spl-discriminator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-program-error 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "spl-type-length-value-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "spl-type-length-value-derive-test" +version = "0.1.0" +dependencies = [ + "borsh 0.10.3", + "solana-program", + "spl-discriminator 0.1.0", + "spl-type-length-value 0.3.0", ] [[package]] @@ -7139,7 +7666,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-associated-token-account 2.0.0", + "spl-associated-token-account 2.2.0", "spl-token 4.0.0", "thiserror", ] @@ -7195,23 +7722,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "rustversion", "syn 1.0.107", ] [[package]] name = "strum_macros" -version = "0.25.0" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9f3bd7d2e45dcc5e265fbb88d6513e4747d8ef9444cf01a533119bce28a157" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "rustversion", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] @@ -7226,36 +7753,25 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.15" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "unicode-ident", ] @@ -7271,10 +7787,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", - "unicode-xid 0.2.2", + "unicode-xid", ] [[package]] @@ -7293,18 +7809,39 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "thiserror", "walkdir", ] +[[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", + "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 = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -7319,7 +7856,7 @@ checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" dependencies = [ "anyhow", "fnv", - "futures 0.3.28", + "futures 0.3.29", "humantime", "opentelemetry", "pin-project", @@ -7341,21 +7878,20 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] [[package]] name = "tempfile" -version = "3.6.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ - "autocfg", "cfg-if 1.0.0", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys 0.48.0", ] @@ -7377,36 +7913,36 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-case" -version = "3.1.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1d6e7bde536b0412f20765b76e921028059adfd1b90d8974d33fd3c91b25df" +checksum = "c8f1e820b7f1d95a0cdbf97a5df9de10e1be731983ab943e56703ac1b8e9d425" dependencies = [ "test-case-macros", ] [[package]] name = "test-case-core" -version = "3.1.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10394d5d1e27794f772b6fc854c7e91a2dc26e2cbf807ad523370c2a59c0cee" +checksum = "54c25e2cb8f5fcd7318157634e8838aa6f7e4715c96637f969fabaccd1ef5462" dependencies = [ "cfg-if 1.0.0", "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] name = "test-case-macros" -version = "3.1.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb9a44b1c6a54c1ba58b152797739dba2a83ca74e18168a68c980eb142f9404" +checksum = "37cfd7bbc88a0104e304229fba519bdc45501a30b760fb72240342f1289ad257" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", "test-case-core", ] @@ -7437,22 +7973,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -7464,17 +8000,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - [[package]] name = "time" version = "0.3.22" @@ -7538,22 +8063,21 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.14.1" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d0183f6f6001549ab68f8c7585093bb732beefbcf6d23a10b9b95c73a1dd49" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.0", "pin-project-lite", "signal-hook-registry", + "socket2 0.5.4", "tokio-macros", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -7568,13 +8092,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 1.0.107", + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -7589,24 +8113,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.20.8", + "rustls", "tokio", - "webpki 0.22.0", ] [[package]] @@ -7627,9 +8139,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -7638,18 +8150,17 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.20.8", + "rustls", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls", "tungstenite", - "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.25.2", ] [[package]] @@ -7693,46 +8204,14 @@ dependencies = [ [[package]] name = "tonic" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" -dependencies = [ - "async-stream", - "async-trait", - "base64 0.13.0", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding 2.3.0", - "pin-project", - "prost 0.9.0", - "prost-derive 0.9.0", - "tokio", - "tokio-rustls 0.22.0", - "tokio-stream", - "tokio-util 0.6.9", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic" -version = "0.8.3" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.13.0", + "base64 0.21.5", "bytes", "futures-core", "futures-util", @@ -7743,42 +8222,27 @@ dependencies = [ "hyper-timeout", "percent-encoding 2.3.0", "pin-project", - "prost 0.11.9", - "prost-derive 0.11.9", + "prost", "rustls-pemfile 1.0.1", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls", "tokio-stream", - "tokio-util 0.7.1", "tower", "tower-layer", "tower-service", "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" -dependencies = [ - "proc-macro2 1.0.60", - "prost-build 0.9.0", - "quote 1.0.26", - "syn 1.0.107", ] [[package]] name = "tonic-build" -version = "0.8.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" dependencies = [ "prettyplease 0.1.9", - "proc-macro2 1.0.60", - "prost-build 0.11.9", - "quote 1.0.26", + "proc-macro2", + "prost-build", + "quote", "syn 1.0.107", ] @@ -7790,7 +8254,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand 0.8.5", @@ -7802,25 +8266,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -7829,9 +8274,9 @@ checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" @@ -7852,8 +8297,8 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", ] @@ -7866,16 +8311,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-opentelemetry" version = "0.17.4" @@ -7914,24 +8349,23 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "base64 0.13.0", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", "rand 0.8.5", - "rustls 0.20.8", - "sha-1 0.10.0", + "rustls", + "sha1", "thiserror", - "url 2.4.0", + "url 2.4.1", "utf-8", - "webpki 0.22.0", - "webpki-roots", + "webpki-roots 0.24.0", ] [[package]] @@ -8006,12 +8440,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.2" @@ -8072,9 +8500,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna 0.4.0", @@ -8122,9 +8550,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -8146,12 +8574,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -8177,9 +8599,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -8201,7 +8623,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.26", + "quote", "wasm-bindgen-macro-support", ] @@ -8211,9 +8633,9 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2", + "quote", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8235,33 +8657,19 @@ dependencies = [ ] [[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki" -version = "0.22.0" +name = "webpki-roots" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ - "ring", - "untrusted", + "rustls-webpki", ] [[package]] name = "webpki-roots" -version = "0.22.1" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c475786c6f47219345717a043a37ec04cb4bc185e28853adcc4fa0a947eba630" -dependencies = [ - "webpki 0.22.0", -] +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "which" @@ -8330,21 +8738,6 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -8509,11 +8902,12 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi 0.3.9", + "cfg-if 1.0.0", + "windows-sys 0.48.0", ] [[package]] @@ -8531,14 +8925,14 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.22", + "time", ] [[package]] name = "xattr" -version = "0.2.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] @@ -8549,7 +8943,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "time 0.3.22", + "time", ] [[package]] @@ -8567,8 +8961,8 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" dependencies = [ - "proc-macro2 1.0.60", - "quote 1.0.26", + "proc-macro2", + "quote", "syn 1.0.107", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 8ced5413751..6f042b7c825 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "examples/rust/sysvar", "examples/rust/transfer-lamports", "examples/rust/transfer-tokens", + "feature-gate/program", "feature-proposal/program", "feature-proposal/cli", "governance/addin-mock/program", @@ -26,18 +27,24 @@ members = [ "libraries/concurrent-merkle-tree", "libraries/math", "libraries/merkle-tree-reference", + "libraries/pod", "libraries/program-error", "libraries/tlv-account-resolution", "libraries/type-length-value", + "libraries/type-length-value-derive-test", "memo/program", "name-service/program", "managed-token/program", "record/program", "shared-memory/program", + "single-pool/cli", + "single-pool/program", "stake-pool/cli", - "stake-pool/single-pool", "stake-pool/program", "stateless-asks/program", + "token-collection/program", + "token-group/example", + "token-group/interface", "token-lending/cli", "token-lending/program", "token-metadata/example", @@ -51,8 +58,9 @@ members = [ "token/program", "token/program-2022", "token/program-2022-test", - "token/transfer-hook-example", - "token/transfer-hook-interface", + "token/transfer-hook/cli", + "token/transfer-hook/example", + "token/transfer-hook/interface", "token/client", "utils/cgen", "utils/test-client", @@ -62,7 +70,4 @@ members = [ exclude = [ ] -[patch.crates-io] -# Rust 1.69+ broke ntapi v0.3.x, which doesn't contain proper fix: -# https://github.com/MSxDOS/ntapi/pull/12 -ntapi = { git = "https://github.com/solana-labs/ntapi", rev = "97ede981a1777883ff86d142b75024b023f04fad" } +resolver = "2" diff --git a/README.md b/README.md index fbc175f3981..25948931f73 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,43 @@ portable across all implementations. For more information see the [SPL documentation](https://spl.solana.com) and the [Token TypeDocs](https://solana-labs.github.io/solana-program-library/token/js/). -## Audits +## Deployments Only a subset of programs within the Solana Program Library repo are deployed to the Solana Mainnet Beta. Currently, this includes: +| Program | Version | +| --- | --- | +| [token](https://github.com/solana-labs/solana-program-library/tree/master/token/program) | [3.4.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-v3.4.0) | +| [associated-token-account](https://github.com/solana-labs/solana-program-library/tree/master/associated-token-account/program) | [1.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/associated-token-account-v1.1.0) | +| [token-2022](https://github.com/solana-labs/solana-program-library/tree/master/token/program-2022) | [0.9.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-2022-v0.9.0) | +| [governance](https://github.com/solana-labs/solana-program-library/tree/master/governance/program) | [3.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/governance-v3.1.0) | +| [stake-pool](https://github.com/solana-labs/solana-program-library/tree/master/stake-pool/program) | [0.6.4](https://github.com/solana-labs/solana-program-library/releases/tag/stake-pool-v0.6.4) | +| [account-compression](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/programs/account-compression) | [0.1.3](https://github.com/solana-labs/solana-program-library/releases/tag/account-compression-v0.1.3) | +| [shared-memory](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory/program) | [1.0.0](https://github.com/solana-labs/solana-program-library/commit/b40e0dd3fd6c0e509dc1e8dd3da0a6d609035bbd) | +| [feature-proposal](https://github.com/solana-labs/solana-program-library/tree/master/feature-proposal/program) | [1.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/feature-proposal-v1.0.0) | +| [name-service](https://github.com/solana-labs/solana-program-library/tree/master/name-service/program) | [0.3.0](https://github.com/solana-labs/solana-program-library/releases/tag/name-service-v0.3.0) | +| [memo](https://github.com/solana-labs/solana-program-library/tree/master/memo/program) | [3.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/memo-v3.0.0) | + +In addition, one program is planned for deployment to Solana Mainnet Beta: + +| Program | Version | +| --- | --- | +| [single-pool](https://github.com/solana-labs/solana-program-library/tree/master/single-pool/program) | [1.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/single-pool-v1.0.0) | + +## Audits + +Only a subset of programs within the Solana Program Library repo are audited. Currently, this includes: + | Program | Last Audit Date | Version | | --- | --- | --- | | [token](https://github.com/solana-labs/solana-program-library/tree/master/token/program) | 2022-08-04 (Peer review) | [3.4.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-v3.4.0) | | [associated-token-account](https://github.com/solana-labs/solana-program-library/tree/master/associated-token-account/program) | 2022-08-04 (Peer review) | [1.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/associated-token-account-v1.1.0) | | [token-2022](https://github.com/solana-labs/solana-program-library/tree/master/token/program-2022) | [2023-02-10](https://github.com/solana-labs/security-audits/blob/master/spl/TrailOfBitsToken2022Audit-2023-02-10.pdf) | [0.5.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-2022-v0.5.0) | -| [governance](https://github.com/solana-labs/solana-program-library/tree/master/governance/program) | N/A | [3.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/governance-v3.1.0) | -| [stake-pool](https://github.com/solana-labs/solana-program-library/tree/master/stake-pool/program) | [2023-01-31](https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2023-01-31.pdf) | [1.0.0]() | +| [stake-pool](https://github.com/solana-labs/solana-program-library/tree/master/stake-pool/program) | [2023-01-31](https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2023-01-31.pdf) | 1.0.0 (currently untagged) | | [account-compression](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/programs/account-compression) | [2022-12-05](https://github.com/solana-labs/security-audits/blob/master/spl/OtterSecAccountCompressionAudit-2022-12-03.pdf) | [0.1.3](https://github.com/solana-labs/solana-program-library/releases/tag/account-compression-v0.1.3) | | [shared-memory](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory/program) | [2021-02-25](https://github.com/solana-labs/security-audits/blob/master/spl/KudelskiTokenSwapSharedMemAudit-2021-02-25.pdf) | [1.0.0](https://github.com/solana-labs/solana-program-library/commit/b40e0dd3fd6c0e509dc1e8dd3da0a6d609035bbd) | -| [feature-proposal](https://github.com/solana-labs/solana-program-library/tree/master/feature-proposal/program) | Not audited | [1.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/feature-proposal-v1.0.0) | -| [name-service](https://github.com/solana-labs/solana-program-library/tree/master/name-service/program) | Not audited | [0.3.0](https://github.com/solana-labs/solana-program-library/releases/tag/name-service-v0.3.0) | -| [memo](https://github.com/solana-labs/solana-program-library/tree/master/memo/program) | Not audited | [3.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/memo-v3.0.0) | +| [single-pool](https://github.com/solana-labs/solana-program-library/tree/master/single-pool/program) | [2023-08-08](https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeSinglePoolAudit-2023-08-08.pdf) | (untagged version prior to 1.0.0) | All other programs may be updated from time to time. These programs are not audited, so fork and deploy them at your own risk. Here is the full list of @@ -34,8 +54,11 @@ unaudited programs: * [binary-option](https://github.com/solana-labs/solana-program-library/tree/master/binary-option/program) * [binary-oracle-pair](https://github.com/solana-labs/solana-program-library/tree/master/binary-oracle-pair/program) +* [feature-proposal](https://github.com/solana-labs/solana-program-library/tree/master/feature-proposal/program) * [instruction-padding](https://github.com/solana-labs/solana-program-library/tree/master/instruction-padding/program) * [managed-token](https://github.com/solana-labs/solana-program-library/tree/master/managed-token/program) +* [memo](https://github.com/solana-labs/solana-program-library/tree/master/memo/program) +* [name-service](https://github.com/solana-labs/solana-program-library/tree/master/name-service/program) * [record](https://github.com/solana-labs/solana-program-library/tree/master/record/program) * [stateless-asks](https://github.com/solana-labs/solana-program-library/tree/master/stateless-asks/program) * [token-lending](https://github.com/solana-labs/solana-program-library/tree/master/token-lending/program) diff --git a/account-compression/Cargo.lock b/account-compression/Cargo.lock index c38d80d4c4f..ac6c09f8207 100644 --- a/account-compression/Cargo.lock +++ b/account-compression/Cargo.lock @@ -8,178 +8,168 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] -name = "aho-corasick" -version = "0.7.18" +name = "ahash" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "memchr", + "cfg-if", + "getrandom 0.2.10", + "once_cell", + "version_check", ] [[package]] name = "anchor-attribute-access-control" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf7d535e1381be3de2c0716c0a1c1e32ad9df1042cddcf7bc18d743569e53319" +checksum = "e5f619f1d04f53621925ba8a2e633ba5a6081f2ae14758cbb67f38fd823e0a3e" dependencies = [ "anchor-syn", - "anyhow", "proc-macro2", "quote", - "regex", - "syn", + "syn 1.0.108", ] [[package]] name = "anchor-attribute-account" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcd731f21048a032be27c7791701120e44f3f6371358fc4261a7f716283d29" +checksum = "e7f2a3e1df4685f18d12a943a9f2a7456305401af21a07c9fe076ef9ecd6e400" dependencies = [ "anchor-syn", - "anyhow", - "bs58 0.4.0", + "bs58 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn", + "syn 1.0.108", ] [[package]] name = "anchor-attribute-constant" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1be64a48e395fe00b8217287f226078be2cf32dae42fdf8a885b997945c3d28" +checksum = "9423945cb55627f0b30903288e78baf6f62c6c8ab28fb344b6b25f1ffee3dca7" dependencies = [ "anchor-syn", - "proc-macro2", - "syn", + "quote", + "syn 1.0.108", ] [[package]] name = "anchor-attribute-error" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ea6713d1938c0da03656ff8a693b17dc0396da66d1ba320557f07e86eca0d4" +checksum = "93ed12720033cc3c3bf3cfa293349c2275cd5ab99936e33dd4bf283aaad3e241" dependencies = [ "anchor-syn", - "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] name = "anchor-attribute-event" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401f11efb3644285685f8339829a9786d43ed7490bb1699f33c478d04d5a582" +checksum = "eef4dc0371eba2d8c8b54794b0b0eb786a234a559b77593d6f80825b6d2c77a2" dependencies = [ "anchor-syn", - "anyhow", "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] -name = "anchor-attribute-interface" -version = "0.26.0" +name = "anchor-attribute-program" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6700a6f5c888a9c33fe8afc0c64fd8575fa28d05446037306d0f96102ae4480" +checksum = "b18c4f191331e078d4a6a080954d1576241c29c56638783322a18d308ab27e4f" dependencies = [ "anchor-syn", - "anyhow", - "heck", - "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] -name = "anchor-attribute-program" -version = "0.26.0" +name = "anchor-derive-accounts" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad769993b5266714e8939e47fbdede90e5c030333c7522d99a4d4748cf26712" +checksum = "5de10d6e9620d3bcea56c56151cad83c5992f50d5960b3a9bebc4a50390ddc3c" dependencies = [ "anchor-syn", - "anyhow", - "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] -name = "anchor-attribute-state" -version = "0.26.0" +name = "anchor-derive-serde" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e677fae4a016a554acdd0e3b7f178d3acafaa7e7ffac6b8690cf4e171f1c116" +checksum = "f4e2e5be518ec6053d90a2a7f26843dbee607583c779e6c8395951b9739bdfbe" dependencies = [ "anchor-syn", - "anyhow", + "borsh-derive-internal 0.10.3", "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] -name = "anchor-derive-accounts" -version = "0.26.0" +name = "anchor-derive-space" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340beef6809d1c3fcc7ae219153d981e95a8a277ff31985bd7050e32645dc9a8" +checksum = "1ecc31d19fa54840e74b7a979d44bcea49d70459de846088a1d71e87ba53c419" dependencies = [ - "anchor-syn", - "anyhow", "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] name = "anchor-lang" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662ceafe667448ee4199a4be2ee83b6bb76da28566eee5cea05f96ab38255af8" +checksum = "35da4785497388af0553586d55ebdc08054a8b1724720ef2749d313494f2b8ad" dependencies = [ "anchor-attribute-access-control", "anchor-attribute-account", "anchor-attribute-constant", "anchor-attribute-error", "anchor-attribute-event", - "anchor-attribute-interface", "anchor-attribute-program", - "anchor-attribute-state", "anchor-derive-accounts", + "anchor-derive-serde", + "anchor-derive-space", "arrayref", "base64 0.13.0", "bincode", - "borsh", + "borsh 0.10.3", "bytemuck", + "getrandom 0.2.10", "solana-program", "thiserror", ] [[package]] name = "anchor-syn" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0418bcb5daac3b8cb1b60d8fdb1d468ca36f5509f31fb51179326fae1028fdcc" +checksum = "d9101b84702fed2ea57bd22992f75065da5648017135b844283a2f6d74f27825" dependencies = [ "anyhow", - "bs58 0.3.1", + "bs58 0.5.0", "heck", "proc-macro2", - "proc-macro2-diagnostics", "quote", "serde", "serde_json", - "sha2 0.9.9", - "syn", + "sha2 0.10.7", + "syn 1.0.108", "thiserror", ] @@ -189,6 +179,123 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +[[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", + "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", + "num-bigint", + "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.108", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.108", +] + +[[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", +] + +[[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.108", +] + +[[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.6" @@ -197,9 +304,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" @@ -219,6 +326,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + [[package]] name = "bincode" version = "1.3.3" @@ -234,6 +347,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + [[package]] name = "bitmaps" version = "2.1.0" @@ -245,16 +367,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "digest 0.10.3", + "digest 0.10.7", ] [[package]] @@ -268,9 +390,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -281,8 +403,18 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive", - "hashbrown", + "borsh-derive 0.9.3", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive 0.10.3", + "hashbrown 0.13.2", ] [[package]] @@ -291,11 +423,24 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", + "borsh-derive-internal 0.9.3", + "borsh-schema-derive-internal 0.9.3", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.108", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal 0.10.3", + "borsh-schema-derive-internal 0.10.3", "proc-macro-crate", "proc-macro2", - "syn", + "syn 1.0.108", ] [[package]] @@ -306,7 +451,18 @@ checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.108", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.108", ] [[package]] @@ -317,14 +473,19 @@ checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] -name = "bs58" -version = "0.3.1" +name = "borsh-schema-derive-internal" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.108", +] [[package]] name = "bs58" @@ -332,6 +493,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -350,9 +520,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" dependencies = [ "bytemuck_derive", ] @@ -365,7 +535,7 @@ checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.108", ] [[package]] @@ -376,9 +546,13 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] [[package]] name = "cfg-if" @@ -398,9 +572,9 @@ dependencies = [ [[package]] name = "console_log" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" dependencies = [ "log", "web-sys", @@ -408,9 +582,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "cpufeatures" @@ -452,7 +626,7 @@ dependencies = [ "cfg-if", "crossbeam-utils", "lazy_static", - "memoffset", + "memoffset 0.6.5", "scopeguard", ] @@ -501,10 +675,22 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", + "serde", "subtle", "zeroize", ] +[[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.108", +] + [[package]] name = "digest" version = "0.9.0" @@ -516,20 +702,20 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.4", "crypto-common", "subtle", ] [[package]] name = "either" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "feature-probe" @@ -539,9 +725,9 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "serde", "typenum", @@ -563,13 +749,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -578,7 +766,16 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -627,7 +824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.6.3", + "rand_core 0.6.4", "rand_xoshiro", "rayon", "serde", @@ -638,9 +835,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -651,20 +848,32 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -674,9 +883,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libsecp256k1" @@ -691,7 +900,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand", + "rand 0.7.3", "serde", "sha2 0.9.9", "typenum", @@ -726,6 +935,17 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "light-poseidon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949bdd22e4ed93481d45e9a6badb34b99132bcad0c8a8d4f05c42f7dcc7b90bc" +dependencies = [ + "ark-bn254", + "ark-ff", + "thiserror", +] + [[package]] name = "lock_api" version = "0.4.7" @@ -738,24 +958,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memmap2" -version = "0.5.4" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -769,6 +980,26 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -777,14 +1008,24 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.108", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -801,9 +1042,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -834,6 +1075,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -851,31 +1107,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - [[package]] name = "quote" -version = "1.0.18" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -888,11 +1131,22 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.16", "libc", - "rand_chacha", + "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_chacha" version = "0.2.2" @@ -903,6 +1157,16 @@ dependencies = [ "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_core" version = "0.5.1" @@ -914,9 +1178,12 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] [[package]] name = "rand_hc" @@ -933,7 +1200,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -966,25 +1233,14 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "bitflags 1.3.2", ] [[package]] -name = "regex-syntax" -version = "0.6.26" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" @@ -997,9 +1253,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" @@ -1021,38 +1277,38 @@ checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.6" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1074,22 +1330,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", "keccak", ] @@ -1111,12 +1367,18 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "solana-frozen-abi" -version = "1.13.5" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1187f9d6ec545567c7d3bdfc3c22c3886331aa4e49b2ad17c8ec5ec3b5334" +checksum = "098378043a888c680de07eee987bf927e23e0720b472ba61c753d7b3757e6b3e" dependencies = [ + "ahash 0.8.3", + "blake3", + "block-buffer 0.10.4", "bs58 0.4.0", "bv", + "byteorder", + "cc", + "either", "generic-array", "im", "lazy_static", @@ -1126,81 +1388,95 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "sha2 0.10.2", + "serde_json", + "sha2 0.10.7", "solana-frozen-abi-macro", + "subtle", "thiserror", ] [[package]] name = "solana-frozen-abi-macro" -version = "1.13.5" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1427b307e619e53e33987967112b0bc5ae2983915e00a46720b589bffe7f924b" +checksum = "c5f48c89a8a3a12f6a409a45fef50dae642211eae0207a91f01211aebb270026" dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.38", ] [[package]] name = "solana-program" -version = "1.13.5" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77cdaa890475c0e21d23f24c5bcc12e0991cf73f2fe98fc1064f065365f6873" +checksum = "6e8834ffcd3773375e7fce4b261efefebbb64409da78e1627cbc4c2632139921" dependencies = [ - "base64 0.13.0", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "base64 0.21.4", "bincode", - "bitflags", + "bitflags 2.4.1", "blake3", - "borsh", - "borsh-derive", + "borsh 0.10.3", + "borsh 0.9.3", "bs58 0.4.0", "bv", "bytemuck", + "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.1.16", + "getrandom 0.2.10", "itertools", "js-sys", "lazy_static", + "libc", "libsecp256k1", + "light-poseidon", "log", + "memoffset 0.9.0", + "num-bigint", "num-derive", "num-traits", "parking_lot", - "rand", + "rand 0.8.5", "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", - "sha2 0.10.2", + "serde_json", + "sha2 0.10.7", "sha3", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-sdk-macro", "thiserror", + "tiny-bip39", "wasm-bindgen", + "zeroize", ] [[package]] name = "solana-sdk-macro" -version = "1.13.5" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df36a10a657b35accd2fd7ae097e9426cbbdbf8219a79e8a7d3be7b500be0e7e" +checksum = "170f3c87d862ba5c74c03fa37a2867c10aa68e7b210bdf004c7a8b330284c789" dependencies = [ "bs58 0.4.0", "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.38", ] [[package]] name = "spl-account-compression" -version = "0.1.8" +version = "0.3.0" dependencies = [ "anchor-lang", "bytemuck", @@ -1210,7 +1486,7 @@ dependencies = [ [[package]] name = "spl-concurrent-merkle-tree" -version = "0.1.3" +version = "0.2.0" dependencies = [ "bytemuck", "solana-program", @@ -1219,7 +1495,7 @@ dependencies = [ [[package]] name = "spl-noop" -version = "0.1.3" +version = "0.2.0" dependencies = [ "solana-program", ] @@ -1241,26 +1517,71 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.5.9" @@ -1282,6 +1603,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -1308,9 +1638,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1318,24 +1648,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1343,22 +1673,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" @@ -1413,14 +1743,22 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "zeroize" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/account-compression/Cargo.toml b/account-compression/Cargo.toml index 1054f841f72..94d054a43fc 100644 --- a/account-compression/Cargo.toml +++ b/account-compression/Cargo.toml @@ -3,3 +3,5 @@ members = [ "programs/account-compression", "programs/noop" ] + +resolver = "2" diff --git a/account-compression/programs/account-compression/Cargo.toml b/account-compression/programs/account-compression/Cargo.toml index 546bcb71c9a..08f150d2f65 100644 --- a/account-compression/programs/account-compression/Cargo.toml +++ b/account-compression/programs/account-compression/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-account-compression" -version = "0.1.10" +version = "0.3.0" description = "Solana Program Library Account Compression Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -18,10 +18,10 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "0.26.0" -bytemuck = "1.8.0" -spl-concurrent-merkle-tree = { version="0.1.2", path="../../../libraries/concurrent-merkle-tree", features = [ "sol-log" ]} -spl-noop = { version = "0.1.3", path="../noop", features = [ "no-entrypoint" ]} +anchor-lang = "0.29.0" +bytemuck = "1.13" +spl-concurrent-merkle-tree = { version="0.2.0", path="../../../libraries/concurrent-merkle-tree", features = [ "sol-log" ]} +spl-noop = { version = "0.2.0", path="../noop", features = [ "no-entrypoint" ]} [profile.release] overflow-checks = true diff --git a/account-compression/programs/account-compression/src/macros.rs b/account-compression/programs/account-compression/src/macros.rs index cac043b6955..cba647cf50b 100644 --- a/account-compression/programs/account-compression/src/macros.rs +++ b/account-compression/programs/account-compression/src/macros.rs @@ -59,6 +59,14 @@ macro_rules! _merkle_tree_apply_fn { match ($header.get_max_depth(), $header.get_max_buffer_size()) { (3, 8) => _merkle_tree_depth_size_apply_fn!(3, 8, $($arg)*), (5, 8) => _merkle_tree_depth_size_apply_fn!(5, 8, $($arg)*), + (6, 16) => _merkle_tree_depth_size_apply_fn!(6, 16, $($arg)*), + (7, 16) => _merkle_tree_depth_size_apply_fn!(7, 16, $($arg)*), + (8, 16) => _merkle_tree_depth_size_apply_fn!(8, 16, $($arg)*), + (9, 16) => _merkle_tree_depth_size_apply_fn!(9, 16, $($arg)*), + (10, 32) => _merkle_tree_depth_size_apply_fn!(10, 32, $($arg)*), + (11, 32) => _merkle_tree_depth_size_apply_fn!(11, 32, $($arg)*), + (12, 32) => _merkle_tree_depth_size_apply_fn!(12, 32, $($arg)*), + (13, 32) => _merkle_tree_depth_size_apply_fn!(13, 32, $($arg)*), (14, 64) => _merkle_tree_depth_size_apply_fn!(14, 64, $($arg)*), (14, 256) => _merkle_tree_depth_size_apply_fn!(14, 256, $($arg)*), (14, 1024) => _merkle_tree_depth_size_apply_fn!(14, 1024, $($arg)*), diff --git a/account-compression/programs/account-compression/src/state/concurrent_merkle_tree_header.rs b/account-compression/programs/account-compression/src/state/concurrent_merkle_tree_header.rs index d9402311050..6d326b76142 100644 --- a/account-compression/programs/account-compression/src/state/concurrent_merkle_tree_header.rs +++ b/account-compression/programs/account-compression/src/state/concurrent_merkle_tree_header.rs @@ -162,6 +162,14 @@ pub fn merkle_tree_get_size(header: &ConcurrentMerkleTreeHeader) -> Result Ok(size_of::>()), (5, 8) => Ok(size_of::>()), + (6, 16) => Ok(size_of::>()), + (7, 16) => Ok(size_of::>()), + (8, 16) => Ok(size_of::>()), + (9, 16) => Ok(size_of::>()), + (10, 32)=> Ok(size_of::>()), + (11, 32)=> Ok(size_of::>()), + (12, 32)=> Ok(size_of::>()), + (13, 32)=> Ok(size_of::>()), (14, 64) => Ok(size_of::>()), (14, 256) => Ok(size_of::>()), (14, 1024) => Ok(size_of::>()), diff --git a/account-compression/programs/noop/Cargo.toml b/account-compression/programs/noop/Cargo.toml index 5e3c00bf7bc..a80e338bc8e 100644 --- a/account-compression/programs/noop/Cargo.toml +++ b/account-compression/programs/noop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-noop" -version = "0.1.3" +version = "0.2.0" description = "Solana Program Library No-op Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -16,4 +16,4 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -solana-program = "1.13.5" +solana-program = "1.17.2" diff --git a/account-compression/sdk/.eslintignore b/account-compression/sdk/.eslintignore index f03093af82b..f5d0a82b2ca 100644 --- a/account-compression/sdk/.eslintignore +++ b/account-compression/sdk/.eslintignore @@ -3,5 +3,4 @@ /jest.config.js /yarn.lock doc -dist -tests \ No newline at end of file +dist \ No newline at end of file diff --git a/account-compression/sdk/.eslintrc.js b/account-compression/sdk/.eslintrc.js index 8777111508a..05db167860a 100644 --- a/account-compression/sdk/.eslintrc.js +++ b/account-compression/sdk/.eslintrc.js @@ -1,47 +1,12 @@ module.exports = { - env: { - browser: true, - es6: true, - node: true, - mocha: true, - }, - extends: [ - "eslint:recommended", - "plugin:import/errors", - "plugin:import/warnings", - "plugin:import/typescript", - ], - parser: "@typescript-eslint/parser", - parserOptions: { - sourceType: "module", - ecmaVersion: 8, - }, - plugins: ["@typescript-eslint"], - rules: { - "@typescript-eslint/no-unused-vars": ["error"], - "import/first": ["error"], - "import/no-commonjs": ["error"], - "import/order": [ - "error", - { - groups: [ - ["internal", "external", "builtin"], - ["index", "sibling", "parent"], - ], - "newlines-between": "always", - }, + extends: ['turbo', '@solana/eslint-config-solana', '@solana/eslint-config-solana/jest'], + overrides: [ + { + files: ['tests/**.ts'], + rules: { + 'no-empty': ['error', { allowEmptyCatch: true }], + }, + }, ], - "linebreak-style": ["error", "unix"], - "no-console": [0], - "no-trailing-spaces": ["error"], - "no-undef": "off", - "no-unused-vars": "off", - quotes: [ - "error", - "single", - { avoidEscape: true, allowTemplateLiterals: true }, - ], - "require-await": ["error"], - semi: ["error", "always"], - }, + root: true, }; diff --git a/account-compression/sdk/.solitarc.js b/account-compression/sdk/.solitarc.js index 7df7d4e9e03..f6613107c1f 100644 --- a/account-compression/sdk/.solitarc.js +++ b/account-compression/sdk/.solitarc.js @@ -1,21 +1,16 @@ // @ts-check -const path = require("path"); -const programDir = path.join( - __dirname, - "..", - "programs", - "account-compression" -); -const idlDir = path.join(__dirname, "idl"); -const sdkDir = path.join(__dirname, "src", "generated"); -const binaryInstallDir = path.join(__dirname, "..", "target", "solita"); +const path = require('path'); +const programDir = path.join(__dirname, '..', 'programs', 'account-compression'); +const idlDir = path.join(__dirname, 'idl'); +const sdkDir = path.join(__dirname, 'src', 'generated'); +const binaryInstallDir = path.join(__dirname, '..', 'target', 'solita'); module.exports = { - idlGenerator: "anchor", - programName: "spl_account_compression", - programId: "cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK", - idlDir, - sdkDir, - binaryInstallDir, - programDir, + idlGenerator: 'anchor', + programName: 'spl_account_compression', + programId: 'cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK', + idlDir, + sdkDir, + binaryInstallDir, + programDir, }; diff --git a/account-compression/sdk/README.md b/account-compression/sdk/README.md index 5d5a8ad8dc2..c631c7fac61 100644 --- a/account-compression/sdk/README.md +++ b/account-compression/sdk/README.md @@ -1,6 +1,7 @@ # `@solana/spl-account-compression` -A TypeScript library for interacting with SPL Account Compression and SPL NoOp. [Docs here](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs/). +A TypeScript library for interacting with SPL Account Compression and SPL NoOp. +For more information, see the full [Solana account compression SDK doumentation](https://solana-labs.github.io/solana-program-library/account-compression/sdk/). ## Install diff --git a/account-compression/sdk/idl/spl_account_compression.json b/account-compression/sdk/idl/spl_account_compression.json index 7fe85863396..1a5de6f83d9 100644 --- a/account-compression/sdk/idl/spl_account_compression.json +++ b/account-compression/sdk/idl/spl_account_compression.json @@ -1,5 +1,5 @@ { - "version": "0.1.4", + "version": "0.2.0", "name": "spl_account_compression", "instructions": [ { @@ -37,9 +37,7 @@ "name": "noop", "isMut": false, "isSigner": false, - "docs": [ - "Program used to emit changelogs as cpi instruction data." - ] + "docs": ["Program used to emit changelogs as cpi instruction data."] } ], "args": [ @@ -88,37 +86,26 @@ "name": "noop", "isMut": false, "isSigner": false, - "docs": [ - "Program used to emit changelogs as cpi instruction data." - ] + "docs": ["Program used to emit changelogs as cpi instruction data."] } ], "args": [ { "name": "root", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { "name": "previousLeaf", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { "name": "newLeaf", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { @@ -129,10 +116,7 @@ }, { "name": "transferAuthority", - "docs": [ - "Transfers `authority`.", - "Requires `authority` to sign" - ], + "docs": ["Transfers `authority`.", "Requires `authority` to sign"], "accounts": [ { "name": "merkleTree", @@ -173,19 +157,13 @@ { "name": "root", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { "name": "leaf", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { @@ -223,19 +201,14 @@ "name": "noop", "isMut": false, "isSigner": false, - "docs": [ - "Program used to emit changelogs as cpi instruction data." - ] + "docs": ["Program used to emit changelogs as cpi instruction data."] } ], "args": [ { "name": "leaf", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } } ] @@ -267,28 +240,20 @@ "name": "noop", "isMut": false, "isSigner": false, - "docs": [ - "Program used to emit changelogs as cpi instruction data." - ] + "docs": ["Program used to emit changelogs as cpi instruction data."] } ], "args": [ { "name": "root", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { "name": "leaf", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { @@ -309,9 +274,7 @@ "name": "authority", "isMut": false, "isSigner": true, - "docs": [ - "Authority that controls write-access to the tree" - ] + "docs": ["Authority that controls write-access to the tree"] }, { "name": "recipient", @@ -342,16 +305,12 @@ "fields": [ { "name": "id", - "docs": [ - "Public key of the ConcurrentMerkleTree" - ], + "docs": ["Public key of the ConcurrentMerkleTree"], "type": "publicKey" }, { "name": "path", - "docs": [ - "Nodes of off-chain merkle tree needed by indexer" - ], + "docs": ["Nodes of off-chain merkle tree needed by indexer"], "type": { "vec": { "defined": "PathNode" @@ -368,9 +327,7 @@ }, { "name": "index", - "docs": [ - "Bitmap of node parity (used when hashing)" - ], + "docs": ["Bitmap of node parity (used when hashing)"], "type": "u32" } ] @@ -397,18 +354,14 @@ "fields": [ { "name": "accountType", - "docs": [ - "Account type" - ], + "docs": ["Account type"], "type": { "defined": "CompressionAccountType" } }, { "name": "header", - "docs": [ - "Versioned header" - ], + "docs": ["Versioned header"], "type": { "defined": "ConcurrentMerkleTreeHeaderData" } @@ -461,10 +414,7 @@ "8-byte alignment is necessary to zero-copy the SPL ConcurrentMerkleTree" ], "type": { - "array": [ - "u8", - 6 - ] + "array": ["u8", 6] } } ] @@ -478,10 +428,7 @@ { "name": "node", "type": { - "array": [ - "u8", - 32 - ] + "array": ["u8", 32] } }, { @@ -631,4 +578,4 @@ "binaryVersion": "0.25.0", "libVersion": "0.25.0" } -} \ No newline at end of file +} diff --git a/account-compression/sdk/jest.config.js b/account-compression/sdk/jest.config.js index 4dd7c478d8d..9cf351f0803 100644 --- a/account-compression/sdk/jest.config.js +++ b/account-compression/sdk/jest.config.js @@ -1,6 +1,6 @@ module.exports = { - preset: "ts-jest/presets/default", - testEnvironment: "node", - testTimeout: 100000, - resolver: "ts-jest-resolver", + preset: 'ts-jest/presets/default', + testEnvironment: 'node', + testTimeout: 100000, + resolver: 'ts-jest-resolver', }; diff --git a/account-compression/sdk/package.json b/account-compression/sdk/package.json index ddb02323969..c6d32d01c9e 100644 --- a/account-compression/sdk/package.json +++ b/account-compression/sdk/package.json @@ -1,7 +1,7 @@ { "name": "@solana/spl-account-compression", "description": "SPL Account Compression Program JS API", - "version": "0.1.8", + "version": "0.2.0", "author": "Solana Labs Maintainers ", "repository": { "url": "https://github.com/solana-labs/solana-program-library", @@ -40,17 +40,17 @@ "lint": "set -ex; npm run pretty; eslint . --ext .js,.ts", "lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts", "docs": "rm -rf docs/ && typedoc --out docs", - "deploy:docs": "yarn docs && gh-pages -d docs", + "deploy:docs": "yarn docs && gh-pages --dest account-compression/sdk --dist docs --dotfiles", "start-validator": "solana-test-validator --reset --quiet --bpf-program cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK ../target/deploy/spl_account_compression.so --bpf-program noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV ../target/deploy/spl_noop.so", "run-tests": "jest tests --detectOpenHandles", "run-tests:events": "jest tests/events --detectOpenHandles", "run-tests:accounts": "jest tests/accounts --detectOpenHandles", "run-tests:e2e": "jest accountCompression.test.ts --detectOpenHandles", - "test:events": "start-server-and-test start-validator http://localhost:8899/health run-tests:events", - "test:accounts": "start-server-and-test start-validator http://localhost:8899/health run-tests:accounts", - "test:e2e": "start-server-and-test start-validator http://localhost:8899/health run-tests:e2e", + "test:events": "start-server-and-test start-validator http://127.0.0.1:8899/health run-tests:events", + "test:accounts": "start-server-and-test start-validator http://127.0.0.1:8899/health run-tests:accounts", + "test:e2e": "start-server-and-test start-validator http://127.0.0.1:8899/health run-tests:e2e", "test:merkle-tree": "jest tests/merkleTree.test.ts --detectOpenHandles", - "test": "start-server-and-test start-validator http://localhost:8899/health run-tests" + "test": "start-server-and-test start-validator http://127.0.0.1:8899/health run-tests" }, "dependencies": { "@metaplex-foundation/beet": "^0.7.1", @@ -67,6 +67,8 @@ "@metaplex-foundation/rustbin": "^0.3.1", "@metaplex-foundation/solita": "0.15.2", "@project-serum/anchor": "^0.25.0", + "@solana/eslint-config-solana": "^1.0.2", + "@solana/prettier-config-solana": "^0.0.2", "@types/bn.js": "^5.1.1", "@types/chai": "^4.3.0", "@types/jest": "^29.0.0", @@ -76,9 +78,13 @@ "chai": "^4.3.4", "eslint": "^8.25.0", "eslint-config-prettier": "^8.5.0", + "eslint-config-turbo": "^1.10.12", "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jest": "^27.2.3", "eslint-plugin-mocha": "^10.1.0", "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-simple-import-sort": "^10.0.0", + "eslint-plugin-sort-keys-fix": "^1.1.2", "gh-pages": "^4.0.0", "jest": "^29.0.1", "jest-config": "^29.0.1", @@ -88,5 +94,6 @@ "ts-node": "^10.9.1", "typedoc": "^0.22.2", "typescript": "=4.7.4" - } + }, + "prettier": "@solana/prettier-config-solana" } diff --git a/account-compression/sdk/src/accounts/ConcurrentMerkleTreeAccount.ts b/account-compression/sdk/src/accounts/ConcurrentMerkleTreeAccount.ts index 7fb8e0476bd..d43f53c4b0e 100644 --- a/account-compression/sdk/src/accounts/ConcurrentMerkleTreeAccount.ts +++ b/account-compression/sdk/src/accounts/ConcurrentMerkleTreeAccount.ts @@ -1,146 +1,126 @@ -import type { - PublicKey, - Connection, - Commitment, - GetAccountInfoConfig, -} from '@solana/web3.js'; +import type { Commitment, Connection, GetAccountInfoConfig, PublicKey } from '@solana/web3.js'; import { BN } from 'bn.js'; +import { ConcurrentMerkleTreeHeaderDataV1, concurrentMerkleTreeHeaderDataV1Beet } from '../generated'; import { - concurrentMerkleTreeHeaderBeet, - ConcurrentMerkleTreeHeader, + ConcurrentMerkleTreeHeader, + concurrentMerkleTreeHeaderBeet, } from '../generated/types/ConcurrentMerkleTreeHeader'; -import { - Canopy, - canopyBeetFactory, - ConcurrentMerkleTree, - concurrentMerkleTreeBeetFactory, -} from '../types'; -import { - ConcurrentMerkleTreeHeaderDataV1, - concurrentMerkleTreeHeaderDataV1Beet, -} from '../generated'; +import { Canopy, canopyBeetFactory, ConcurrentMerkleTree, concurrentMerkleTreeBeetFactory } from '../types'; /** * This class provides all the getter methods to deserialize * information associated with an on-chain ConcurrentMerkleTree */ export class ConcurrentMerkleTreeAccount { - public header: ConcurrentMerkleTreeHeader; - public tree: ConcurrentMerkleTree; - public canopy: Canopy; - - constructor( - header: ConcurrentMerkleTreeHeader, - tree: ConcurrentMerkleTree, - canopy: Canopy - ) { - this.header = header; - this.tree = tree; - this.canopy = canopy; - } - - static fromBuffer(buffer: Buffer): ConcurrentMerkleTreeAccount { - return deserializeConcurrentMerkleTree(buffer); - } - - static async fromAccountAddress( - connection: Connection, - publicKey: PublicKey, - commitmentOrConfig?: Commitment | GetAccountInfoConfig - ): Promise { - const account = await connection.getAccountInfo( - publicKey, - commitmentOrConfig - ); - if (!account) { - throw new Error('CMT account data unexpectedly null!'); - } - return deserializeConcurrentMerkleTree(account.data); - } - - private getHeaderV1(): ConcurrentMerkleTreeHeaderDataV1 { - return this.header.header.fields[0]; - } - - /** - * Returns the `maxBufferSize` for this tree, by reading the account's header - * @returns - */ - getMaxBufferSize(): number { - return this.getHeaderV1().maxBufferSize; - } - - /** - * Returns the `maxDepth` of this tree, by reading the account's header - * @returns - */ - getMaxDepth(): number { - return this.getHeaderV1().maxDepth; - } - - /** - * Returns `min(seq, maxBufferSize)` - * @returns - */ - getBufferSize(): number { - return new BN.BN(this.tree.bufferSize).toNumber(); - } - - /** - * Returns the current root hash for this on-chain tree - * @returns - */ - getCurrentRoot(): Buffer { - return this.tree.changeLogs[this.getCurrentBufferIndex()].root.toBuffer(); - } - - /** - * Returns the index to the spot in the on-chain buffer that stores the current - * root and last changelog. - * - * Should always be `this.getCurrentSeq() % this.getMaxBufferSize()` - * @returns - */ - getCurrentBufferIndex(): number { - return new BN.BN(this.tree.activeIndex).toNumber(); - } - - /** - * Returns the PublicKey that can execute modifying operations - * on this tree - * @returns - */ - getAuthority(): PublicKey { - return this.getHeaderV1().authority; - } - - /** - * Returns the slot that this tree was created in. Useful for indexing - * transactions associated with this tree. - * @returns - */ - getCreationSlot() { - return new BN(this.getHeaderV1().creationSlot); - } - - /** - * Returns the number of modifying operations that have been performed - * on this tree. - * @returns - */ - getCurrentSeq() { - return new BN(this.tree.sequenceNumber); - } - - /** - * Returns the depth of the on-chain tree-cache. Increasing the canopy depth reduces the size of the proofs - * that have to be passed for tree instructions. - * @returns the size - */ - getCanopyDepth(): number { - return getCanopyDepth(this.canopy.canopyBytes.length); - } + public header: ConcurrentMerkleTreeHeader; + public tree: ConcurrentMerkleTree; + public canopy: Canopy; + + constructor(header: ConcurrentMerkleTreeHeader, tree: ConcurrentMerkleTree, canopy: Canopy) { + this.header = header; + this.tree = tree; + this.canopy = canopy; + } + + static fromBuffer(buffer: Buffer): ConcurrentMerkleTreeAccount { + return deserializeConcurrentMerkleTree(buffer); + } + + static async fromAccountAddress( + connection: Connection, + publicKey: PublicKey, + commitmentOrConfig?: Commitment | GetAccountInfoConfig + ): Promise { + const account = await connection.getAccountInfo(publicKey, commitmentOrConfig); + if (!account) { + throw new Error('CMT account data unexpectedly null!'); + } + return deserializeConcurrentMerkleTree(account.data); + } + + private getHeaderV1(): ConcurrentMerkleTreeHeaderDataV1 { + return this.header.header.fields[0]; + } + + /** + * Returns the `maxBufferSize` for this tree, by reading the account's header + * @returns + */ + getMaxBufferSize(): number { + return this.getHeaderV1().maxBufferSize; + } + + /** + * Returns the `maxDepth` of this tree, by reading the account's header + * @returns + */ + getMaxDepth(): number { + return this.getHeaderV1().maxDepth; + } + + /** + * Returns `min(seq, maxBufferSize)` + * @returns + */ + getBufferSize(): number { + return new BN.BN(this.tree.bufferSize).toNumber(); + } + + /** + * Returns the current root hash for this on-chain tree + * @returns + */ + getCurrentRoot(): Buffer { + return this.tree.changeLogs[this.getCurrentBufferIndex()].root.toBuffer(); + } + + /** + * Returns the index to the spot in the on-chain buffer that stores the current + * root and last changelog. + * + * Should always be `this.getCurrentSeq() % this.getMaxBufferSize()` + * @returns + */ + getCurrentBufferIndex(): number { + return new BN.BN(this.tree.activeIndex).toNumber(); + } + + /** + * Returns the PublicKey that can execute modifying operations + * on this tree + * @returns + */ + getAuthority(): PublicKey { + return this.getHeaderV1().authority; + } + + /** + * Returns the slot that this tree was created in. Useful for indexing + * transactions associated with this tree. + * @returns + */ + getCreationSlot() { + return new BN(this.getHeaderV1().creationSlot); + } + + /** + * Returns the number of modifying operations that have been performed + * on this tree. + * @returns + */ + getCurrentSeq() { + return new BN(this.tree.sequenceNumber); + } + + /** + * Returns the depth of the on-chain tree-cache. Increasing the canopy depth reduces the size of the proofs + * that have to be passed for tree instructions. + * @returns the size + */ + getCanopyDepth(): number { + return getCanopyDepth(this.canopy.canopyBytes.length); + } } /** @@ -151,51 +131,42 @@ export class ConcurrentMerkleTreeAccount { * @returns */ export function getCanopyDepth(canopyByteLength: number): number { - if (canopyByteLength === 0) { - return 0; - } - return Math.log2(canopyByteLength / 32 + 2) - 1; + if (canopyByteLength === 0) { + return 0; + } + return Math.log2(canopyByteLength / 32 + 2) - 1; } -function deserializeConcurrentMerkleTree( - buffer: Buffer -): ConcurrentMerkleTreeAccount { - let offset = 0; - const [versionedHeader, offsetIncr] = - concurrentMerkleTreeHeaderBeet.deserialize(buffer); - offset = offsetIncr; - - // Only 1 version available - if (versionedHeader.header.__kind !== 'V1') { - throw Error( - `Header has unsupported version: ${versionedHeader.header.__kind}` - ); - } - const header = versionedHeader.header.fields[0]; - const [tree, offsetIncr2] = concurrentMerkleTreeBeetFactory( - header.maxDepth, - header.maxBufferSize - ).deserialize(buffer, offset); - offset = offsetIncr2; - - const canopyDepth = getCanopyDepth(buffer.byteLength - offset); - let canopy: Canopy = { - canopyBytes: [], - }; - if (canopyDepth !== 0) { - const [deserializedCanopy, offsetIncr3] = canopyBeetFactory( - canopyDepth - ).deserialize(buffer, offset); - canopy = deserializedCanopy; - offset = offsetIncr3; - } - - if (buffer.byteLength !== offset) { - throw new Error( - 'Failed to process whole buffer when deserializing Merkle Account Data' +function deserializeConcurrentMerkleTree(buffer: Buffer): ConcurrentMerkleTreeAccount { + let offset = 0; + const [versionedHeader, offsetIncr] = concurrentMerkleTreeHeaderBeet.deserialize(buffer); + offset = offsetIncr; + + // Only 1 version available + if (versionedHeader.header.__kind !== 'V1') { + throw Error(`Header has unsupported version: ${versionedHeader.header.__kind}`); + } + const header = versionedHeader.header.fields[0]; + const [tree, offsetIncr2] = concurrentMerkleTreeBeetFactory(header.maxDepth, header.maxBufferSize).deserialize( + buffer, + offset ); - } - return new ConcurrentMerkleTreeAccount(versionedHeader, tree, canopy); + offset = offsetIncr2; + + const canopyDepth = getCanopyDepth(buffer.byteLength - offset); + let canopy: Canopy = { + canopyBytes: [], + }; + if (canopyDepth !== 0) { + const [deserializedCanopy, offsetIncr3] = canopyBeetFactory(canopyDepth).deserialize(buffer, offset); + canopy = deserializedCanopy; + offset = offsetIncr3; + } + + if (buffer.byteLength !== offset) { + throw new Error('Failed to process whole buffer when deserializing Merkle Account Data'); + } + return new ConcurrentMerkleTreeAccount(versionedHeader, tree, canopy); } /** @@ -207,22 +178,22 @@ function deserializeConcurrentMerkleTree( * @returns */ export function getConcurrentMerkleTreeAccountSize( - maxDepth: number, - maxBufferSize: number, - canopyDepth?: number, - headerVersion: string = 'V1' + maxDepth: number, + maxBufferSize: number, + canopyDepth?: number, + headerVersion = 'V1' ): number { - if (headerVersion != 'V1') { - throw Error('Unsupported header version'); - } - - // The additional 2 bytes are needed for - // - the account disciminant (1 byte) - // - the header version (1 byte) - return ( - 2 + - concurrentMerkleTreeHeaderDataV1Beet.byteSize + - concurrentMerkleTreeBeetFactory(maxDepth, maxBufferSize).byteSize + - (canopyDepth ? canopyBeetFactory(canopyDepth).byteSize : 0) - ); + if (headerVersion != 'V1') { + throw Error('Unsupported header version'); + } + + // The additional 2 bytes are needed for + // - the account disciminant (1 byte) + // - the header version (1 byte) + return ( + 2 + + concurrentMerkleTreeHeaderDataV1Beet.byteSize + + concurrentMerkleTreeBeetFactory(maxDepth, maxBufferSize).byteSize + + (canopyDepth ? canopyBeetFactory(canopyDepth).byteSize : 0) + ); } diff --git a/account-compression/sdk/src/constants/index.ts b/account-compression/sdk/src/constants/index.ts index f9e023f4113..d7e7875e812 100644 --- a/account-compression/sdk/src/constants/index.ts +++ b/account-compression/sdk/src/constants/index.ts @@ -1,6 +1,6 @@ -import { PublicKey } from "@solana/web3.js"; +import { PublicKey } from '@solana/web3.js'; -export const SPL_NOOP_ADDRESS = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; +export const SPL_NOOP_ADDRESS = 'noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV'; export const SPL_NOOP_PROGRAM_ID = new PublicKey(SPL_NOOP_ADDRESS); /** @@ -9,75 +9,89 @@ export const SPL_NOOP_PROGRAM_ID = new PublicKey(SPL_NOOP_ADDRESS); * creating a new {@link ConcurrentMerkleTreeAccount}. */ export type DepthSizePair = { - maxDepth: number; - maxBufferSize: number; + maxDepth: number; + maxBufferSize: number; }; const allPairs: number[][] = [ - [3, 8], - [5, 8], - [14, 64], - [14, 256], - [14, 1024], - [14, 2048], - [15, 64], - [16, 64], - [17, 64], - [18, 64], - [19, 64], - [20, 64], - [20, 256], - [20, 1024], - [20, 2048], - [24, 64], - [24, 256], - [24, 512], - [24, 1024], - [24, 2048], - [26, 512], - [26, 1024], - [26, 2048], - [30, 512], - [30, 1024], - [30, 2048], + [3, 8], + [5, 8], + [6, 16], + [7, 16], + [8, 16], + [9, 16], + [10, 32], + [11, 32], + [12, 32], + [13, 32], + [14, 64], + [14, 256], + [14, 1024], + [14, 2048], + [15, 64], + [16, 64], + [17, 64], + [18, 64], + [19, 64], + [20, 64], + [20, 256], + [20, 1024], + [20, 2048], + [24, 64], + [24, 256], + [24, 512], + [24, 1024], + [24, 2048], + [26, 512], + [26, 1024], + [26, 2048], + [30, 512], + [30, 1024], + [30, 2048], ]; /** * Valid pairs for creating a new {@link ConcurrentMerkleTreeAccount} */ -export const ALL_DEPTH_SIZE_PAIRS: ValidDepthSizePair[] = allPairs.map( - (pair) => { +export const ALL_DEPTH_SIZE_PAIRS: ValidDepthSizePair[] = allPairs.map(pair => { return { - maxDepth: pair[0], - maxBufferSize: pair[1], + maxBufferSize: pair[1], + maxDepth: pair[0], } as ValidDepthSizePair; - } -); +}); export type ValidDepthSizePair = - | { maxDepth: 3; maxBufferSize: 8 } - | { maxDepth: 5; maxBufferSize: 8 } - | { maxDepth: 14; maxBufferSize: 64 } - | { maxDepth: 14; maxBufferSize: 256 } - | { maxDepth: 14; maxBufferSize: 1024 } - | { maxDepth: 14; maxBufferSize: 2048 } - | { maxDepth: 15; maxBufferSize: 64 } - | { maxDepth: 16; maxBufferSize: 64 } - | { maxDepth: 17; maxBufferSize: 64 } - | { maxDepth: 18; maxBufferSize: 64 } - | { maxDepth: 19; maxBufferSize: 64 } - | { maxDepth: 20; maxBufferSize: 64 } - | { maxDepth: 20; maxBufferSize: 256 } - | { maxDepth: 20; maxBufferSize: 1024 } - | { maxDepth: 20; maxBufferSize: 2048 } - | { maxDepth: 24; maxBufferSize: 64 } - | { maxDepth: 24; maxBufferSize: 256 } - | { maxDepth: 24; maxBufferSize: 512 } - | { maxDepth: 24; maxBufferSize: 1024 } - | { maxDepth: 24; maxBufferSize: 2048 } - | { maxDepth: 26; maxBufferSize: 512 } - | { maxDepth: 26; maxBufferSize: 1024 } - | { maxDepth: 26; maxBufferSize: 2048 } - | { maxDepth: 30; maxBufferSize: 512 } - | { maxDepth: 30; maxBufferSize: 1024 } - | { maxDepth: 30; maxBufferSize: 2048 }; + | { maxDepth: 3; maxBufferSize: 8 } + | { maxDepth: 5; maxBufferSize: 8 } + | { maxDepth: 6; maxBufferSize: 16 } + | { maxDepth: 7; maxBufferSize: 16 } + | { maxDepth: 8; maxBufferSize: 16 } + | { maxDepth: 9; maxBufferSize: 16 } + | { maxDepth: 10; maxBufferSize: 32 } + | { maxDepth: 11; maxBufferSize: 32 } + | { maxDepth: 12; maxBufferSize: 32 } + | { maxDepth: 13; maxBufferSize: 32 } + | { maxDepth: 14; maxBufferSize: 64 } + | { maxDepth: 14; maxBufferSize: 256 } + | { maxDepth: 14; maxBufferSize: 1024 } + | { maxDepth: 14; maxBufferSize: 2048 } + | { maxDepth: 15; maxBufferSize: 64 } + | { maxDepth: 16; maxBufferSize: 64 } + | { maxDepth: 17; maxBufferSize: 64 } + | { maxDepth: 18; maxBufferSize: 64 } + | { maxDepth: 19; maxBufferSize: 64 } + | { maxDepth: 20; maxBufferSize: 64 } + | { maxDepth: 20; maxBufferSize: 256 } + | { maxDepth: 20; maxBufferSize: 1024 } + | { maxDepth: 20; maxBufferSize: 2048 } + | { maxDepth: 24; maxBufferSize: 64 } + | { maxDepth: 24; maxBufferSize: 256 } + | { maxDepth: 24; maxBufferSize: 512 } + | { maxDepth: 24; maxBufferSize: 1024 } + | { maxDepth: 24; maxBufferSize: 2048 } + | { maxDepth: 26; maxBufferSize: 512 } + | { maxDepth: 26; maxBufferSize: 1024 } + | { maxDepth: 26; maxBufferSize: 2048 } + | { maxDepth: 30; maxBufferSize: 512 } + | { maxDepth: 30; maxBufferSize: 1024 } + | { maxDepth: 30; maxBufferSize: 2048 }; diff --git a/account-compression/sdk/src/events/index.ts b/account-compression/sdk/src/events/index.ts index 735f2d1d182..ac9341575d6 100644 --- a/account-compression/sdk/src/events/index.ts +++ b/account-compression/sdk/src/events/index.ts @@ -1,8 +1,8 @@ import BN from 'bn.js'; -import { ChangeLogEventV1 } from '../types'; -import { accountCompressionEventBeet } from '../generated/types/AccountCompressionEvent'; import { ApplicationDataEvent, ChangeLogEventV1 as CLV1 } from '../generated'; +import { accountCompressionEventBeet } from '../generated/types/AccountCompressionEvent'; +import { ChangeLogEventV1 } from '../types'; /** * Helper method for indexing a {@link ConcurrentMerkleTree} @@ -10,21 +10,19 @@ import { ApplicationDataEvent, ChangeLogEventV1 as CLV1 } from '../generated'; * @returns */ export function deserializeChangeLogEventV1(data: Buffer): ChangeLogEventV1 { - const event = accountCompressionEventBeet - .toFixedFromData(data, 0) - .read(data, 0); + const event = accountCompressionEventBeet.toFixedFromData(data, 0).read(data, 0); - if (event.__kind == 'ChangeLog' && event.fields[0].__kind == 'V1') { - const changeLogV1: CLV1 = event.fields[0].fields[0]; - return { - treeId: changeLogV1.id, - seq: new BN.BN(changeLogV1.seq), - path: changeLogV1.path, - index: changeLogV1.index, - }; - } else { - throw Error('Unable to decode buffer as ChangeLogEvent V1'); - } + if (event.__kind == 'ChangeLog' && event.fields[0].__kind == 'V1') { + const changeLogV1: CLV1 = event.fields[0].fields[0]; + return { + index: changeLogV1.index, + path: changeLogV1.path, + seq: new BN.BN(changeLogV1.seq), + treeId: changeLogV1.id, + }; + } else { + throw Error('Unable to decode buffer as ChangeLogEvent V1'); + } } /** @@ -32,17 +30,13 @@ export function deserializeChangeLogEventV1(data: Buffer): ChangeLogEventV1 { * @param data * @returns */ -export function deserializeApplicationDataEvent( - data: Buffer -): ApplicationDataEvent { - const event = accountCompressionEventBeet - .toFixedFromData(data, 0) - .read(data, 0); - switch (event.__kind) { - case 'ApplicationData': { - return event.fields[0]; +export function deserializeApplicationDataEvent(data: Buffer): ApplicationDataEvent { + const event = accountCompressionEventBeet.toFixedFromData(data, 0).read(data, 0); + switch (event.__kind) { + case 'ApplicationData': { + return event.fields[0]; + } + default: + throw Error('Unable to decode buffer as ApplicationDataEvent'); } - default: - throw Error('Unable to decode buffer as ApplicationDataEvent'); - } } diff --git a/account-compression/sdk/src/generated/errors/index.ts b/account-compression/sdk/src/generated/errors/index.ts index d67e5c53415..15cea66afaa 100644 --- a/account-compression/sdk/src/generated/errors/index.ts +++ b/account-compression/sdk/src/generated/errors/index.ts @@ -18,21 +18,18 @@ const createErrorFromNameLookup: Map ErrorWithCode> = new Map(); * @category generated */ export class IncorrectLeafLengthError extends Error { - readonly code: number = 0x1770; - readonly name: string = 'IncorrectLeafLength'; - constructor() { - super('Incorrect leaf length. Expected vec of 32 bytes'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, IncorrectLeafLengthError); + readonly code: number = 0x1770; + readonly name: string = 'IncorrectLeafLength'; + constructor() { + super('Incorrect leaf length. Expected vec of 32 bytes'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, IncorrectLeafLengthError); + } } - } } createErrorFromCodeLookup.set(0x1770, () => new IncorrectLeafLengthError()); -createErrorFromNameLookup.set( - 'IncorrectLeafLength', - () => new IncorrectLeafLengthError() -); +createErrorFromNameLookup.set('IncorrectLeafLength', () => new IncorrectLeafLengthError()); /** * ConcurrentMerkleTreeError: 'Concurrent merkle tree error' @@ -41,24 +38,18 @@ createErrorFromNameLookup.set( * @category generated */ export class ConcurrentMerkleTreeErrorError extends Error { - readonly code: number = 0x1771; - readonly name: string = 'ConcurrentMerkleTreeError'; - constructor() { - super('Concurrent merkle tree error'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, ConcurrentMerkleTreeErrorError); + readonly code: number = 0x1771; + readonly name: string = 'ConcurrentMerkleTreeError'; + constructor() { + super('Concurrent merkle tree error'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, ConcurrentMerkleTreeErrorError); + } } - } } -createErrorFromCodeLookup.set( - 0x1771, - () => new ConcurrentMerkleTreeErrorError() -); -createErrorFromNameLookup.set( - 'ConcurrentMerkleTreeError', - () => new ConcurrentMerkleTreeErrorError() -); +createErrorFromCodeLookup.set(0x1771, () => new ConcurrentMerkleTreeErrorError()); +createErrorFromNameLookup.set('ConcurrentMerkleTreeError', () => new ConcurrentMerkleTreeErrorError()); /** * ZeroCopyError: 'Issue zero copying concurrent merkle tree data' @@ -67,14 +58,14 @@ createErrorFromNameLookup.set( * @category generated */ export class ZeroCopyErrorError extends Error { - readonly code: number = 0x1772; - readonly name: string = 'ZeroCopyError'; - constructor() { - super('Issue zero copying concurrent merkle tree data'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, ZeroCopyErrorError); + readonly code: number = 0x1772; + readonly name: string = 'ZeroCopyError'; + constructor() { + super('Issue zero copying concurrent merkle tree data'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, ZeroCopyErrorError); + } } - } } createErrorFromCodeLookup.set(0x1772, () => new ZeroCopyErrorError()); @@ -87,23 +78,20 @@ createErrorFromNameLookup.set('ZeroCopyError', () => new ZeroCopyErrorError()); * @category generated */ export class ConcurrentMerkleTreeConstantsErrorError extends Error { - readonly code: number = 0x1773; - readonly name: string = 'ConcurrentMerkleTreeConstantsError'; - constructor() { - super('An unsupported max depth or max buffer size constant was provided'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, ConcurrentMerkleTreeConstantsErrorError); + readonly code: number = 0x1773; + readonly name: string = 'ConcurrentMerkleTreeConstantsError'; + constructor() { + super('An unsupported max depth or max buffer size constant was provided'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, ConcurrentMerkleTreeConstantsErrorError); + } } - } } -createErrorFromCodeLookup.set( - 0x1773, - () => new ConcurrentMerkleTreeConstantsErrorError() -); +createErrorFromCodeLookup.set(0x1773, () => new ConcurrentMerkleTreeConstantsErrorError()); createErrorFromNameLookup.set( - 'ConcurrentMerkleTreeConstantsError', - () => new ConcurrentMerkleTreeConstantsErrorError() + 'ConcurrentMerkleTreeConstantsError', + () => new ConcurrentMerkleTreeConstantsErrorError() ); /** @@ -113,21 +101,18 @@ createErrorFromNameLookup.set( * @category generated */ export class CanopyLengthMismatchError extends Error { - readonly code: number = 0x1774; - readonly name: string = 'CanopyLengthMismatch'; - constructor() { - super('Expected a different byte length for the merkle tree canopy'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, CanopyLengthMismatchError); + readonly code: number = 0x1774; + readonly name: string = 'CanopyLengthMismatch'; + constructor() { + super('Expected a different byte length for the merkle tree canopy'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, CanopyLengthMismatchError); + } } - } } createErrorFromCodeLookup.set(0x1774, () => new CanopyLengthMismatchError()); -createErrorFromNameLookup.set( - 'CanopyLengthMismatch', - () => new CanopyLengthMismatchError() -); +createErrorFromNameLookup.set('CanopyLengthMismatch', () => new CanopyLengthMismatchError()); /** * IncorrectAuthority: 'Provided authority does not match expected tree authority' @@ -136,21 +121,18 @@ createErrorFromNameLookup.set( * @category generated */ export class IncorrectAuthorityError extends Error { - readonly code: number = 0x1775; - readonly name: string = 'IncorrectAuthority'; - constructor() { - super('Provided authority does not match expected tree authority'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, IncorrectAuthorityError); + readonly code: number = 0x1775; + readonly name: string = 'IncorrectAuthority'; + constructor() { + super('Provided authority does not match expected tree authority'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, IncorrectAuthorityError); + } } - } } createErrorFromCodeLookup.set(0x1775, () => new IncorrectAuthorityError()); -createErrorFromNameLookup.set( - 'IncorrectAuthority', - () => new IncorrectAuthorityError() -); +createErrorFromNameLookup.set('IncorrectAuthority', () => new IncorrectAuthorityError()); /** * IncorrectAccountOwner: 'Account is owned by a different program, expected it to be owned by this program' @@ -159,23 +141,18 @@ createErrorFromNameLookup.set( * @category generated */ export class IncorrectAccountOwnerError extends Error { - readonly code: number = 0x1776; - readonly name: string = 'IncorrectAccountOwner'; - constructor() { - super( - 'Account is owned by a different program, expected it to be owned by this program' - ); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, IncorrectAccountOwnerError); + readonly code: number = 0x1776; + readonly name: string = 'IncorrectAccountOwner'; + constructor() { + super('Account is owned by a different program, expected it to be owned by this program'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, IncorrectAccountOwnerError); + } } - } } createErrorFromCodeLookup.set(0x1776, () => new IncorrectAccountOwnerError()); -createErrorFromNameLookup.set( - 'IncorrectAccountOwner', - () => new IncorrectAccountOwnerError() -); +createErrorFromNameLookup.set('IncorrectAccountOwner', () => new IncorrectAccountOwnerError()); /** * IncorrectAccountType: 'Account provided has incorrect account type' @@ -184,21 +161,18 @@ createErrorFromNameLookup.set( * @category generated */ export class IncorrectAccountTypeError extends Error { - readonly code: number = 0x1777; - readonly name: string = 'IncorrectAccountType'; - constructor() { - super('Account provided has incorrect account type'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, IncorrectAccountTypeError); + readonly code: number = 0x1777; + readonly name: string = 'IncorrectAccountType'; + constructor() { + super('Account provided has incorrect account type'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, IncorrectAccountTypeError); + } } - } } createErrorFromCodeLookup.set(0x1777, () => new IncorrectAccountTypeError()); -createErrorFromNameLookup.set( - 'IncorrectAccountType', - () => new IncorrectAccountTypeError() -); +createErrorFromNameLookup.set('IncorrectAccountType', () => new IncorrectAccountTypeError()); /** * LeafIndexOutOfBounds: 'Leaf index of concurrent merkle tree is out of bounds' @@ -207,21 +181,18 @@ createErrorFromNameLookup.set( * @category generated */ export class LeafIndexOutOfBoundsError extends Error { - readonly code: number = 0x1778; - readonly name: string = 'LeafIndexOutOfBounds'; - constructor() { - super('Leaf index of concurrent merkle tree is out of bounds'); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, LeafIndexOutOfBoundsError); + readonly code: number = 0x1778; + readonly name: string = 'LeafIndexOutOfBounds'; + constructor() { + super('Leaf index of concurrent merkle tree is out of bounds'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, LeafIndexOutOfBoundsError); + } } - } } createErrorFromCodeLookup.set(0x1778, () => new LeafIndexOutOfBoundsError()); -createErrorFromNameLookup.set( - 'LeafIndexOutOfBounds', - () => new LeafIndexOutOfBoundsError() -); +createErrorFromNameLookup.set('LeafIndexOutOfBounds', () => new LeafIndexOutOfBoundsError()); /** * Attempts to resolve a custom program error from the provided error code. @@ -229,8 +200,8 @@ createErrorFromNameLookup.set( * @category generated */ export function errorFromCode(code: number): MaybeErrorWithCode { - const createError = createErrorFromCodeLookup.get(code); - return createError != null ? createError() : null; + const createError = createErrorFromCodeLookup.get(code); + return createError != null ? createError() : null; } /** @@ -239,6 +210,6 @@ export function errorFromCode(code: number): MaybeErrorWithCode { * @category generated */ export function errorFromName(name: string): MaybeErrorWithCode { - const createError = createErrorFromNameLookup.get(name); - return createError != null ? createError() : null; + const createError = createErrorFromNameLookup.get(name); + return createError != null ? createError() : null; } diff --git a/account-compression/sdk/src/generated/instructions/append.ts b/account-compression/sdk/src/generated/instructions/append.ts index 68344cf3f3b..56260a2ab1c 100644 --- a/account-compression/sdk/src/generated/instructions/append.ts +++ b/account-compression/sdk/src/generated/instructions/append.ts @@ -14,7 +14,7 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export type AppendInstructionArgs = { - leaf: number[] /* size: 32 */; + leaf: number[] /* size: 32 */; }; /** * @category Instructions @@ -22,15 +22,15 @@ export type AppendInstructionArgs = { * @category generated */ export const appendStruct = new beet.BeetArgsStruct< - AppendInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + AppendInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], - ], - 'AppendInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ], + 'AppendInstructionArgs' ); /** * Accounts required by the _append_ instruction @@ -43,15 +43,13 @@ export const appendStruct = new beet.BeetArgsStruct< * @category generated */ export type AppendInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - noop: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + noop: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const appendInstructionDiscriminator = [ - 149, 120, 18, 222, 236, 225, 88, 203, -]; +export const appendInstructionDiscriminator = [149, 120, 18, 222, 236, 225, 88, 203]; /** * Creates a _Append_ instruction. @@ -64,42 +62,42 @@ export const appendInstructionDiscriminator = [ * @category generated */ export function createAppendInstruction( - accounts: AppendInstructionAccounts, - args: AppendInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: AppendInstructionAccounts, + args: AppendInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = appendStruct.serialize({ - instructionDiscriminator: appendInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - { - pubkey: accounts.noop, - isWritable: false, - isSigner: false, - }, - ]; + const [data] = appendStruct.serialize({ + instructionDiscriminator: appendInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + { + isSigner: false, + isWritable: false, + pubkey: accounts.noop, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/closeEmptyTree.ts b/account-compression/sdk/src/generated/instructions/closeEmptyTree.ts index c9120001341..63aeeca4a2b 100644 --- a/account-compression/sdk/src/generated/instructions/closeEmptyTree.ts +++ b/account-compression/sdk/src/generated/instructions/closeEmptyTree.ts @@ -14,11 +14,8 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export const closeEmptyTreeStruct = new beet.BeetArgsStruct<{ - instructionDiscriminator: number[] /* size: 8 */; -}>( - [['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], - 'CloseEmptyTreeInstructionArgs' -); + instructionDiscriminator: number[] /* size: 8 */; +}>([['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], 'CloseEmptyTreeInstructionArgs'); /** * Accounts required by the _closeEmptyTree_ instruction * @@ -30,15 +27,13 @@ export const closeEmptyTreeStruct = new beet.BeetArgsStruct<{ * @category generated */ export type CloseEmptyTreeInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - recipient: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + recipient: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const closeEmptyTreeInstructionDiscriminator = [ - 50, 14, 219, 107, 78, 103, 16, 103, -]; +export const closeEmptyTreeInstructionDiscriminator = [50, 14, 219, 107, 78, 103, 16, 103]; /** * Creates a _CloseEmptyTree_ instruction. @@ -49,40 +44,40 @@ export const closeEmptyTreeInstructionDiscriminator = [ * @category generated */ export function createCloseEmptyTreeInstruction( - accounts: CloseEmptyTreeInstructionAccounts, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: CloseEmptyTreeInstructionAccounts, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = closeEmptyTreeStruct.serialize({ - instructionDiscriminator: closeEmptyTreeInstructionDiscriminator, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - { - pubkey: accounts.recipient, - isWritable: true, - isSigner: false, - }, - ]; + const [data] = closeEmptyTreeStruct.serialize({ + instructionDiscriminator: closeEmptyTreeInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + { + isSigner: false, + isWritable: true, + pubkey: accounts.recipient, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/initEmptyMerkleTree.ts b/account-compression/sdk/src/generated/instructions/initEmptyMerkleTree.ts index 97a87446a14..610cde2ef1d 100644 --- a/account-compression/sdk/src/generated/instructions/initEmptyMerkleTree.ts +++ b/account-compression/sdk/src/generated/instructions/initEmptyMerkleTree.ts @@ -14,8 +14,8 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export type InitEmptyMerkleTreeInstructionArgs = { - maxDepth: number; - maxBufferSize: number; + maxDepth: number; + maxBufferSize: number; }; /** * @category Instructions @@ -23,16 +23,16 @@ export type InitEmptyMerkleTreeInstructionArgs = { * @category generated */ export const initEmptyMerkleTreeStruct = new beet.BeetArgsStruct< - InitEmptyMerkleTreeInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + InitEmptyMerkleTreeInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['maxDepth', beet.u32], - ['maxBufferSize', beet.u32], - ], - 'InitEmptyMerkleTreeInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['maxDepth', beet.u32], + ['maxBufferSize', beet.u32], + ], + 'InitEmptyMerkleTreeInstructionArgs' ); /** * Accounts required by the _initEmptyMerkleTree_ instruction @@ -45,15 +45,13 @@ export const initEmptyMerkleTreeStruct = new beet.BeetArgsStruct< * @category generated */ export type InitEmptyMerkleTreeInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - noop: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + noop: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const initEmptyMerkleTreeInstructionDiscriminator = [ - 191, 11, 119, 7, 180, 107, 220, 110, -]; +export const initEmptyMerkleTreeInstructionDiscriminator = [191, 11, 119, 7, 180, 107, 220, 110]; /** * Creates a _InitEmptyMerkleTree_ instruction. @@ -66,42 +64,42 @@ export const initEmptyMerkleTreeInstructionDiscriminator = [ * @category generated */ export function createInitEmptyMerkleTreeInstruction( - accounts: InitEmptyMerkleTreeInstructionAccounts, - args: InitEmptyMerkleTreeInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: InitEmptyMerkleTreeInstructionAccounts, + args: InitEmptyMerkleTreeInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = initEmptyMerkleTreeStruct.serialize({ - instructionDiscriminator: initEmptyMerkleTreeInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - { - pubkey: accounts.noop, - isWritable: false, - isSigner: false, - }, - ]; + const [data] = initEmptyMerkleTreeStruct.serialize({ + instructionDiscriminator: initEmptyMerkleTreeInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + { + isSigner: false, + isWritable: false, + pubkey: accounts.noop, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/insertOrAppend.ts b/account-compression/sdk/src/generated/instructions/insertOrAppend.ts index 9d3cd1983a4..d3ed9f4389c 100644 --- a/account-compression/sdk/src/generated/instructions/insertOrAppend.ts +++ b/account-compression/sdk/src/generated/instructions/insertOrAppend.ts @@ -14,9 +14,9 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export type InsertOrAppendInstructionArgs = { - root: number[] /* size: 32 */; - leaf: number[] /* size: 32 */; - index: number; + root: number[] /* size: 32 */; + leaf: number[] /* size: 32 */; + index: number; }; /** * @category Instructions @@ -24,17 +24,17 @@ export type InsertOrAppendInstructionArgs = { * @category generated */ export const insertOrAppendStruct = new beet.BeetArgsStruct< - InsertOrAppendInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + InsertOrAppendInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['root', beet.uniformFixedSizeArray(beet.u8, 32)], - ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], - ['index', beet.u32], - ], - 'InsertOrAppendInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ['index', beet.u32], + ], + 'InsertOrAppendInstructionArgs' ); /** * Accounts required by the _insertOrAppend_ instruction @@ -47,15 +47,13 @@ export const insertOrAppendStruct = new beet.BeetArgsStruct< * @category generated */ export type InsertOrAppendInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - noop: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + noop: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const insertOrAppendInstructionDiscriminator = [ - 6, 42, 50, 190, 51, 109, 178, 168, -]; +export const insertOrAppendInstructionDiscriminator = [6, 42, 50, 190, 51, 109, 178, 168]; /** * Creates a _InsertOrAppend_ instruction. @@ -68,42 +66,42 @@ export const insertOrAppendInstructionDiscriminator = [ * @category generated */ export function createInsertOrAppendInstruction( - accounts: InsertOrAppendInstructionAccounts, - args: InsertOrAppendInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: InsertOrAppendInstructionAccounts, + args: InsertOrAppendInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = insertOrAppendStruct.serialize({ - instructionDiscriminator: insertOrAppendInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - { - pubkey: accounts.noop, - isWritable: false, - isSigner: false, - }, - ]; + const [data] = insertOrAppendStruct.serialize({ + instructionDiscriminator: insertOrAppendInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + { + isSigner: false, + isWritable: false, + pubkey: accounts.noop, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/replaceLeaf.ts b/account-compression/sdk/src/generated/instructions/replaceLeaf.ts index 582950fb435..513d0d61800 100644 --- a/account-compression/sdk/src/generated/instructions/replaceLeaf.ts +++ b/account-compression/sdk/src/generated/instructions/replaceLeaf.ts @@ -14,10 +14,10 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export type ReplaceLeafInstructionArgs = { - root: number[] /* size: 32 */; - previousLeaf: number[] /* size: 32 */; - newLeaf: number[] /* size: 32 */; - index: number; + root: number[] /* size: 32 */; + previousLeaf: number[] /* size: 32 */; + newLeaf: number[] /* size: 32 */; + index: number; }; /** * @category Instructions @@ -25,18 +25,18 @@ export type ReplaceLeafInstructionArgs = { * @category generated */ export const replaceLeafStruct = new beet.BeetArgsStruct< - ReplaceLeafInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + ReplaceLeafInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['root', beet.uniformFixedSizeArray(beet.u8, 32)], - ['previousLeaf', beet.uniformFixedSizeArray(beet.u8, 32)], - ['newLeaf', beet.uniformFixedSizeArray(beet.u8, 32)], - ['index', beet.u32], - ], - 'ReplaceLeafInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['previousLeaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ['newLeaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ['index', beet.u32], + ], + 'ReplaceLeafInstructionArgs' ); /** * Accounts required by the _replaceLeaf_ instruction @@ -49,15 +49,13 @@ export const replaceLeafStruct = new beet.BeetArgsStruct< * @category generated */ export type ReplaceLeafInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - noop: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + noop: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const replaceLeafInstructionDiscriminator = [ - 204, 165, 76, 100, 73, 147, 0, 128, -]; +export const replaceLeafInstructionDiscriminator = [204, 165, 76, 100, 73, 147, 0, 128]; /** * Creates a _ReplaceLeaf_ instruction. @@ -70,42 +68,42 @@ export const replaceLeafInstructionDiscriminator = [ * @category generated */ export function createReplaceLeafInstruction( - accounts: ReplaceLeafInstructionAccounts, - args: ReplaceLeafInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: ReplaceLeafInstructionAccounts, + args: ReplaceLeafInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = replaceLeafStruct.serialize({ - instructionDiscriminator: replaceLeafInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - { - pubkey: accounts.noop, - isWritable: false, - isSigner: false, - }, - ]; + const [data] = replaceLeafStruct.serialize({ + instructionDiscriminator: replaceLeafInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + { + isSigner: false, + isWritable: false, + pubkey: accounts.noop, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/transferAuthority.ts b/account-compression/sdk/src/generated/instructions/transferAuthority.ts index 0fbf9f2583e..c26457c28e7 100644 --- a/account-compression/sdk/src/generated/instructions/transferAuthority.ts +++ b/account-compression/sdk/src/generated/instructions/transferAuthority.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from '@solana/web3.js'; -import * as beetSolana from '@metaplex-foundation/beet-solana'; import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as beet from '@metaplex-foundation/beet'; * @category generated */ export type TransferAuthorityInstructionArgs = { - newAuthority: web3.PublicKey; + newAuthority: web3.PublicKey; }; /** * @category Instructions @@ -23,15 +23,15 @@ export type TransferAuthorityInstructionArgs = { * @category generated */ export const transferAuthorityStruct = new beet.BeetArgsStruct< - TransferAuthorityInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + TransferAuthorityInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['newAuthority', beetSolana.publicKey], - ], - 'TransferAuthorityInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['newAuthority', beetSolana.publicKey], + ], + 'TransferAuthorityInstructionArgs' ); /** * Accounts required by the _transferAuthority_ instruction @@ -43,14 +43,12 @@ export const transferAuthorityStruct = new beet.BeetArgsStruct< * @category generated */ export type TransferAuthorityInstructionAccounts = { - merkleTree: web3.PublicKey; - authority: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + authority: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const transferAuthorityInstructionDiscriminator = [ - 48, 169, 76, 72, 229, 180, 55, 161, -]; +export const transferAuthorityInstructionDiscriminator = [48, 169, 76, 72, 229, 180, 55, 161]; /** * Creates a _TransferAuthority_ instruction. @@ -63,37 +61,37 @@ export const transferAuthorityInstructionDiscriminator = [ * @category generated */ export function createTransferAuthorityInstruction( - accounts: TransferAuthorityInstructionAccounts, - args: TransferAuthorityInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: TransferAuthorityInstructionAccounts, + args: TransferAuthorityInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = transferAuthorityStruct.serialize({ - instructionDiscriminator: transferAuthorityInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: true, - }, - ]; + const [data] = transferAuthorityStruct.serialize({ + instructionDiscriminator: transferAuthorityInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: true, + pubkey: accounts.merkleTree, + }, + { + isSigner: true, + isWritable: false, + pubkey: accounts.authority, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/instructions/verifyLeaf.ts b/account-compression/sdk/src/generated/instructions/verifyLeaf.ts index 6cabb7be33c..7c85532b5b9 100644 --- a/account-compression/sdk/src/generated/instructions/verifyLeaf.ts +++ b/account-compression/sdk/src/generated/instructions/verifyLeaf.ts @@ -14,9 +14,9 @@ import * as web3 from '@solana/web3.js'; * @category generated */ export type VerifyLeafInstructionArgs = { - root: number[] /* size: 32 */; - leaf: number[] /* size: 32 */; - index: number; + root: number[] /* size: 32 */; + leaf: number[] /* size: 32 */; + index: number; }; /** * @category Instructions @@ -24,17 +24,17 @@ export type VerifyLeafInstructionArgs = { * @category generated */ export const verifyLeafStruct = new beet.BeetArgsStruct< - VerifyLeafInstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } + VerifyLeafInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */; + } >( - [ - ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], - ['root', beet.uniformFixedSizeArray(beet.u8, 32)], - ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], - ['index', beet.u32], - ], - 'VerifyLeafInstructionArgs' + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ['index', beet.u32], + ], + 'VerifyLeafInstructionArgs' ); /** * Accounts required by the _verifyLeaf_ instruction @@ -45,13 +45,11 @@ export const verifyLeafStruct = new beet.BeetArgsStruct< * @category generated */ export type VerifyLeafInstructionAccounts = { - merkleTree: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; + merkleTree: web3.PublicKey; + anchorRemainingAccounts?: web3.AccountMeta[]; }; -export const verifyLeafInstructionDiscriminator = [ - 124, 220, 22, 223, 104, 10, 250, 224, -]; +export const verifyLeafInstructionDiscriminator = [124, 220, 22, 223, 104, 10, 250, 224]; /** * Creates a _VerifyLeaf_ instruction. @@ -64,32 +62,32 @@ export const verifyLeafInstructionDiscriminator = [ * @category generated */ export function createVerifyLeafInstruction( - accounts: VerifyLeafInstructionAccounts, - args: VerifyLeafInstructionArgs, - programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') + accounts: VerifyLeafInstructionAccounts, + args: VerifyLeafInstructionArgs, + programId = new web3.PublicKey('cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK') ) { - const [data] = verifyLeafStruct.serialize({ - instructionDiscriminator: verifyLeafInstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.merkleTree, - isWritable: false, - isSigner: false, - }, - ]; + const [data] = verifyLeafStruct.serialize({ + instructionDiscriminator: verifyLeafInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + isSigner: false, + isWritable: false, + pubkey: accounts.merkleTree, + }, + ]; - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc); + } } - } - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; + const ix = new web3.TransactionInstruction({ + data, + keys, + programId, + }); + return ix; } diff --git a/account-compression/sdk/src/generated/types/AccountCompressionEvent.ts b/account-compression/sdk/src/generated/types/AccountCompressionEvent.ts index 7912f428bde..d3910b41e7f 100644 --- a/account-compression/sdk/src/generated/types/AccountCompressionEvent.ts +++ b/account-compression/sdk/src/generated/types/AccountCompressionEvent.ts @@ -7,11 +7,8 @@ import * as beet from '@metaplex-foundation/beet'; +import { ApplicationDataEvent, applicationDataEventBeet } from './ApplicationDataEvent'; import { ChangeLogEvent, changeLogEventBeet } from './ChangeLogEvent'; -import { - ApplicationDataEvent, - applicationDataEventBeet, -} from './ApplicationDataEvent'; /** * This type is used to derive the {@link AccountCompressionEvent} type as well as the de/serializer. * However don't refer to it in your code but use the {@link AccountCompressionEvent} type instead. @@ -22,8 +19,8 @@ import { * @private */ export type AccountCompressionEventRecord = { - ChangeLog: { fields: [ChangeLogEvent] }; - ApplicationData: { fields: [ApplicationDataEvent] }; + ChangeLog: { fields: [ChangeLogEvent] }; + ApplicationData: { fields: [ApplicationDataEvent] }; }; /** @@ -37,40 +34,32 @@ export type AccountCompressionEventRecord = { * @category enums * @category generated */ -export type AccountCompressionEvent = - beet.DataEnumKeyAsKind; +export type AccountCompressionEvent = beet.DataEnumKeyAsKind; export const isAccountCompressionEventChangeLog = ( - x: AccountCompressionEvent -): x is AccountCompressionEvent & { __kind: 'ChangeLog' } => - x.__kind === 'ChangeLog'; + x: AccountCompressionEvent +): x is AccountCompressionEvent & { __kind: 'ChangeLog' } => x.__kind === 'ChangeLog'; export const isAccountCompressionEventApplicationData = ( - x: AccountCompressionEvent -): x is AccountCompressionEvent & { __kind: 'ApplicationData' } => - x.__kind === 'ApplicationData'; + x: AccountCompressionEvent +): x is AccountCompressionEvent & { __kind: 'ApplicationData' } => x.__kind === 'ApplicationData'; /** * @category userTypes * @category generated */ -export const accountCompressionEventBeet = - beet.dataEnum([ +export const accountCompressionEventBeet = beet.dataEnum([ [ - 'ChangeLog', - new beet.FixableBeetArgsStruct< - AccountCompressionEventRecord['ChangeLog'] - >( - [['fields', beet.tuple([changeLogEventBeet])]], - 'AccountCompressionEventRecord["ChangeLog"]' - ), + 'ChangeLog', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([changeLogEventBeet])]], + 'AccountCompressionEventRecord["ChangeLog"]' + ), ], [ - 'ApplicationData', - new beet.FixableBeetArgsStruct< - AccountCompressionEventRecord['ApplicationData'] - >( - [['fields', beet.tuple([applicationDataEventBeet])]], - 'AccountCompressionEventRecord["ApplicationData"]' - ), + 'ApplicationData', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([applicationDataEventBeet])]], + 'AccountCompressionEventRecord["ApplicationData"]' + ), ], - ]) as beet.FixableBeet; +]) as beet.FixableBeet; diff --git a/account-compression/sdk/src/generated/types/ApplicationDataEvent.ts b/account-compression/sdk/src/generated/types/ApplicationDataEvent.ts index 13f96e7e635..4721ffb25d9 100644 --- a/account-compression/sdk/src/generated/types/ApplicationDataEvent.ts +++ b/account-compression/sdk/src/generated/types/ApplicationDataEvent.ts @@ -7,10 +7,7 @@ import * as beet from '@metaplex-foundation/beet'; -import { - ApplicationDataEventV1, - applicationDataEventV1Beet, -} from './ApplicationDataEventV1'; +import { ApplicationDataEventV1, applicationDataEventV1Beet } from './ApplicationDataEventV1'; /** * This type is used to derive the {@link ApplicationDataEvent} type as well as the de/serializer. * However don't refer to it in your code but use the {@link ApplicationDataEvent} type instead. @@ -21,7 +18,7 @@ import { * @private */ export type ApplicationDataEventRecord = { - V1: { fields: [ApplicationDataEventV1] }; + V1: { fields: [ApplicationDataEventV1] }; }; /** @@ -35,24 +32,21 @@ export type ApplicationDataEventRecord = { * @category enums * @category generated */ -export type ApplicationDataEvent = - beet.DataEnumKeyAsKind; +export type ApplicationDataEvent = beet.DataEnumKeyAsKind; -export const isApplicationDataEventV1 = ( - x: ApplicationDataEvent -): x is ApplicationDataEvent & { __kind: 'V1' } => x.__kind === 'V1'; +export const isApplicationDataEventV1 = (x: ApplicationDataEvent): x is ApplicationDataEvent & { __kind: 'V1' } => + x.__kind === 'V1'; /** * @category userTypes * @category generated */ -export const applicationDataEventBeet = - beet.dataEnum([ +export const applicationDataEventBeet = beet.dataEnum([ [ - 'V1', - new beet.FixableBeetArgsStruct( - [['fields', beet.tuple([applicationDataEventV1Beet])]], - 'ApplicationDataEventRecord["V1"]' - ), + 'V1', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([applicationDataEventV1Beet])]], + 'ApplicationDataEventRecord["V1"]' + ), ], - ]) as beet.FixableBeet; +]) as beet.FixableBeet; diff --git a/account-compression/sdk/src/generated/types/ApplicationDataEventV1.ts b/account-compression/sdk/src/generated/types/ApplicationDataEventV1.ts index f14f9eb96ee..7f1aef1ee24 100644 --- a/account-compression/sdk/src/generated/types/ApplicationDataEventV1.ts +++ b/account-compression/sdk/src/generated/types/ApplicationDataEventV1.ts @@ -7,15 +7,14 @@ import * as beet from '@metaplex-foundation/beet'; export type ApplicationDataEventV1 = { - applicationData: Uint8Array; + applicationData: Uint8Array; }; /** * @category userTypes * @category generated */ -export const applicationDataEventV1Beet = - new beet.FixableBeetArgsStruct( +export const applicationDataEventV1Beet = new beet.FixableBeetArgsStruct( [['applicationData', beet.bytes]], 'ApplicationDataEventV1' - ); +); diff --git a/account-compression/sdk/src/generated/types/ChangeLogEvent.ts b/account-compression/sdk/src/generated/types/ChangeLogEvent.ts index c38dd9ee66d..9250577b2f0 100644 --- a/account-compression/sdk/src/generated/types/ChangeLogEvent.ts +++ b/account-compression/sdk/src/generated/types/ChangeLogEvent.ts @@ -18,7 +18,7 @@ import { ChangeLogEventV1, changeLogEventV1Beet } from './ChangeLogEventV1'; * @private */ export type ChangeLogEventRecord = { - V1: { fields: [ChangeLogEventV1] }; + V1: { fields: [ChangeLogEventV1] }; }; /** @@ -34,20 +34,18 @@ export type ChangeLogEventRecord = { */ export type ChangeLogEvent = beet.DataEnumKeyAsKind; -export const isChangeLogEventV1 = ( - x: ChangeLogEvent -): x is ChangeLogEvent & { __kind: 'V1' } => x.__kind === 'V1'; +export const isChangeLogEventV1 = (x: ChangeLogEvent): x is ChangeLogEvent & { __kind: 'V1' } => x.__kind === 'V1'; /** * @category userTypes * @category generated */ export const changeLogEventBeet = beet.dataEnum([ - [ - 'V1', - new beet.FixableBeetArgsStruct( - [['fields', beet.tuple([changeLogEventV1Beet])]], - 'ChangeLogEventRecord["V1"]' - ), - ], + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([changeLogEventV1Beet])]], + 'ChangeLogEventRecord["V1"]' + ), + ], ]) as beet.FixableBeet; diff --git a/account-compression/sdk/src/generated/types/ChangeLogEventV1.ts b/account-compression/sdk/src/generated/types/ChangeLogEventV1.ts index aad565d916a..8c242d3f7e0 100644 --- a/account-compression/sdk/src/generated/types/ChangeLogEventV1.ts +++ b/account-compression/sdk/src/generated/types/ChangeLogEventV1.ts @@ -5,29 +5,28 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from '@solana/web3.js'; import * as beet from '@metaplex-foundation/beet'; import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as web3 from '@solana/web3.js'; import { PathNode, pathNodeBeet } from './PathNode'; export type ChangeLogEventV1 = { - id: web3.PublicKey; - path: PathNode[]; - seq: beet.bignum; - index: number; + id: web3.PublicKey; + path: PathNode[]; + seq: beet.bignum; + index: number; }; /** * @category userTypes * @category generated */ -export const changeLogEventV1Beet = - new beet.FixableBeetArgsStruct( +export const changeLogEventV1Beet = new beet.FixableBeetArgsStruct( [ - ['id', beetSolana.publicKey], - ['path', beet.array(pathNodeBeet)], - ['seq', beet.u64], - ['index', beet.u32], + ['id', beetSolana.publicKey], + ['path', beet.array(pathNodeBeet)], + ['seq', beet.u64], + ['index', beet.u32], ], 'ChangeLogEventV1' - ); +); diff --git a/account-compression/sdk/src/generated/types/CompressionAccountType.ts b/account-compression/sdk/src/generated/types/CompressionAccountType.ts index 2ba385ebaf4..8ad34d172dd 100644 --- a/account-compression/sdk/src/generated/types/CompressionAccountType.ts +++ b/account-compression/sdk/src/generated/types/CompressionAccountType.ts @@ -11,14 +11,15 @@ import * as beet from '@metaplex-foundation/beet'; * @category generated */ export enum CompressionAccountType { - Uninitialized, - ConcurrentMerkleTree, + Uninitialized, + ConcurrentMerkleTree, } /** * @category userTypes * @category generated */ -export const compressionAccountTypeBeet = beet.fixedScalarEnum( - CompressionAccountType -) as beet.FixedSizeBeet; +export const compressionAccountTypeBeet = beet.fixedScalarEnum(CompressionAccountType) as beet.FixedSizeBeet< + CompressionAccountType, + CompressionAccountType +>; diff --git a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeader.ts b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeader.ts index 919611922f0..a17460b1249 100644 --- a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeader.ts +++ b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeader.ts @@ -7,28 +7,21 @@ import * as beet from '@metaplex-foundation/beet'; -import { - CompressionAccountType, - compressionAccountTypeBeet, -} from './CompressionAccountType'; -import { - ConcurrentMerkleTreeHeaderData, - concurrentMerkleTreeHeaderDataBeet, -} from './ConcurrentMerkleTreeHeaderData'; +import { CompressionAccountType, compressionAccountTypeBeet } from './CompressionAccountType'; +import { ConcurrentMerkleTreeHeaderData, concurrentMerkleTreeHeaderDataBeet } from './ConcurrentMerkleTreeHeaderData'; export type ConcurrentMerkleTreeHeader = { - accountType: CompressionAccountType; - header: ConcurrentMerkleTreeHeaderData; + accountType: CompressionAccountType; + header: ConcurrentMerkleTreeHeaderData; }; /** * @category userTypes * @category generated */ -export const concurrentMerkleTreeHeaderBeet = - new beet.FixableBeetArgsStruct( +export const concurrentMerkleTreeHeaderBeet = new beet.FixableBeetArgsStruct( [ - ['accountType', compressionAccountTypeBeet], - ['header', concurrentMerkleTreeHeaderDataBeet], + ['accountType', compressionAccountTypeBeet], + ['header', concurrentMerkleTreeHeaderDataBeet], ], 'ConcurrentMerkleTreeHeader' - ); +); diff --git a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderData.ts b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderData.ts index d1fdaee518b..ac24b84d189 100644 --- a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderData.ts +++ b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderData.ts @@ -8,8 +8,8 @@ import * as beet from '@metaplex-foundation/beet'; import { - ConcurrentMerkleTreeHeaderDataV1, - concurrentMerkleTreeHeaderDataV1Beet, + ConcurrentMerkleTreeHeaderDataV1, + concurrentMerkleTreeHeaderDataV1Beet, } from './ConcurrentMerkleTreeHeaderDataV1'; /** * This type is used to derive the {@link ConcurrentMerkleTreeHeaderData} type as well as the de/serializer. @@ -21,7 +21,7 @@ import { * @private */ export type ConcurrentMerkleTreeHeaderDataRecord = { - V1: { fields: [ConcurrentMerkleTreeHeaderDataV1] }; + V1: { fields: [ConcurrentMerkleTreeHeaderDataV1] }; }; /** @@ -35,29 +35,22 @@ export type ConcurrentMerkleTreeHeaderDataRecord = { * @category enums * @category generated */ -export type ConcurrentMerkleTreeHeaderData = - beet.DataEnumKeyAsKind; +export type ConcurrentMerkleTreeHeaderData = beet.DataEnumKeyAsKind; export const isConcurrentMerkleTreeHeaderDataV1 = ( - x: ConcurrentMerkleTreeHeaderData + x: ConcurrentMerkleTreeHeaderData ): x is ConcurrentMerkleTreeHeaderData & { __kind: 'V1' } => x.__kind === 'V1'; /** * @category userTypes * @category generated */ -export const concurrentMerkleTreeHeaderDataBeet = - beet.dataEnum([ +export const concurrentMerkleTreeHeaderDataBeet = beet.dataEnum([ [ - 'V1', - new beet.BeetArgsStruct( - [ - [ - 'fields', - beet.fixedSizeTuple([concurrentMerkleTreeHeaderDataV1Beet]), - ], - ], - 'ConcurrentMerkleTreeHeaderDataRecord["V1"]' - ), + 'V1', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([concurrentMerkleTreeHeaderDataV1Beet])]], + 'ConcurrentMerkleTreeHeaderDataRecord["V1"]' + ), ], - ]) as beet.FixableBeet; +]) as beet.FixableBeet; diff --git a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderDataV1.ts b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderDataV1.ts index aff0dd2c3e8..e75cbc3172e 100644 --- a/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderDataV1.ts +++ b/account-compression/sdk/src/generated/types/ConcurrentMerkleTreeHeaderDataV1.ts @@ -5,29 +5,28 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from '@solana/web3.js'; import * as beet from '@metaplex-foundation/beet'; import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as web3 from '@solana/web3.js'; export type ConcurrentMerkleTreeHeaderDataV1 = { - maxBufferSize: number; - maxDepth: number; - authority: web3.PublicKey; - creationSlot: beet.bignum; - padding: number[] /* size: 6 */; + maxBufferSize: number; + maxDepth: number; + authority: web3.PublicKey; + creationSlot: beet.bignum; + padding: number[] /* size: 6 */; }; /** * @category userTypes * @category generated */ -export const concurrentMerkleTreeHeaderDataV1Beet = - new beet.BeetArgsStruct( +export const concurrentMerkleTreeHeaderDataV1Beet = new beet.BeetArgsStruct( [ - ['maxBufferSize', beet.u32], - ['maxDepth', beet.u32], - ['authority', beetSolana.publicKey], - ['creationSlot', beet.u64], - ['padding', beet.uniformFixedSizeArray(beet.u8, 6)], + ['maxBufferSize', beet.u32], + ['maxDepth', beet.u32], + ['authority', beetSolana.publicKey], + ['creationSlot', beet.u64], + ['padding', beet.uniformFixedSizeArray(beet.u8, 6)], ], 'ConcurrentMerkleTreeHeaderDataV1' - ); +); diff --git a/account-compression/sdk/src/generated/types/PathNode.ts b/account-compression/sdk/src/generated/types/PathNode.ts index d6209d894b5..72eb77a2feb 100644 --- a/account-compression/sdk/src/generated/types/PathNode.ts +++ b/account-compression/sdk/src/generated/types/PathNode.ts @@ -7,8 +7,8 @@ import * as beet from '@metaplex-foundation/beet'; export type PathNode = { - node: number[] /* size: 32 */; - index: number; + node: number[] /* size: 32 */; + index: number; }; /** @@ -16,9 +16,9 @@ export type PathNode = { * @category generated */ export const pathNodeBeet = new beet.BeetArgsStruct( - [ - ['node', beet.uniformFixedSizeArray(beet.u8, 32)], - ['index', beet.u32], - ], - 'PathNode' + [ + ['node', beet.uniformFixedSizeArray(beet.u8, 32)], + ['index', beet.u32], + ], + 'PathNode' ); diff --git a/account-compression/sdk/src/index.ts b/account-compression/sdk/src/index.ts index d82cc7adc14..9ea2dfa9c7b 100644 --- a/account-compression/sdk/src/index.ts +++ b/account-compression/sdk/src/index.ts @@ -1,7 +1,7 @@ export * from './generated'; export { - PROGRAM_ADDRESS as SPL_ACCOUNT_COMPRESSION_ADDRESS, - PROGRAM_ID as SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + PROGRAM_ADDRESS as SPL_ACCOUNT_COMPRESSION_ADDRESS, + PROGRAM_ID as SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, } from './generated'; export * from './instructions'; export * from './accounts'; diff --git a/account-compression/sdk/src/instructions/index.ts b/account-compression/sdk/src/instructions/index.ts index 2bdcf87a783..a2ccb9dc74e 100644 --- a/account-compression/sdk/src/instructions/index.ts +++ b/account-compression/sdk/src/instructions/index.ts @@ -1,20 +1,15 @@ -import { - Connection, - PublicKey, - SystemProgram, - TransactionInstruction, -} from '@solana/web3.js'; +import { Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; -import { SPL_NOOP_PROGRAM_ID, ValidDepthSizePair } from '../constants'; import { getConcurrentMerkleTreeAccountSize } from '../accounts'; +import { SPL_NOOP_PROGRAM_ID, ValidDepthSizePair } from '../constants'; import { - createReplaceLeafInstruction, - createAppendInstruction, - createTransferAuthorityInstruction, - createVerifyLeafInstruction, - PROGRAM_ID, - createInitEmptyMerkleTreeInstruction, - createCloseEmptyTreeInstruction, + createAppendInstruction, + createCloseEmptyTreeInstruction, + createInitEmptyMerkleTreeInstruction, + createReplaceLeafInstruction, + createTransferAuthorityInstruction, + createVerifyLeafInstruction, + PROGRAM_ID, } from '../generated'; import { MerkleTreeProof } from '../merkle-tree'; @@ -22,20 +17,17 @@ import { MerkleTreeProof } from '../merkle-tree'; * Helper function that adds proof nodes to a TransactionInstruction * by adding extra keys to the transaction */ -export function addProof( - instruction: TransactionInstruction, - nodeProof: Buffer[] -): TransactionInstruction { - instruction.keys = instruction.keys.concat( - nodeProof.map((node) => { - return { - pubkey: new PublicKey(node), - isSigner: false, - isWritable: false, - }; - }) - ); - return instruction; +export function addProof(instruction: TransactionInstruction, nodeProof: Buffer[]): TransactionInstruction { + instruction.keys = instruction.keys.concat( + nodeProof.map(node => { + return { + isSigner: false, + isWritable: false, + pubkey: new PublicKey(node), + }; + }) + ); + return instruction; } /** @@ -47,18 +39,18 @@ export function addProof( * @returns */ export function createInitEmptyMerkleTreeIx( - merkleTree: PublicKey, - authority: PublicKey, - depthSizePair: ValidDepthSizePair + merkleTree: PublicKey, + authority: PublicKey, + depthSizePair: ValidDepthSizePair ): TransactionInstruction { - return createInitEmptyMerkleTreeInstruction( - { - merkleTree, - authority: authority, - noop: SPL_NOOP_PROGRAM_ID, - }, - depthSizePair - ); + return createInitEmptyMerkleTreeInstruction( + { + authority: authority, + merkleTree, + noop: SPL_NOOP_PROGRAM_ID, + }, + depthSizePair + ); } /** @@ -70,27 +62,27 @@ export function createInitEmptyMerkleTreeIx( * @returns */ export function createReplaceIx( - merkleTree: PublicKey, - authority: PublicKey, - newLeaf: Buffer, - proof: MerkleTreeProof + merkleTree: PublicKey, + authority: PublicKey, + newLeaf: Buffer, + proof: MerkleTreeProof ): TransactionInstruction { - return addProof( - createReplaceLeafInstruction( - { - merkleTree, - authority: authority, - noop: SPL_NOOP_PROGRAM_ID, - }, - { - root: Array.from(proof.root), - previousLeaf: Array.from(proof.leaf), - newLeaf: Array.from(newLeaf), - index: proof.leafIndex, - } - ), - proof.proof - ); + return addProof( + createReplaceLeafInstruction( + { + authority: authority, + merkleTree, + noop: SPL_NOOP_PROGRAM_ID, + }, + { + index: proof.leafIndex, + newLeaf: Array.from(newLeaf), + previousLeaf: Array.from(proof.leaf), + root: Array.from(proof.root), + } + ), + proof.proof + ); } /** @@ -101,20 +93,20 @@ export function createReplaceIx( * @returns */ export function createAppendIx( - merkleTree: PublicKey, - authority: PublicKey, - newLeaf: Buffer | ArrayLike + merkleTree: PublicKey, + authority: PublicKey, + newLeaf: Buffer | ArrayLike ): TransactionInstruction { - return createAppendInstruction( - { - merkleTree, - authority: authority, - noop: SPL_NOOP_PROGRAM_ID, - }, - { - leaf: Array.from(newLeaf), - } - ); + return createAppendInstruction( + { + authority: authority, + merkleTree, + noop: SPL_NOOP_PROGRAM_ID, + }, + { + leaf: Array.from(newLeaf), + } + ); } /** @@ -125,19 +117,19 @@ export function createAppendIx( * @returns */ export function createTransferAuthorityIx( - merkleTree: PublicKey, - authority: PublicKey, - newAuthority: PublicKey + merkleTree: PublicKey, + authority: PublicKey, + newAuthority: PublicKey ): TransactionInstruction { - return createTransferAuthorityInstruction( - { - merkleTree, - authority: authority, - }, - { - newAuthority, - } - ); + return createTransferAuthorityInstruction( + { + authority: authority, + merkleTree, + }, + { + newAuthority, + } + ); } /** @@ -146,23 +138,20 @@ export function createTransferAuthorityIx( * @param proof * @returns */ -export function createVerifyLeafIx( - merkleTree: PublicKey, - proof: MerkleTreeProof -): TransactionInstruction { - return addProof( - createVerifyLeafInstruction( - { - merkleTree, - }, - { - root: Array.from(proof.root), - leaf: Array.from(proof.leaf), - index: proof.leafIndex, - } - ), - proof.proof - ); +export function createVerifyLeafIx(merkleTree: PublicKey, proof: MerkleTreeProof): TransactionInstruction { + return addProof( + createVerifyLeafInstruction( + { + merkleTree, + }, + { + index: proof.leafIndex, + leaf: Array.from(proof.leaf), + root: Array.from(proof.root), + } + ), + proof.proof + ); } /** @@ -178,24 +167,24 @@ export function createVerifyLeafIx( * @returns */ export async function createAllocTreeIx( - connection: Connection, - merkleTree: PublicKey, - payer: PublicKey, - depthSizePair: ValidDepthSizePair, - canopyDepth: number + connection: Connection, + merkleTree: PublicKey, + payer: PublicKey, + depthSizePair: ValidDepthSizePair, + canopyDepth: number ): Promise { - const requiredSpace = getConcurrentMerkleTreeAccountSize( - depthSizePair.maxDepth, - depthSizePair.maxBufferSize, - canopyDepth ?? 0 - ); - return SystemProgram.createAccount({ - fromPubkey: payer, - newAccountPubkey: merkleTree, - lamports: await connection.getMinimumBalanceForRentExemption(requiredSpace), - space: requiredSpace, - programId: PROGRAM_ID, - }); + const requiredSpace = getConcurrentMerkleTreeAccountSize( + depthSizePair.maxDepth, + depthSizePair.maxBufferSize, + canopyDepth ?? 0 + ); + return SystemProgram.createAccount({ + fromPubkey: payer, + lamports: await connection.getMinimumBalanceForRentExemption(requiredSpace), + newAccountPubkey: merkleTree, + programId: PROGRAM_ID, + space: requiredSpace, + }); } /** @@ -206,13 +195,13 @@ export async function createAllocTreeIx( * @returns */ export function createCloseEmptyTreeIx( - merkleTree: PublicKey, - authority: PublicKey, - recipient: PublicKey + merkleTree: PublicKey, + authority: PublicKey, + recipient: PublicKey ): TransactionInstruction { - return createCloseEmptyTreeInstruction({ - merkleTree, - authority, - recipient, - }); + return createCloseEmptyTreeInstruction({ + authority, + merkleTree, + recipient, + }); } diff --git a/account-compression/sdk/src/merkle-tree/index.ts b/account-compression/sdk/src/merkle-tree/index.ts index 928e13206e8..fc95fd1a26c 100644 --- a/account-compression/sdk/src/merkle-tree/index.ts +++ b/account-compression/sdk/src/merkle-tree/index.ts @@ -1,219 +1,203 @@ +import { PublicKey } from '@solana/web3.js'; import pkg from 'js-sha3'; import * as Collections from 'typescript-collections'; -import { PublicKey } from '@solana/web3.js'; const { keccak_256 } = pkg; -let CACHE_EMPTY_NODE = new Map(); -export const LEAF_BUFFER_LENGTH: number = 32; +const CACHE_EMPTY_NODE = new Map(); +export const LEAF_BUFFER_LENGTH = 32; export type MerkleTreeProof = { - leafIndex: number; - leaf: Buffer; - proof: Buffer[]; - root: Buffer; + leafIndex: number; + leaf: Buffer; + proof: Buffer[]; + root: Buffer; }; export class MerkleTree { - leaves: TreeNode[]; - root: Buffer; - depth: number; + leaves: TreeNode[]; + root: Buffer; + depth: number; - /** - * Please use `MerkleTree.sparseMerkleTreeFromLeaves` to - * create trees instead. This method is exposed for testing purposes, - * and for those that are familiar with the MerkleTree data structure. - * @param leaves leaf nodes of the tree - */ - constructor(leaves: Buffer[]) { - let [nodes, finalLeaves] = buildLeaves(leaves); - let seqNum = leaves.length; + /** + * Please use `MerkleTree.sparseMerkleTreeFromLeaves` to + * create trees instead. This method is exposed for testing purposes, + * and for those that are familiar with the MerkleTree data structure. + * @param leaves leaf nodes of the tree + */ + constructor(leaves: Buffer[]) { + const [nodes, finalLeaves] = buildLeaves(leaves); + let seqNum = leaves.length; - while (nodes.size() > 1) { - let left = nodes.dequeue()!; - const level = left.level; + while (nodes.size() > 1) { + const left = nodes.dequeue()!; + const level = left.level; - let right: TreeNode; - if (level != nodes.peek()!.level) { - right = emptyTreeNode(level, seqNum); - seqNum++; - } else { - right = nodes.dequeue()!; - } + let right: TreeNode; + if (level != nodes.peek()!.level) { + right = emptyTreeNode(level, seqNum); + seqNum++; + } else { + right = nodes.dequeue()!; + } - let parent: TreeNode = { - node: hash(left.node, right.node), - left: left, - right: right, - parent: undefined, - level: level + 1, - id: seqNum, - }; - left.parent = parent; - right.parent = parent; - nodes.enqueue(parent); - seqNum++; + const parent: TreeNode = { + id: seqNum, + left: left, + level: level + 1, + node: hash(left.node, right.node), + parent: undefined, + right: right, + }; + left.parent = parent; + right.parent = parent; + nodes.enqueue(parent); + seqNum++; + } + + this.leaves = finalLeaves; + this.root = nodes.peek()!.node; + this.depth = nodes.peek()!.level + 1; } - this.leaves = finalLeaves; - this.root = nodes.peek()!.node; - this.depth = nodes.peek()!.level + 1; - } + /** + * This is the recommended way to create MerkleTrees. + * If you're trying to match an on-chain MerkleTree, + * set `depth` to `{@link ConcurrentMerkleTreeAccount}.getMaxDepth()` + * + * @param leaves leaves of the tree + * @param depth number of levels in the tree + * @returns MerkleTree + */ + static sparseMerkleTreeFromLeaves(leaves: Buffer[], depth: number): MerkleTree { + const _leaves: Buffer[] = []; + for (let i = 0; i < 2 ** depth; i++) { + if (i < leaves.length) { + _leaves.push(leaves[i]); + } else { + _leaves.push(Buffer.alloc(32)); + } + } + return new MerkleTree(_leaves); + } - /** - * This is the recommended way to create MerkleTrees. - * If you're trying to match an on-chain MerkleTree, - * set `depth` to `{@link ConcurrentMerkleTreeAccount}.getMaxDepth()` - * - * @param leaves leaves of the tree - * @param depth number of levels in the tree - * @returns MerkleTree - */ - static sparseMerkleTreeFromLeaves( - leaves: Buffer[], - depth: number - ): MerkleTree { - const _leaves: Buffer[] = []; - for (let i = 0; i < 2 ** depth; i++) { - if (i < leaves.length) { - _leaves.push(leaves[i]); - } else { - _leaves.push(Buffer.alloc(32)); - } + getRoot(): Buffer { + return this.root; } - return new MerkleTree(_leaves); - } - getRoot(): Buffer { - return this.root; - } + getProof(leafIndex: number, minimizeProofHeight = false, treeHeight = -1, verbose = false): MerkleTreeProof { + const proof: TreeNode[] = []; - getProof( - leafIndex: number, - minimizeProofHeight: boolean = false, - treeHeight: number = -1, - verbose = false - ): MerkleTreeProof { - let proof: TreeNode[] = []; + let node = this.leaves[leafIndex]; - let node = this.leaves[leafIndex]; + let height = 0; + while (typeof node.parent !== 'undefined') { + if (minimizeProofHeight && height >= treeHeight) { + break; + } + if (verbose) { + console.log(`${node.level}: ${Uint8Array.from(node.node)}`); + } + const parent = node.parent; + if (parent.left!.id === node.id) { + proof.push(parent.right!); - let height = 0; - while (typeof node.parent !== 'undefined') { - if (minimizeProofHeight && height >= treeHeight) { - break; - } - if (verbose) { - console.log(`${node.level}: ${Uint8Array.from(node.node)}`); - } - let parent = node.parent; - if (parent.left!.id === node.id) { - proof.push(parent.right!); + const hashed = hash(node.node, parent.right!.node); + if (!hashed.equals(parent.node)) { + console.log(hashed); + console.log(parent.node); + throw new Error('Invariant broken when hashing left node'); + } + } else { + proof.push(parent.left!); - const hashed = hash(node.node, parent.right!.node); - if (!hashed.equals(parent.node)) { - console.log(hashed); - console.log(parent.node); - throw new Error('Invariant broken when hashing left node'); + const hashed = hash(parent.left!.node, node.node); + if (!hashed.equals(parent.node)) { + console.log(hashed); + console.log(parent.node); + throw new Error('Invariant broken when hashing right node'); + } + } + node = parent; + height++; } - } else { - proof.push(parent.left!); - const hashed = hash(parent.left!.node, node.node); - if (!hashed.equals(parent.node)) { - console.log(hashed); - console.log(parent.node); - throw new Error('Invariant broken when hashing right node'); - } - } - node = parent; - height++; + return { + leaf: this.leaves[leafIndex].node, + leafIndex, + proof: proof.map(treeNode => treeNode.node), + root: this.getRoot(), + }; } - return { - leafIndex, - leaf: this.leaves[leafIndex].node, - root: this.getRoot(), - proof: proof.map((treeNode) => treeNode.node), - }; - } - - updateLeaf(leafIndex: number, newLeaf: Buffer, verbose = false) { - let leaf = this.leaves[leafIndex]; - leaf.node = newLeaf; - let node = leaf; + updateLeaf(leafIndex: number, newLeaf: Buffer, verbose = false) { + const leaf = this.leaves[leafIndex]; + leaf.node = newLeaf; + let node = leaf; - var i = 0; - while (typeof node.parent !== 'undefined') { - if (verbose) { - console.log(`${i}: ${Uint8Array.from(node.node)}`); - } - node = node.parent; - node.node = hash(node.left!.node, node.right!.node); - i++; - } - if (verbose) { - console.log(`${i}: ${Uint8Array.from(node.node)}`); + let i = 0; + while (typeof node.parent !== 'undefined') { + if (verbose) { + console.log(`${i}: ${Uint8Array.from(node.node)}`); + } + node = node.parent; + node.node = hash(node.left!.node, node.right!.node); + i++; + } + if (verbose) { + console.log(`${i}: ${Uint8Array.from(node.node)}`); + } + this.root = node.node; } - this.root = node.node; - } - static hashProof( - merkleTreeProof: MerkleTreeProof, - verbose: boolean = false - ): Buffer { - const { leaf, leafIndex, proof } = merkleTreeProof; + static hashProof(merkleTreeProof: MerkleTreeProof, verbose = false): Buffer { + const { leaf, leafIndex, proof } = merkleTreeProof; - let node = new PublicKey(leaf).toBuffer(); - for (let i = 0; i < proof.length; i++) { - if ((leafIndex >> i) % 2 === 0) { - node = hash(node, new PublicKey(proof[i]).toBuffer()); - } else { - node = hash(new PublicKey(proof[i]).toBuffer(), node); - } - if (verbose) console.log(`node ${i} ${new PublicKey(node).toString()}`); + let node = new PublicKey(leaf).toBuffer(); + for (let i = 0; i < proof.length; i++) { + if ((leafIndex >> i) % 2 === 0) { + node = hash(node, new PublicKey(proof[i]).toBuffer()); + } else { + node = hash(new PublicKey(proof[i]).toBuffer(), node); + } + if (verbose) console.log(`node ${i} ${new PublicKey(node).toString()}`); + } + return node; } - return node; - } - /** - * Verifies that a root matches the proof. - * @param root Root of a MerkleTree - * @param merkleTreeProof Proof to a leaf in the MerkleTree - * @param verbose Whether to print hashed nodes - * @returns Whether the proof is valid - */ - static verify( - root: Buffer, - merkleTreeProof: MerkleTreeProof, - verbose: boolean = false - ): boolean { - const node = MerkleTree.hashProof(merkleTreeProof, verbose); - const rehashed = new PublicKey(node).toString(); - const received = new PublicKey(root).toString(); - if (rehashed !== received) { - if (verbose) - console.log(`Roots don't match! Expected ${rehashed} got ${received}`); - return false; + /** + * Verifies that a root matches the proof. + * @param root Root of a MerkleTree + * @param merkleTreeProof Proof to a leaf in the MerkleTree + * @param verbose Whether to print hashed nodes + * @returns Whether the proof is valid + */ + static verify(root: Buffer, merkleTreeProof: MerkleTreeProof, verbose = false): boolean { + const node = MerkleTree.hashProof(merkleTreeProof, verbose); + const rehashed = new PublicKey(node).toString(); + const received = new PublicKey(root).toString(); + if (rehashed !== received) { + if (verbose) console.log(`Roots don't match! Expected ${rehashed} got ${received}`); + return false; + } + if (verbose) console.log(`Hashed ${rehashed} got ${received}`); + return rehashed === received; } - if (verbose) console.log(`Hashed ${rehashed} got ${received}`); - return rehashed === received; - } } export type TreeNode = { - node: Buffer; - left: TreeNode | undefined; - right: TreeNode | undefined; - parent: TreeNode | undefined; - level: number; - id: number; + node: Buffer; + left: TreeNode | undefined; + right: TreeNode | undefined; + parent: TreeNode | undefined; + level: number; + id: number; }; /** * Uses on-chain hash fn to hash together buffers */ export function hash(left: Buffer, right: Buffer): Buffer { - return Buffer.from(keccak_256.digest(Buffer.concat([left, right]))); + return Buffer.from(keccak_256.digest(Buffer.concat([left, right]))); } /* @@ -248,16 +232,16 @@ export function hash(left: Buffer, right: Buffer): Buffer { * @returns */ export function emptyNode(level: number): Buffer { - if (CACHE_EMPTY_NODE.has(level)) { - return CACHE_EMPTY_NODE.get(level)!; - } - if (level == 0) { - return Buffer.alloc(32); - } + if (CACHE_EMPTY_NODE.has(level)) { + return CACHE_EMPTY_NODE.get(level)!; + } + if (level == 0) { + return Buffer.alloc(32); + } - let result = hash(emptyNode(level - 1), emptyNode(level - 1)); - CACHE_EMPTY_NODE.set(level, result); - return result; + const result = hash(emptyNode(level - 1), emptyNode(level - 1)); + CACHE_EMPTY_NODE.set(level, result); + return result; } /** @@ -267,14 +251,14 @@ export function emptyNode(level: number): Buffer { * @returns */ function emptyTreeNode(level: number, id: number): TreeNode { - return { - node: emptyNode(level), - left: undefined, - right: undefined, - parent: undefined, - level: level, - id, - }; + return { + id, + left: undefined, + level: level, + node: emptyNode(level), + parent: undefined, + right: undefined, + }; } /** @@ -282,28 +266,26 @@ function emptyTreeNode(level: number, id: number): TreeNode { * @param leaves * @returns */ -function buildLeaves( - leaves: Buffer[] -): [Collections.Queue, TreeNode[]] { - let nodes = new Collections.Queue(); - let finalLeaves: TreeNode[] = []; - leaves.forEach((buffer, index) => { - if (buffer.length != LEAF_BUFFER_LENGTH) { - throw Error( - `Provided leaf has length: ${buffer.length}, but we need all leaves to be length ${LEAF_BUFFER_LENGTH}` - ); - } +function buildLeaves(leaves: Buffer[]): [Collections.Queue, TreeNode[]] { + const nodes = new Collections.Queue(); + const finalLeaves: TreeNode[] = []; + leaves.forEach((buffer, index) => { + if (buffer.length != LEAF_BUFFER_LENGTH) { + throw Error( + `Provided leaf has length: ${buffer.length}, but we need all leaves to be length ${LEAF_BUFFER_LENGTH}` + ); + } - const treeNode = { - node: buffer, - left: undefined, - right: undefined, - parent: undefined, - level: 0, - id: index, - }; - nodes.enqueue(treeNode); - finalLeaves.push(treeNode); - }); - return [nodes, finalLeaves]; + const treeNode = { + id: index, + left: undefined, + level: 0, + node: buffer, + parent: undefined, + right: undefined, + }; + nodes.enqueue(treeNode); + finalLeaves.push(treeNode); + }); + return [nodes, finalLeaves]; } diff --git a/account-compression/sdk/src/types/Canopy.ts b/account-compression/sdk/src/types/Canopy.ts index e02d30fb18a..fcd4c7fde41 100644 --- a/account-compression/sdk/src/types/Canopy.ts +++ b/account-compression/sdk/src/types/Canopy.ts @@ -5,7 +5,7 @@ import * as beet from '@metaplex-foundation/beet'; * for a {@link ConcurrentMerkleTreeAccount} */ export type Canopy = { - canopyBytes: number[]; + canopyBytes: number[]; }; /** @@ -24,16 +24,8 @@ export type Canopy = { * @returns */ export const canopyBeetFactory = (canopyDepth: number) => { - return new beet.BeetArgsStruct( - [ - [ - 'canopyBytes', - beet.uniformFixedSizeArray( - beet.u8, - Math.max(((1 << (canopyDepth + 1)) - 2) * 32, 0) - ), - ], - ], - 'Canopy' - ); + return new beet.BeetArgsStruct( + [['canopyBytes', beet.uniformFixedSizeArray(beet.u8, Math.max(((1 << (canopyDepth + 1)) - 2) * 32, 0))]], + 'Canopy' + ); }; diff --git a/account-compression/sdk/src/types/ConcurrentMerkleTree.ts b/account-compression/sdk/src/types/ConcurrentMerkleTree.ts index 4310d298998..d29ab258428 100644 --- a/account-compression/sdk/src/types/ConcurrentMerkleTree.ts +++ b/account-compression/sdk/src/types/ConcurrentMerkleTree.ts @@ -1,6 +1,6 @@ import * as beet from '@metaplex-foundation/beet'; -import { PublicKey } from '@solana/web3.js'; import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { PublicKey } from '@solana/web3.js'; import { Path, pathBeetFactory } from './Path'; @@ -9,33 +9,33 @@ import { Path, pathBeetFactory } from './Path'; * @private */ export type ChangeLogInternal = { - root: PublicKey; - pathNodes: PublicKey[]; - index: number; // u32 - _padding: number; // u32 + root: PublicKey; + pathNodes: PublicKey[]; + index: number; // u32 + _padding: number; // u32 }; const changeLogBeetFactory = (maxDepth: number) => { - return new beet.BeetArgsStruct( - [ - ['root', beetSolana.publicKey], - ['pathNodes', beet.uniformFixedSizeArray(beetSolana.publicKey, maxDepth)], - ['index', beet.u32], - ['_padding', beet.u32], - ], - 'ChangeLog' - ); + return new beet.BeetArgsStruct( + [ + ['root', beetSolana.publicKey], + ['pathNodes', beet.uniformFixedSizeArray(beetSolana.publicKey, maxDepth)], + ['index', beet.u32], + ['_padding', beet.u32], + ], + 'ChangeLog' + ); }; /** * ConcurrentMerkleTree fields necessary for deserializing an on-chain ConcurrentMerkleTree */ export type ConcurrentMerkleTree = { - sequenceNumber: beet.bignum; // u64 - activeIndex: beet.bignum; // u64 - bufferSize: beet.bignum; // u64 - changeLogs: ChangeLogInternal[]; - rightMostPath: Path; + sequenceNumber: beet.bignum; // u64 + activeIndex: beet.bignum; // u64 + bufferSize: beet.bignum; // u64 + changeLogs: ChangeLogInternal[]; + rightMostPath: Path; }; /** @@ -46,24 +46,15 @@ export type ConcurrentMerkleTree = { * @param maxBufferSize * @returns */ -export const concurrentMerkleTreeBeetFactory = ( - maxDepth: number, - maxBufferSize: number -) => { - return new beet.BeetArgsStruct( - [ - ['sequenceNumber', beet.u64], - ['activeIndex', beet.u64], - ['bufferSize', beet.u64], - [ - 'changeLogs', - beet.uniformFixedSizeArray( - changeLogBeetFactory(maxDepth), - maxBufferSize - ), - ], - ['rightMostPath', pathBeetFactory(maxDepth)], - ], - 'ConcurrentMerkleTree' - ); +export const concurrentMerkleTreeBeetFactory = (maxDepth: number, maxBufferSize: number) => { + return new beet.BeetArgsStruct( + [ + ['sequenceNumber', beet.u64], + ['activeIndex', beet.u64], + ['bufferSize', beet.u64], + ['changeLogs', beet.uniformFixedSizeArray(changeLogBeetFactory(maxDepth), maxBufferSize)], + ['rightMostPath', pathBeetFactory(maxDepth)], + ], + 'ConcurrentMerkleTree' + ); }; diff --git a/account-compression/sdk/src/types/Path.ts b/account-compression/sdk/src/types/Path.ts index cb709cca838..33c373695bb 100644 --- a/account-compression/sdk/src/types/Path.ts +++ b/account-compression/sdk/src/types/Path.ts @@ -1,16 +1,16 @@ -import { PublicKey } from '@solana/web3.js'; -import * as beetSolana from '@metaplex-foundation/beet-solana'; import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { PublicKey } from '@solana/web3.js'; /** * Canopy fields necessary for deserializing an on-chain Path * used in an {@link ConcurrentMerkleTree} */ export type Path = { - proof: PublicKey[]; - leaf: PublicKey; - index: number; // u32 - _padding: number; // u32 + proof: PublicKey[]; + leaf: PublicKey; + index: number; // u32 + _padding: number; // u32 }; /** @@ -21,13 +21,13 @@ export type Path = { * @returns */ export const pathBeetFactory = (maxDepth: number) => { - return new beet.BeetArgsStruct( - [ - ['proof', beet.uniformFixedSizeArray(beetSolana.publicKey, maxDepth)], - ['leaf', beetSolana.publicKey], - ['index', beet.u32], - ['_padding', beet.u32], - ], - 'Path' - ); + return new beet.BeetArgsStruct( + [ + ['proof', beet.uniformFixedSizeArray(beetSolana.publicKey, maxDepth)], + ['leaf', beetSolana.publicKey], + ['index', beet.u32], + ['_padding', beet.u32], + ], + 'Path' + ); }; diff --git a/account-compression/sdk/src/types/index.ts b/account-compression/sdk/src/types/index.ts index 5b89ef10634..2f03308c59f 100644 --- a/account-compression/sdk/src/types/index.ts +++ b/account-compression/sdk/src/types/index.ts @@ -7,8 +7,8 @@ export * from './Canopy'; export * from './ConcurrentMerkleTree'; export type ChangeLogEventV1 = { - treeId: PublicKey; - path: PathNode[]; - seq: BN; - index: number; + treeId: PublicKey; + path: PathNode[]; + seq: BN; + index: number; }; diff --git a/account-compression/sdk/tests/accountCompression.test.ts b/account-compression/sdk/tests/accountCompression.test.ts index 9b832023794..86d741e63a4 100644 --- a/account-compression/sdk/tests/accountCompression.test.ts +++ b/account-compression/sdk/tests/accountCompression.test.ts @@ -1,621 +1,508 @@ -import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet'; -import { BN } from 'bn.js'; import { AnchorProvider } from '@project-serum/anchor'; +import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet'; import { Connection, Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { BN } from 'bn.js'; import { assert } from 'chai'; -import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'; import * as crypto from 'crypto'; -import { createTreeOnChain, execute } from './utils'; -import { - hash, - MerkleTree, -} from '../src/merkle-tree'; import { - createReplaceIx, - createAppendIx, - createTransferAuthorityIx, - createVerifyLeafIx, - ConcurrentMerkleTreeAccount, - createCloseEmptyTreeInstruction, - ValidDepthSizePair, + ConcurrentMerkleTreeAccount, + createAppendIx, + createCloseEmptyTreeInstruction, + createReplaceIx, + createTransferAuthorityIx, + createVerifyLeafIx, + ValidDepthSizePair, } from '../src'; +import { hash, MerkleTree } from '../src/merkle-tree'; +import { createTreeOnChain, execute } from './utils'; +// eslint-disable-next-line no-empty describe('Account Compression', () => { - // Configure the client to use the local cluster. - let offChainTree: MerkleTree; - let cmtKeypair: Keypair; - let cmt: PublicKey; - let payerKeypair: Keypair; - let payer: PublicKey; - let connection: Connection; - let provider: AnchorProvider; - - const MAX_SIZE = 64; - const MAX_DEPTH = 14; - const DEPTH_SIZE_PAIR: ValidDepthSizePair = { - maxBufferSize: MAX_SIZE, - maxDepth: MAX_DEPTH - }; - - beforeEach(async () => { - payerKeypair = Keypair.generate(); - payer = payerKeypair.publicKey; - connection = new Connection('http://localhost:8899', { - commitment: 'confirmed', - }); - const wallet = new NodeWallet(payerKeypair); - provider = new AnchorProvider(connection, wallet, { - commitment: connection.commitment, - skipPreflight: true, - }); + // Configure the client to use the local cluster. + let offChainTree: MerkleTree; + let cmtKeypair: Keypair; + let cmt: PublicKey; + let payerKeypair: Keypair; + let payer: PublicKey; + let connection: Connection; + let provider: AnchorProvider; + + const MAX_SIZE = 64; + const MAX_DEPTH = 14; + const DEPTH_SIZE_PAIR: ValidDepthSizePair = { + maxBufferSize: MAX_SIZE, + maxDepth: MAX_DEPTH, + }; - await provider.connection.confirmTransaction( - await provider.connection.requestAirdrop(payer, 1e10), - 'confirmed' - ); - }); - - describe('Having created a tree with a single leaf', () => { beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 1, - DEPTH_SIZE_PAIR - ); - cmt = cmtKeypair.publicKey; - }); - it('Append single leaf', async () => { - const newLeaf = crypto.randomBytes(32); - const appendIx = createAppendIx(cmt, payer, newLeaf); - - await execute(provider, [appendIx], [payerKeypair]); - offChainTree.updateLeaf(1, newLeaf); - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' - ); - }); - it('Verify proof works for that leaf', async () => { - const newLeaf = crypto.randomBytes(32); - const index = 0; - const proof = offChainTree.getProof(index); - - const verifyLeafIx = createVerifyLeafIx(cmt, proof); - const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, proof); - await execute(provider, [verifyLeafIx, replaceLeafIx], [payerKeypair]); - - offChainTree.updateLeaf(index, newLeaf); - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' - ); - }); - it('Verify leaf fails when proof fails', async () => { - const newLeaf = crypto.randomBytes(32); - const index = 0; - // Replace valid proof with random bytes so it is wrong - const proof = offChainTree.getProof(index); - proof.proof = proof.proof.map((_) => { - return crypto.randomBytes(32); - }); - - // Verify proof is invalid - const verifyLeafIx = createVerifyLeafIx(cmt, proof); - try { - await execute(provider, [verifyLeafIx], [payerKeypair]); - assert(false, 'Proof should have failed to verify'); - } catch { } - - // Replace instruction with same proof fails - const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, proof); - try { - await execute(provider, [replaceLeafIx], [payerKeypair]); - assert(false, 'Replace should have failed to verify'); - } catch { } - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmtKeypair.publicKey - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' - ); - }); - it('Replace that leaf', async () => { - const newLeaf = crypto.randomBytes(32); - const index = 0; - - const replaceLeafIx = createReplaceIx( - cmt, - payer, - newLeaf, - offChainTree.getProof(index, false, -1), - ); - assert( - replaceLeafIx.keys.length == 3 + MAX_DEPTH, - `Failed to create proof for ${MAX_DEPTH}` - ); - - await execute(provider, [replaceLeafIx], [payerKeypair]); - - offChainTree.updateLeaf(index, newLeaf); - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' - ); - }); - - it('Replace that leaf with a minimal proof', async () => { - const newLeaf = crypto.randomBytes(32); - const index = 0; - - const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, offChainTree.getProof(index, true, 1)); - assert( - replaceLeafIx.keys.length == 3 + 1, - 'Failed to minimize proof to expected size of 1' - ); - await execute(provider, [replaceLeafIx], [payerKeypair]); - - offChainTree.updateLeaf(index, newLeaf); - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' - ); - }); - }); - - describe('Examples transferring authority', () => { - const authorityKeypair = Keypair.generate(); - const authority = authorityKeypair.publicKey; - const randomSignerKeypair = Keypair.generate(); - const randomSigner = randomSignerKeypair.publicKey; - - beforeEach(async () => { - await provider.connection.confirmTransaction( - await (connection as Connection).requestAirdrop( - authority, - 1e10 - ) - ); - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - authorityKeypair, - 1, - DEPTH_SIZE_PAIR - ); - cmt = cmtKeypair.publicKey; - }); - it('Attempting to replace with random authority fails', async () => { - const newLeaf = crypto.randomBytes(32); - const replaceIndex = 0; - const proof = offChainTree.getProof(replaceIndex); - const replaceIx = createReplaceIx(cmt, randomSigner, newLeaf, proof); - - try { - await execute(provider, [replaceIx], [randomSignerKeypair]); - assert( - false, - 'Transaction should have failed since incorrect authority cannot execute replaces' + payerKeypair = Keypair.generate(); + payer = payerKeypair.publicKey; + connection = new Connection('http://127.0.0.1:8899', { + commitment: 'confirmed', + }); + const wallet = new NodeWallet(payerKeypair); + provider = new AnchorProvider(connection, wallet, { + commitment: connection.commitment, + skipPreflight: true, + }); + + await provider.connection.confirmTransaction( + await provider.connection.requestAirdrop(payer, 1e10), + 'confirmed' ); - } catch { } }); - it('Can transfer authority', async () => { - const transferAuthorityIx = createTransferAuthorityIx(cmt, authority, randomSigner,); - await execute(provider, [transferAuthorityIx], [authorityKeypair]); - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt,); - - assert( - splCMT.getAuthority().equals(randomSigner), - `Upon transfering authority, authority should be ${randomSigner.toString()}, but was instead updated to ${splCMT.getAuthority()}` - ); - - // Attempting to replace with new authority now works - const newLeaf = crypto.randomBytes(32); - const replaceIndex = 0; - const proof = offChainTree.getProof(replaceIndex); - const replaceIx = createReplaceIx(cmt, randomSigner, newLeaf, proof); - - await execute(provider, [replaceIx], [randomSignerKeypair]); + describe('Having created a tree with a single leaf', () => { + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, 1, DEPTH_SIZE_PAIR); + cmt = cmtKeypair.publicKey; + }); + it('Append single leaf', async () => { + const newLeaf = crypto.randomBytes(32); + const appendIx = createAppendIx(cmt, payer, newLeaf); + + await execute(provider, [appendIx], [payerKeypair]); + offChainTree.updateLeaf(1, newLeaf); + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root matches root of updated off chain tree' + ); + }); + it('Verify proof works for that leaf', async () => { + const newLeaf = crypto.randomBytes(32); + const index = 0; + const proof = offChainTree.getProof(index); + + const verifyLeafIx = createVerifyLeafIx(cmt, proof); + const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, proof); + await execute(provider, [verifyLeafIx, replaceLeafIx], [payerKeypair]); + + offChainTree.updateLeaf(index, newLeaf); + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root matches root of updated off chain tree' + ); + }); + it('Verify leaf fails when proof fails', async () => { + const newLeaf = crypto.randomBytes(32); + const index = 0; + // Replace valid proof with random bytes so it is wrong + const proof = offChainTree.getProof(index); + proof.proof = proof.proof.map(_ => { + return crypto.randomBytes(32); + }); + + // Verify proof is invalid + const verifyLeafIx = createVerifyLeafIx(cmt, proof); + try { + await execute(provider, [verifyLeafIx], [payerKeypair]); + assert(false, 'Proof should have failed to verify'); + } catch {} + + // Replace instruction with same proof fails + const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, proof); + try { + await execute(provider, [replaceLeafIx], [payerKeypair]); + assert(false, 'Replace should have failed to verify'); + } catch {} + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmtKeypair.publicKey); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root matches root of updated off chain tree' + ); + }); + it('Replace that leaf', async () => { + const newLeaf = crypto.randomBytes(32); + const index = 0; + + const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, offChainTree.getProof(index, false, -1)); + assert(replaceLeafIx.keys.length == 3 + MAX_DEPTH, `Failed to create proof for ${MAX_DEPTH}`); + + await execute(provider, [replaceLeafIx], [payerKeypair]); + + offChainTree.updateLeaf(index, newLeaf); + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root matches root of updated off chain tree' + ); + }); + + it('Replace that leaf with a minimal proof', async () => { + const newLeaf = crypto.randomBytes(32); + const index = 0; + + const replaceLeafIx = createReplaceIx(cmt, payer, newLeaf, offChainTree.getProof(index, true, 1)); + assert(replaceLeafIx.keys.length == 3 + 1, 'Failed to minimize proof to expected size of 1'); + await execute(provider, [replaceLeafIx], [payerKeypair]); + + offChainTree.updateLeaf(index, newLeaf); + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root matches root of updated off chain tree' + ); + }); }); - }); - describe(`Having created a tree with ${MAX_SIZE} leaves`, () => { - beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - MAX_SIZE, - DEPTH_SIZE_PAIR - ); - cmt = cmtKeypair.publicKey; - }); - it(`Replace all of them in a block`, async () => { - // Replace 64 leaves before syncing off-chain tree with on-chain tree - let ixArray: TransactionInstruction[] = []; - let txList: Promise[] = []; - - const leavesToUpdate: Buffer[] = []; - for (let i = 0; i < MAX_SIZE; i++) { - const index = i; - const newLeaf = hash( - payer.toBuffer(), - Buffer.from(new BN(i).toArray()) - ); - leavesToUpdate.push(newLeaf); - const proof = offChainTree.getProof(index); - const replaceIx = createReplaceIx(cmt, payer, newLeaf, proof); - ixArray.push(replaceIx); - } - - // Execute all replaces - ixArray.map((ix) => { - txList.push(execute(provider, [ix], [payerKeypair])); - }); - await Promise.all(txList); - - leavesToUpdate.map((leaf, index) => { - offChainTree.updateLeaf(index, leaf) - }); - - // Compare on-chain & off-chain roots - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - const onChainRoot = splCMT.getCurrentRoot(); - - assert( - Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root does not match root of updated off chain tree' - ); - }); - it('Empty all of the leaves and close the tree', async () => { - let ixArray: TransactionInstruction[] = []; - let txList: Promise[] = []; - const leavesToUpdate: Buffer[] = []; - for (let i = 0; i < MAX_SIZE; i++) { - const index = i; - const newLeaf = hash( - payer.toBuffer(), - Buffer.from(new BN(i).toArray()) - ); - leavesToUpdate.push(newLeaf); - const proof = offChainTree.getProof(index); - const replaceIx = createReplaceIx(cmt, payer, Buffer.alloc(32), proof); - ixArray.push(replaceIx); - } - // Execute all replaces - ixArray.map((ix) => { - txList.push(execute(provider, [ix], [payerKeypair])); - }); - await Promise.all(txList); - - let payerInfo = await provider.connection.getAccountInfo(payer, 'confirmed')!; - let treeInfo = await provider.connection.getAccountInfo(cmt, 'confirmed')!; - - let payerLamports = payerInfo!.lamports; - let treeLamports = treeInfo!.lamports; - - const ix = createCloseEmptyTreeInstruction({ - merkleTree: cmt, - authority: payer, - recipient: payer, - }); - await execute(provider, [ix], [payerKeypair]); - - payerInfo = await provider.connection.getAccountInfo( - payer, - 'confirmed' - )!; - const finalLamports = payerInfo!.lamports; - assert( - finalLamports === payerLamports + treeLamports - 5000, - 'Expected payer to have received the lamports from the closed tree account' - ); - - treeInfo = await provider.connection.getAccountInfo( - cmt, - 'confirmed' - ); - assert( - treeInfo === null, - 'Expected the merkle tree account info to be null' - ); - }); - it('It cannot be closed until empty', async () => { - const ix = createCloseEmptyTreeInstruction({ - merkleTree: cmt, - authority: payer, - recipient: payer, - }); - try { - await execute(provider, [ix], [payerKeypair]); - assert( - false, - 'Closing a tree account before it is empty should ALWAYS error' - ); - } catch (e) { } + describe('Examples transferring authority', () => { + const authorityKeypair = Keypair.generate(); + const authority = authorityKeypair.publicKey; + const randomSignerKeypair = Keypair.generate(); + const randomSigner = randomSignerKeypair.publicKey; + + beforeEach(async () => { + await provider.connection.confirmTransaction( + await (connection as Connection).requestAirdrop(authority, 1e10) + ); + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, authorityKeypair, 1, DEPTH_SIZE_PAIR); + cmt = cmtKeypair.publicKey; + }); + it('Attempting to replace with random authority fails', async () => { + const newLeaf = crypto.randomBytes(32); + const replaceIndex = 0; + const proof = offChainTree.getProof(replaceIndex); + const replaceIx = createReplaceIx(cmt, randomSigner, newLeaf, proof); + + try { + await execute(provider, [replaceIx], [randomSignerKeypair]); + assert(false, 'Transaction should have failed since incorrect authority cannot execute replaces'); + } catch {} + }); + it('Can transfer authority', async () => { + const transferAuthorityIx = createTransferAuthorityIx(cmt, authority, randomSigner); + await execute(provider, [transferAuthorityIx], [authorityKeypair]); + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + + assert( + splCMT.getAuthority().equals(randomSigner), + `Upon transfering authority, authority should be ${randomSigner.toString()}, but was instead updated to ${splCMT.getAuthority()}` + ); + + // Attempting to replace with new authority now works + const newLeaf = crypto.randomBytes(32); + const replaceIndex = 0; + const proof = offChainTree.getProof(replaceIndex); + const replaceIx = createReplaceIx(cmt, randomSigner, newLeaf, proof); + + await execute(provider, [replaceIx], [randomSignerKeypair]); + }); }); - }); - describe(`Having created a tree with depth 3`, () => { - const DEPTH = 3; - beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 0, - { maxDepth: DEPTH, maxBufferSize: 8 } - ); - cmt = cmtKeypair.publicKey; - - for (let i = 0; i < 2 ** DEPTH; i++) { - const newLeaf = Array.from(Buffer.alloc(32, i + 1)); - const appendIx = createAppendIx(cmt, payer, newLeaf); - await execute(provider, [appendIx], [payerKeypair]); - } - - // Compare on-chain & off-chain roots - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - - assert( - splCMT.getBufferSize() === 2 ** DEPTH, - 'Not all changes were processed' - ); - assert(splCMT.getCurrentBufferIndex() === 0, 'Not all changes were processed'); + describe(`Having created a tree with ${MAX_SIZE} leaves`, () => { + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, MAX_SIZE, DEPTH_SIZE_PAIR); + cmt = cmtKeypair.publicKey; + }); + it(`Replace all of them in a block`, async () => { + // Replace 64 leaves before syncing off-chain tree with on-chain tree + const ixArray: TransactionInstruction[] = []; + const txList: Promise[] = []; + + const leavesToUpdate: Buffer[] = []; + for (let i = 0; i < MAX_SIZE; i++) { + const index = i; + const newLeaf = hash(payer.toBuffer(), Buffer.from(new BN(i).toArray())); + leavesToUpdate.push(newLeaf); + const proof = offChainTree.getProof(index); + const replaceIx = createReplaceIx(cmt, payer, newLeaf, proof); + ixArray.push(replaceIx); + } + + // Execute all replaces + ixArray.map(ix => { + txList.push(execute(provider, [ix], [payerKeypair])); + }); + await Promise.all(txList); + + leavesToUpdate.map((leaf, index) => { + offChainTree.updateLeaf(index, leaf); + }); + + // Compare on-chain & off-chain roots + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const onChainRoot = splCMT.getCurrentRoot(); + + assert( + Buffer.from(onChainRoot).equals(offChainTree.root), + 'Updated on chain root does not match root of updated off chain tree' + ); + }); + it('Empty all of the leaves and close the tree', async () => { + const ixArray: TransactionInstruction[] = []; + const txList: Promise[] = []; + const leavesToUpdate: Buffer[] = []; + for (let i = 0; i < MAX_SIZE; i++) { + const index = i; + const newLeaf = hash(payer.toBuffer(), Buffer.from(new BN(i).toArray())); + leavesToUpdate.push(newLeaf); + const proof = offChainTree.getProof(index); + const replaceIx = createReplaceIx(cmt, payer, Buffer.alloc(32), proof); + ixArray.push(replaceIx); + } + // Execute all replaces + ixArray.map(ix => { + txList.push(execute(provider, [ix], [payerKeypair])); + }); + await Promise.all(txList); + + let payerInfo = await provider.connection.getAccountInfo(payer, 'confirmed')!; + let treeInfo = await provider.connection.getAccountInfo(cmt, 'confirmed')!; + + const payerLamports = payerInfo!.lamports; + const treeLamports = treeInfo!.lamports; + + const ix = createCloseEmptyTreeInstruction({ + authority: payer, + merkleTree: cmt, + recipient: payer, + }); + await execute(provider, [ix], [payerKeypair]); + + payerInfo = await provider.connection.getAccountInfo(payer, 'confirmed')!; + const finalLamports = payerInfo!.lamports; + assert( + finalLamports === payerLamports + treeLamports - 5000, + 'Expected payer to have received the lamports from the closed tree account' + ); + + treeInfo = await provider.connection.getAccountInfo(cmt, 'confirmed'); + assert(treeInfo === null, 'Expected the merkle tree account info to be null'); + }); + it('It cannot be closed until empty', async () => { + const ix = createCloseEmptyTreeInstruction({ + authority: payer, + merkleTree: cmt, + recipient: payer, + }); + try { + await execute(provider, [ix], [payerKeypair]); + assert(false, 'Closing a tree account before it is empty should ALWAYS error'); + } catch (e) {} + }); }); - it('Random attacker fails to fake the existence of a leaf by autocompleting proof', async () => { - const maliciousLeafHash = crypto.randomBytes(32); - const maliciousLeafHash1 = crypto.randomBytes(32); - const nodeProof: Buffer[] = []; - for (let i = 0; i < DEPTH; i++) { - nodeProof.push(Buffer.alloc(32)); - } - - // Root - make this nonsense so it won't match what's in ChangeLog, thus forcing proof autocompletion - const replaceIx = createReplaceIx( - cmt, - payer, - maliciousLeafHash1, - { - root: Buffer.alloc(32), - leaf: maliciousLeafHash, - leafIndex: 0, - proof: nodeProof - } - ); - - try { - await execute(provider, [replaceIx], [payerKeypair]); - assert( - false, - 'Attacker was able to succesfully write fake existence of a leaf' - ); - } catch (e) { } - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - ); - - assert( - splCMT.getCurrentBufferIndex() === 0, - "CMT updated its active index after attacker's transaction, when it shouldn't have done anything" - ); - }); - }); - describe(`Canopy test`, () => { - const DEPTH = 5; - it(`Testing canopy for verify leaf instructions`, async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 2 ** DEPTH, - { maxDepth: DEPTH, maxBufferSize: 8 }, - DEPTH // Store full tree on chain - ); - cmt = cmtKeypair.publicKey; - - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmt, - "confirmed" - ); - let i = 0; - const stepSize = 4; - while (i < 2 ** DEPTH) { - const ixs: TransactionInstruction[] = []; - for (let j = 0; j < stepSize; j += 1) { - const leafIndex = i + j; - const leaf = offChainTree.leaves[leafIndex].node; - const verifyIx = createVerifyLeafIx(cmt, { - root: splCMT.getCurrentRoot(), - leaf, - leafIndex, - proof: [], - }); - ixs.push(verifyIx); - } - i += stepSize; - await execute(provider, ixs, [payerKeypair]); - } + describe(`Having created a tree with depth 3`, () => { + const DEPTH = 3; + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, 0, { + maxBufferSize: 8, + maxDepth: DEPTH, + }); + cmt = cmtKeypair.publicKey; + + for (let i = 0; i < 2 ** DEPTH; i++) { + const newLeaf = Array.from(Buffer.alloc(32, i + 1)); + const appendIx = createAppendIx(cmt, payer, newLeaf); + await execute(provider, [appendIx], [payerKeypair]); + } + + // Compare on-chain & off-chain roots + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + + assert(splCMT.getBufferSize() === 2 ** DEPTH, 'Not all changes were processed'); + assert(splCMT.getCurrentBufferIndex() === 0, 'Not all changes were processed'); + }); + + it('Random attacker fails to fake the existence of a leaf by autocompleting proof', async () => { + const maliciousLeafHash = crypto.randomBytes(32); + const maliciousLeafHash1 = crypto.randomBytes(32); + const nodeProof: Buffer[] = []; + for (let i = 0; i < DEPTH; i++) { + nodeProof.push(Buffer.alloc(32)); + } + + // Root - make this nonsense so it won't match what's in ChangeLog, thus forcing proof autocompletion + const replaceIx = createReplaceIx(cmt, payer, maliciousLeafHash1, { + leaf: maliciousLeafHash, + leafIndex: 0, + proof: nodeProof, + root: Buffer.alloc(32), + }); + + try { + await execute(provider, [replaceIx], [payerKeypair]); + assert(false, 'Attacker was able to succesfully write fake existence of a leaf'); + } catch (e) {} + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + + assert( + splCMT.getCurrentBufferIndex() === 0, + "CMT updated its active index after attacker's transaction, when it shouldn't have done anything" + ); + }); }); - it('Testing canopy for appends and replaces on a full on chain tree', async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 0, - { maxDepth: DEPTH, maxBufferSize: 8 }, - DEPTH // Store full tree on chain - ); - cmt = cmtKeypair.publicKey; - - // Test that the canopy updates properly throughout multiple modifying instructions - // in the same transaction - let leaves: Array[] = []; - let i = 0; - let stepSize = 4; - while (i < 2 ** DEPTH) { - let ixs: TransactionInstruction[] = []; - for (let j = 0; j < stepSize; ++j) { - const newLeaf = Array.from(Buffer.alloc(32, i + 1)); - leaves.push(newLeaf); - const appendIx = createAppendIx(cmt, payer, newLeaf); - ixs.push(appendIx); - } - await execute(provider, ixs, [payerKeypair]); - i += stepSize; - console.log('Appended', i, 'leaves'); - } - - // Compare on-chain & off-chain roots - let ixs: TransactionInstruction[] = []; - const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt,); - const root = splCMT.getCurrentRoot(); - - // Test that the entire state of the tree is stored properly - // by using the canopy to infer proofs to all of the leaves in the tree. - // We test that the canopy is updating properly by replacing all the leaves - // in the tree - let leafList = Array.from(leaves.entries()); - leafList.sort(() => Math.random() - 0.5); - let replaces = 0; - let newLeaves: Record = {}; - for (const [i, leaf] of leafList) { - const newLeaf = crypto.randomBytes(32); - newLeaves[i] = newLeaf; - const replaceIx = createReplaceIx( - cmt, - payer, - newLeaf, - { - root, - leaf: Buffer.from(Uint8Array.from(leaf)), - leafIndex: i, - proof: [] // No proof necessary - } - ); - ixs.push(replaceIx); - if (ixs.length == stepSize) { - replaces++; - await execute(provider, ixs, [payerKeypair]); - console.log('Replaced', replaces * stepSize, 'leaves'); - ixs = []; - } - } - - let newLeafList: Buffer[] = []; - for (let i = 0; i < 32; ++i) { - newLeafList.push(newLeaves[i]); - } - - let tree = new MerkleTree(newLeafList); - - for (let proofSize = 1; proofSize <= 5; ++proofSize) { - const newLeaf = crypto.randomBytes(32); - let i = Math.floor(Math.random() * 32); - const leaf = newLeaves[i]; - - let proof = tree.getProof(i); - let partialProof = proof.proof.slice(0, proofSize); - - // Create an instruction to replace the leaf - const replaceIx = createReplaceIx(cmt, payer, newLeaf, { ...proof, proof: partialProof, }); - tree.updateLeaf(i, newLeaf); - - // Create an instruction to undo the previous replace, but using the now-outdated partialProof - proof = tree.getProof(i); - const replaceBackIx = createReplaceIx(cmt, payer, leaf, { ...proof, proof: partialProof }); - tree.updateLeaf(i, leaf); - await execute( - provider, - [replaceIx, replaceBackIx], - [payerKeypair], - true, - true - ); - } + describe(`Canopy test`, () => { + const DEPTH = 5; + it(`Testing canopy for verify leaf instructions`, async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain( + provider, + payerKeypair, + 2 ** DEPTH, + { maxBufferSize: 8, maxDepth: DEPTH }, + DEPTH // Store full tree on chain + ); + cmt = cmtKeypair.publicKey; + + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt, 'confirmed'); + let i = 0; + const stepSize = 4; + while (i < 2 ** DEPTH) { + const ixs: TransactionInstruction[] = []; + for (let j = 0; j < stepSize; j += 1) { + const leafIndex = i + j; + const leaf = offChainTree.leaves[leafIndex].node; + const verifyIx = createVerifyLeafIx(cmt, { + leaf, + leafIndex, + proof: [], + root: splCMT.getCurrentRoot(), + }); + ixs.push(verifyIx); + } + i += stepSize; + await execute(provider, ixs, [payerKeypair]); + } + }); + it('Testing canopy for appends and replaces on a full on chain tree', async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain( + provider, + payerKeypair, + 0, + { maxBufferSize: 8, maxDepth: DEPTH }, + DEPTH // Store full tree on chain + ); + cmt = cmtKeypair.publicKey; + + // Test that the canopy updates properly throughout multiple modifying instructions + // in the same transaction + const leaves: Array[] = []; + let i = 0; + const stepSize = 4; + while (i < 2 ** DEPTH) { + const ixs: TransactionInstruction[] = []; + for (let j = 0; j < stepSize; ++j) { + const newLeaf = Array.from(Buffer.alloc(32, i + 1)); + leaves.push(newLeaf); + const appendIx = createAppendIx(cmt, payer, newLeaf); + ixs.push(appendIx); + } + await execute(provider, ixs, [payerKeypair]); + i += stepSize; + console.log('Appended', i, 'leaves'); + } + + // Compare on-chain & off-chain roots + let ixs: TransactionInstruction[] = []; + const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); + const root = splCMT.getCurrentRoot(); + + // Test that the entire state of the tree is stored properly + // by using the canopy to infer proofs to all of the leaves in the tree. + // We test that the canopy is updating properly by replacing all the leaves + // in the tree + const leafList = Array.from(leaves.entries()); + leafList.sort(() => Math.random() - 0.5); + let replaces = 0; + const newLeaves: Record = {}; + for (const [i, leaf] of leafList) { + const newLeaf = crypto.randomBytes(32); + newLeaves[i] = newLeaf; + const replaceIx = createReplaceIx(cmt, payer, newLeaf, { + leaf: Buffer.from(Uint8Array.from(leaf)), + leafIndex: i, + proof: [], + root, // No proof necessary + }); + ixs.push(replaceIx); + if (ixs.length == stepSize) { + replaces++; + await execute(provider, ixs, [payerKeypair]); + console.log('Replaced', replaces * stepSize, 'leaves'); + ixs = []; + } + } + + const newLeafList: Buffer[] = []; + for (let i = 0; i < 32; ++i) { + newLeafList.push(newLeaves[i]); + } + + const tree = new MerkleTree(newLeafList); + + for (let proofSize = 1; proofSize <= 5; ++proofSize) { + const newLeaf = crypto.randomBytes(32); + const i = Math.floor(Math.random() * 32); + const leaf = newLeaves[i]; + + let proof = tree.getProof(i); + const partialProof = proof.proof.slice(0, proofSize); + + // Create an instruction to replace the leaf + const replaceIx = createReplaceIx(cmt, payer, newLeaf, { + ...proof, + proof: partialProof, + }); + tree.updateLeaf(i, newLeaf); + + // Create an instruction to undo the previous replace, but using the now-outdated partialProof + proof = tree.getProof(i); + const replaceBackIx = createReplaceIx(cmt, payer, leaf, { + ...proof, + proof: partialProof, + }); + tree.updateLeaf(i, leaf); + await execute(provider, [replaceIx, replaceBackIx], [payerKeypair], true, true); + } + }); }); - }); - describe(`Having created a tree with 8 leaves`, () => { - beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 1 << 3, - { maxDepth: 3, maxBufferSize: 8 }, - ); - cmt = cmtKeypair.publicKey; - }); - it(`Attempt to replace a leaf beyond the tree's capacity`, async () => { - // Ensure that this fails - let outOfBoundsIndex = 8; - const index = outOfBoundsIndex; - const newLeaf = hash( - payer.toBuffer(), - Buffer.from(new BN(outOfBoundsIndex).toArray()) - ); - let proof; - let node; - node = offChainTree.leaves[outOfBoundsIndex - 1].node; - proof = offChainTree.getProof(index - 1).proof; - - const replaceIx = createReplaceIx( - cmt, - payer, - newLeaf, - { - root: offChainTree.root, - leaf: node, - leafIndex: index, - proof - } - ); - - try { - await execute(provider, [replaceIx], [payerKeypair]); - throw Error( - 'This replace instruction should have failed because the leaf index is OOB' - ); - } catch (_e) { } + describe(`Having created a tree with 8 leaves`, () => { + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, 1 << 3, { + maxBufferSize: 8, + maxDepth: 3, + }); + cmt = cmtKeypair.publicKey; + }); + it(`Attempt to replace a leaf beyond the tree's capacity`, async () => { + // Ensure that this fails + const outOfBoundsIndex = 8; + const index = outOfBoundsIndex; + const newLeaf = hash(payer.toBuffer(), Buffer.from(new BN(outOfBoundsIndex).toArray())); + const node = offChainTree.leaves[outOfBoundsIndex - 1].node; + const proof = offChainTree.getProof(index - 1).proof; + + const replaceIx = createReplaceIx(cmt, payer, newLeaf, { + leaf: node, + leafIndex: index, + proof, + root: offChainTree.root, + }); + + try { + await execute(provider, [replaceIx], [payerKeypair]); + throw Error('This replace instruction should have failed because the leaf index is OOB'); + } catch (_e) {} + }); }); - }); }); diff --git a/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts b/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts index 02b64e7282e..f7f9799b223 100644 --- a/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts +++ b/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts @@ -1,197 +1,144 @@ -import { - Keypair, - Connection, - PublicKey, - LAMPORTS_PER_SOL, -} from '@solana/web3.js'; import { AnchorProvider } from '@project-serum/anchor'; import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet'; +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; import { assert } from 'chai'; +import { ALL_DEPTH_SIZE_PAIRS, ConcurrentMerkleTreeAccount, getConcurrentMerkleTreeAccountSize } from '../../src'; import { emptyNode, MerkleTree } from '../../src/merkle-tree'; -import { createTreeOnChain, createEmptyTreeOnChain } from '../utils'; -import { - ALL_DEPTH_SIZE_PAIRS, - ConcurrentMerkleTreeAccount, - getConcurrentMerkleTreeAccountSize, -} from '../../src'; +import { createEmptyTreeOnChain, createTreeOnChain } from '../utils'; async function assertCMTProperties( - onChainCMT: ConcurrentMerkleTreeAccount, - expectedMaxDepth: number, - expectedMaxBufferSize: number, - expectedAuthority: PublicKey, - expectedRoot: Buffer, - expectedCanopyDepth?: number + onChainCMT: ConcurrentMerkleTreeAccount, + expectedMaxDepth: number, + expectedMaxBufferSize: number, + expectedAuthority: PublicKey, + expectedRoot: Buffer, + expectedCanopyDepth?: number ) { - assert( - onChainCMT.getMaxDepth() === expectedMaxDepth, - `Max depth does not match ${onChainCMT.getMaxDepth()}, expected ${expectedMaxDepth}` - ); - assert( - onChainCMT.getMaxBufferSize() === expectedMaxBufferSize, - `Max buffer size does not match ${onChainCMT.getMaxBufferSize()}, expected ${expectedMaxBufferSize}` - ); - assert( - onChainCMT.getAuthority().equals(expectedAuthority), - 'Failed to write auth pubkey' - ); - assert( - onChainCMT.getCurrentRoot().equals(expectedRoot), - 'On chain root does not match root passed in instruction' - ); - if (expectedCanopyDepth) { assert( - onChainCMT.getCanopyDepth() === expectedCanopyDepth, - 'On chain canopy depth does not match expected canopy depth' + onChainCMT.getMaxDepth() === expectedMaxDepth, + `Max depth does not match ${onChainCMT.getMaxDepth()}, expected ${expectedMaxDepth}` ); - } + assert( + onChainCMT.getMaxBufferSize() === expectedMaxBufferSize, + `Max buffer size does not match ${onChainCMT.getMaxBufferSize()}, expected ${expectedMaxBufferSize}` + ); + assert(onChainCMT.getAuthority().equals(expectedAuthority), 'Failed to write auth pubkey'); + assert(onChainCMT.getCurrentRoot().equals(expectedRoot), 'On chain root does not match root passed in instruction'); + if (expectedCanopyDepth) { + assert( + onChainCMT.getCanopyDepth() === expectedCanopyDepth, + 'On chain canopy depth does not match expected canopy depth' + ); + } } describe('ConcurrentMerkleTreeAccount tests', () => { - // Configure the client to use the local cluster. - let offChainTree: MerkleTree; - let cmtKeypair: Keypair; - let payerKeypair: Keypair; - let payer: PublicKey; - let connection: Connection; - let provider: AnchorProvider; - - beforeEach(async () => { - payerKeypair = Keypair.generate(); - payer = payerKeypair.publicKey; - connection = new Connection('http://localhost:8899', { - commitment: 'confirmed', - }); - const wallet = new NodeWallet(payerKeypair); - provider = new AnchorProvider(connection, wallet, { - commitment: connection.commitment, - skipPreflight: true, - }); - - await provider.connection.confirmTransaction( - await provider.connection.requestAirdrop(payer, 1e10), - 'confirmed' - ); - }); - - describe('Can deserialize a CMTAccount from an on-chain CMT with a single leaf', () => { - const MAX_SIZE = 64; - const MAX_DEPTH = 14; + // Configure the client to use the local cluster. + let offChainTree: MerkleTree; + let cmtKeypair: Keypair; + let payerKeypair: Keypair; + let payer: PublicKey; + let connection: Connection; + let provider: AnchorProvider; beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 1, - { maxDepth: MAX_DEPTH, maxBufferSize: MAX_SIZE } - ); + payerKeypair = Keypair.generate(); + payer = payerKeypair.publicKey; + connection = new Connection('http://127.0.0.1:8899', { + commitment: 'confirmed', + }); + const wallet = new NodeWallet(payerKeypair); + provider = new AnchorProvider(connection, wallet, { + commitment: connection.commitment, + skipPreflight: true, + }); + + await provider.connection.confirmTransaction( + await provider.connection.requestAirdrop(payer, 1e10), + 'confirmed' + ); }); - it('Interpreted on-chain fields correctly', async () => { - const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmtKeypair.publicKey, - 'confirmed' - ); - - await assertCMTProperties( - cmt, - MAX_DEPTH, - MAX_SIZE, - payer, - offChainTree.root - ); + describe('Can deserialize a CMTAccount from an on-chain CMT with a single leaf', () => { + const MAX_SIZE = 64; + const MAX_DEPTH = 14; + + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, 1, { + maxBufferSize: MAX_SIZE, + maxDepth: MAX_DEPTH, + }); + }); + + it('Interpreted on-chain fields correctly', async () => { + const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( + connection, + cmtKeypair.publicKey, + 'confirmed' + ); + + await assertCMTProperties(cmt, MAX_DEPTH, MAX_SIZE, payer, offChainTree.root); + }); }); - }); - - describe('Test deserialization for available depth-size pairs', () => { - it('Test all pairs', async () => { - for (const depthSizePair of ALL_DEPTH_SIZE_PAIRS) { - // Airdrop enough SOL to cover tree creation - const size = getConcurrentMerkleTreeAccountSize( - depthSizePair.maxDepth, - depthSizePair.maxBufferSize - ); - const rent = await connection.getMinimumBalanceForRentExemption( - size, - 'confirmed' - ); - const airdropId = await connection.requestAirdrop( - payer, - rent + 5000 * 2 - ); - await connection.confirmTransaction(airdropId, 'confirmed'); - - // Create on chain tree - cmtKeypair = await createEmptyTreeOnChain( - provider, - payerKeypair, - depthSizePair - ); - const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmtKeypair.publicKey, - 'confirmed' - ); - // Verify it was initialized correctly - await assertCMTProperties( - cmt, - depthSizePair.maxDepth, - depthSizePair.maxBufferSize, - payer, - emptyNode(depthSizePair.maxDepth) - ); - } + describe('Test deserialization for available depth-size pairs', () => { + it('Test all pairs', async () => { + for (const depthSizePair of ALL_DEPTH_SIZE_PAIRS) { + // Airdrop enough SOL to cover tree creation + const size = getConcurrentMerkleTreeAccountSize(depthSizePair.maxDepth, depthSizePair.maxBufferSize); + const rent = await connection.getMinimumBalanceForRentExemption(size, 'confirmed'); + const airdropId = await connection.requestAirdrop(payer, rent + 5000 * 2); + await connection.confirmTransaction(airdropId, 'confirmed'); + + // Create on chain tree + cmtKeypair = await createEmptyTreeOnChain(provider, payerKeypair, depthSizePair); + const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( + connection, + cmtKeypair.publicKey, + 'confirmed' + ); + + // Verify it was initialized correctly + await assertCMTProperties( + cmt, + depthSizePair.maxDepth, + depthSizePair.maxBufferSize, + payer, + emptyNode(depthSizePair.maxDepth) + ); + } + }); }); - }); - - describe('Test deserialization for canopy size for depth 30 tree', () => { - it('Test all pairs', async () => { - const maxDepth = 30; - const maxBufferSize = 2048; - - for (let canopyDepth = 1; canopyDepth <= 14; canopyDepth++) { - // Airdrop enough SOL to cover tree creation - const size = getConcurrentMerkleTreeAccountSize( - maxDepth, - maxBufferSize, - canopyDepth - ); - const rent = await connection.getMinimumBalanceForRentExemption( - size, - 'confirmed' - ); - const airdropId = await connection.requestAirdrop( - payer, - rent + 5000 * 2 - ); - await connection.confirmTransaction(airdropId, 'confirmed'); - - // Create on chain tree - cmtKeypair = await createEmptyTreeOnChain( - provider, - payerKeypair, - { maxDepth, maxBufferSize }, - canopyDepth - ); - const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( - connection, - cmtKeypair.publicKey, - 'confirmed' - ); - // Verify it was initialized correctly - await assertCMTProperties( - cmt, - maxDepth, - maxBufferSize, - payer, - emptyNode(maxDepth), - canopyDepth - ); - } + describe('Test deserialization for canopy size for depth 30 tree', () => { + it('Test all pairs', async () => { + const maxDepth = 30; + const maxBufferSize = 2048; + + for (let canopyDepth = 1; canopyDepth <= 14; canopyDepth++) { + // Airdrop enough SOL to cover tree creation + const size = getConcurrentMerkleTreeAccountSize(maxDepth, maxBufferSize, canopyDepth); + const rent = await connection.getMinimumBalanceForRentExemption(size, 'confirmed'); + const airdropId = await connection.requestAirdrop(payer, rent + 5000 * 2); + await connection.confirmTransaction(airdropId, 'confirmed'); + + // Create on chain tree + cmtKeypair = await createEmptyTreeOnChain( + provider, + payerKeypair, + { maxBufferSize, maxDepth }, + canopyDepth + ); + const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( + connection, + cmtKeypair.publicKey, + 'confirmed' + ); + + // Verify it was initialized correctly + await assertCMTProperties(cmt, maxDepth, maxBufferSize, payer, emptyNode(maxDepth), canopyDepth); + } + }); }); - }); }); diff --git a/account-compression/sdk/tests/events/applicationData.test.ts b/account-compression/sdk/tests/events/applicationData.test.ts index 1c24816996e..f71521b63a4 100644 --- a/account-compression/sdk/tests/events/applicationData.test.ts +++ b/account-compression/sdk/tests/events/applicationData.test.ts @@ -1,16 +1,16 @@ -import { BN } from 'bn.js'; -import { assert } from 'chai'; +import { BN } from "bn.js"; +import { assert } from "chai"; -import { deserializeApplicationDataEvent } from '../../src'; +import { deserializeApplicationDataEvent } from "../../src"; -describe('Serde tests', () => { - describe('ApplicationDataEvent tests', () => { - it('Can serialize and deserialize ApplicationDataEvent', async () => { - const data = Buffer.from('Hello world'); +describe("Serde tests", () => { + describe("ApplicationDataEvent tests", () => { + it("Can serialize and deserialize ApplicationDataEvent", async () => { + const data = Buffer.from("Hello world"); const applicationDataEvent = Buffer.concat([ Buffer.from([0x1]), // ApplicationData Event tag Buffer.from([0x0]), // version 0 tag - Buffer.from(new BN.BN(data.length).toArray('le', 4)), // Size of application data (for Vec) + Buffer.from(new BN.BN(data.length).toArray("le", 4)), // Size of application data (for Vec) data, // serialized application data (for Vec) ]); @@ -20,7 +20,7 @@ describe('Serde tests', () => { const deserializedData = decoder.decode( deserialized.fields[0].applicationData ); - assert('Hello world' === deserializedData); + assert("Hello world" === deserializedData); }); }); }); diff --git a/account-compression/sdk/tests/events/changelog.test.ts b/account-compression/sdk/tests/events/changelog.test.ts index 08813662c2f..807ad0eb60a 100644 --- a/account-compression/sdk/tests/events/changelog.test.ts +++ b/account-compression/sdk/tests/events/changelog.test.ts @@ -1,20 +1,20 @@ -import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet'; -import { BN } from 'bn.js'; -import { AnchorProvider } from '@project-serum/anchor'; -import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'; -import { assert } from 'chai'; -import * as crypto from 'crypto'; +import { AnchorProvider } from "@project-serum/anchor"; +import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; +import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes"; +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; +import { BN } from "bn.js"; +import { assert } from "chai"; +import * as crypto from "crypto"; import { createAppendIx, deserializeChangeLogEventV1, SPL_NOOP_PROGRAM_ID, -} from '../../src'; -import { MerkleTree } from '../../src/merkle-tree'; -import { execute, createTreeOnChain } from '../utils'; +} from "../../src"; +import { MerkleTree } from "../../src/merkle-tree"; +import { createTreeOnChain,execute } from "../utils"; -describe('Serde tests', () => { +describe("Serde tests", () => { let offChainTree: MerkleTree; let cmtKeypair: Keypair; let payerKeypair: Keypair; @@ -29,8 +29,8 @@ describe('Serde tests', () => { payerKeypair = Keypair.generate(); payer = payerKeypair.publicKey; - connection = new Connection('http://localhost:8899', { - commitment: 'confirmed', + connection = new Connection("http://127.0.0.1:8899", { + commitment: "confirmed", }); const wallet = new NodeWallet(payerKeypair); provider = new AnchorProvider(connection, wallet, { @@ -40,21 +40,21 @@ describe('Serde tests', () => { await provider.connection.confirmTransaction( await provider.connection.requestAirdrop(payer, 1e10), - 'confirmed' + "confirmed" ); }); - describe('ChangeLogEvent tests', () => { + describe("ChangeLogEvent tests", () => { let cmt: PublicKey; beforeEach(async () => { [cmtKeypair, offChainTree] = await createTreeOnChain( provider, payerKeypair, 0, - { maxDepth: MAX_DEPTH, maxBufferSize: MAX_SIZE } + { maxBufferSize: MAX_SIZE, maxDepth: MAX_DEPTH } ); cmt = cmtKeypair.publicKey; }); - it('Can deserialize a ChangeLogEvent', async () => { + it("Can deserialize a ChangeLogEvent", async () => { const newLeaf = crypto.randomBytes(32); const txId = await execute( provider, @@ -64,7 +64,7 @@ describe('Serde tests', () => { offChainTree.updateLeaf(0, newLeaf); const transaction = await connection.getTransaction(txId, { - commitment: 'confirmed', + commitment: "confirmed", maxSupportedTransactionVersion: 2, }); diff --git a/account-compression/sdk/tests/utils.ts b/account-compression/sdk/tests/utils.ts index ce9714f3627..784f36266b6 100644 --- a/account-compression/sdk/tests/utils.ts +++ b/account-compression/sdk/tests/utils.ts @@ -1,34 +1,15 @@ -import { - Connection, - PublicKey, - Transaction, - TransactionInstruction, - Keypair, - Signer, -} from '@solana/web3.js'; import { AnchorProvider } from '@project-serum/anchor'; +import { Keypair, Signer, Transaction, TransactionInstruction } from '@solana/web3.js'; import * as crypto from 'crypto'; -import { - createInitEmptyMerkleTreeIx, - createAllocTreeIx, - createAppendIx, - ValidDepthSizePair, -} from '../src'; +import { createAllocTreeIx, createAppendIx, createInitEmptyMerkleTreeIx, ValidDepthSizePair } from '../src'; import { MerkleTree } from '../src/merkle-tree'; /// Wait for a transaction of a certain id to confirm and optionally log its messages -export async function confirmAndLogTx( - provider: AnchorProvider, - txId: string, - verbose: boolean = false -) { +export async function confirmAndLogTx(provider: AnchorProvider, txId: string, verbose = false) { const tx = await provider.connection.confirmTransaction(txId, 'confirmed'); if (tx.value.err || verbose) { - console.log( - (await provider.connection.getConfirmedTransaction(txId, 'confirmed'))! - .meta!.logMessages - ); + console.log((await provider.connection.getConfirmedTransaction(txId, 'confirmed'))!.meta!.logMessages); } if (tx.value.err) { console.log('Transaction failed'); @@ -41,11 +22,11 @@ export async function execute( provider: AnchorProvider, instructions: TransactionInstruction[], signers: Signer[], - skipPreflight: boolean = false, - verbose: boolean = false + skipPreflight = false, + verbose = false ): Promise { let tx = new Transaction(); - instructions.map((ix) => { + instructions.map(ix => { tx = tx.add(ix); }); @@ -60,10 +41,7 @@ export async function execute( } if (verbose && txid) { - console.log( - (await provider.connection.getConfirmedTransaction(txid, 'confirmed'))! - .meta!.logMessages - ); + console.log((await provider.connection.getConfirmedTransaction(txid, 'confirmed'))!.meta!.logMessages); } return txid; @@ -74,7 +52,7 @@ export async function createTreeOnChain( payer: Keypair, numLeaves: number, depthSizePair: ValidDepthSizePair, - canopyDepth: number = 0 + canopyDepth = 0 ): Promise<[Keypair, MerkleTree]> { const cmtKeypair = Keypair.generate(); @@ -89,22 +67,19 @@ export async function createTreeOnChain( cmtKeypair.publicKey, payer.publicKey, depthSizePair, - canopyDepth, + canopyDepth ); - let ixs = [ - allocAccountIx, - createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair), - ]; + const ixs = [allocAccountIx, createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair)]; - let txId = await execute(provider, ixs, [payer, cmtKeypair]); + const txId = await execute(provider, ixs, [payer, cmtKeypair]); if (canopyDepth) { await confirmAndLogTx(provider, txId as string); } if (numLeaves) { const nonZeroLeaves = leaves.slice(0, numLeaves); - let appendIxs: TransactionInstruction[] = nonZeroLeaves.map((leaf) => { + let appendIxs: TransactionInstruction[] = nonZeroLeaves.map(leaf => { return createAppendIx(cmtKeypair.publicKey, payer.publicKey, leaf); }); while (appendIxs.length) { @@ -120,7 +95,7 @@ export async function createEmptyTreeOnChain( provider: AnchorProvider, payer: Keypair, depthSizePair: ValidDepthSizePair, - canopyDepth: number = 0 + canopyDepth = 0 ): Promise { const cmtKeypair = Keypair.generate(); const allocAccountIx = await createAllocTreeIx( @@ -128,15 +103,12 @@ export async function createEmptyTreeOnChain( cmtKeypair.publicKey, payer.publicKey, depthSizePair, - canopyDepth, + canopyDepth ); - let ixs = [ - allocAccountIx, - createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair), - ]; + const ixs = [allocAccountIx, createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair)]; - let txId = await execute(provider, ixs, [payer, cmtKeypair]); + const txId = await execute(provider, ixs, [payer, cmtKeypair]); await confirmAndLogTx(provider, txId as string); return cmtKeypair; diff --git a/account-compression/sdk/typedoc.json b/account-compression/sdk/typedoc.json index bb601e894e5..265991d0559 100644 --- a/account-compression/sdk/typedoc.json +++ b/account-compression/sdk/typedoc.json @@ -1,9 +1,6 @@ { - "entryPoints": [ - "src/index.ts", - "src/merkle-tree/index.ts" - ], + "entryPoints": ["src/index.ts", "src/merkle-tree/index.ts"], "excludeInternal": true, "excludePrivate": false, "out": "doc" -} \ No newline at end of file +} diff --git a/account-compression/sdk/yarn.lock b/account-compression/sdk/yarn.lock index 3b05ba6fd15..bd8c6d8c4d4 100644 --- a/account-compression/sdk/yarn.lock +++ b/account-compression/sdk/yarn.lock @@ -17,6 +17,14 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" @@ -52,6 +60,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-compilation-targets@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" @@ -67,20 +85,25 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -122,16 +145,33 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -155,11 +195,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -274,19 +328,28 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.19.0", "@babel/traverse@^7.7.2": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed" - integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.19.0", "@babel/traverse@^7.7.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -299,6 +362,15 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -311,6 +383,13 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + "@eslint/eslintrc@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" @@ -618,6 +697,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -628,6 +712,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -644,6 +733,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@metaplex-foundation/beet-solana@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" @@ -816,6 +913,16 @@ dependencies: buffer "~6.0.3" +"@solana/eslint-config-solana@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@solana/eslint-config-solana/-/eslint-config-solana-1.0.2.tgz#5ed07bc11e349bbf7ea9952d6e07af85370ed949" + integrity sha512-/mBi2xFsX0fq3ukZ3ugA+Zh2+Ct+bh713ZSdOJAv1G7ARzyOEDxmiUz8TLzHu4joOC/3Ok4Yl7cjupOcIRzMHw== + +"@solana/prettier-config-solana@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@solana/prettier-config-solana/-/prettier-config-solana-0.0.2.tgz#bebf03dd74967443fdf631ed27da7e64eb38ad61" + integrity sha512-F/e2UIJwb30Y8QjR9nr/OrJiCc8yjMkiP9Ctk4VYg+8jODNP31dx6s9mn4sbMFVYA0Km5EPZLN2xsZacBy0y/A== + "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2": version "1.59.1" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.59.1.tgz#05ac572f11663cb8a718546e14b16aa4578cbc0b" @@ -1044,6 +1151,14 @@ "@typescript-eslint/types" "5.40.1" "@typescript-eslint/visitor-keys" "5.40.1" +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + "@typescript-eslint/type-utils@5.40.1": version "5.40.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.40.1.tgz#091e4ce3bebbdb68f4980bae9dee2e4e1725f601" @@ -1059,6 +1174,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.40.1.tgz#de37f4f64de731ee454bb2085d71030aa832f749" integrity sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + "@typescript-eslint/typescript-estree@5.40.1": version "5.40.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz#9a7d25492f02c69882ce5e0cd1857b0c55645d72" @@ -1072,6 +1192,19 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.40.1": version "5.40.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.40.1.tgz#3204fb73a559d3b7bab7dc9d3c44487c2734a9ca" @@ -1086,6 +1219,20 @@ eslint-utils "^3.0.0" semver "^7.3.7" +"@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.40.1": version "5.40.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz#f3d2bf5af192f4432b84cec6fdcb387193518754" @@ -1094,6 +1241,14 @@ "@typescript-eslint/types" "5.40.1" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -1102,7 +1257,7 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -acorn-jsx@^5.3.2: +acorn-jsx@^5.2.0, acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -1112,6 +1267,11 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + acorn@^8.4.1: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" @@ -1504,7 +1664,7 @@ chai@^4.3.4: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1756,6 +1916,11 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dotenv@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + duplexer@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -1871,6 +2036,13 @@ eslint-config-prettier@^8.5.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-turbo@^1.10.12: + version "1.10.12" + resolved "https://registry.yarnpkg.com/eslint-config-turbo/-/eslint-config-turbo-1.10.12.tgz#5868252d6833dd2b5cab4414751ed31ebe2177c3" + integrity sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA== + dependencies: + eslint-plugin-turbo "1.10.12" + eslint-import-resolver-node@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" @@ -1905,6 +2077,13 @@ eslint-plugin-import@^2.26.0: resolve "^1.22.0" tsconfig-paths "^3.14.1" +eslint-plugin-jest@^27.2.3: + version "27.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.2.3.tgz#6f8a4bb2ca82c0c5d481d1b3be256ab001f5a3ec" + integrity sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + eslint-plugin-mocha@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz#69325414f875be87fb2cb00b2ef33168d4eb7c8d" @@ -1920,6 +2099,28 @@ eslint-plugin-prettier@^4.2.1: dependencies: prettier-linter-helpers "^1.0.0" +eslint-plugin-simple-import-sort@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz#cc4ceaa81ba73252427062705b64321946f61351" + integrity sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw== + +eslint-plugin-sort-keys-fix@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-sort-keys-fix/-/eslint-plugin-sort-keys-fix-1.1.2.tgz#00c8b5791612ec32162b8d7a0563e9c6eb27ec59" + integrity sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw== + dependencies: + espree "^6.1.2" + esutils "^2.0.2" + natural-compare "^1.4.0" + requireindex "~1.2.0" + +eslint-plugin-turbo@1.10.12: + version "1.10.12" + resolved "https://registry.yarnpkg.com/eslint-plugin-turbo/-/eslint-plugin-turbo-1.10.12.tgz#3f95884faf35b56e0855d939585fa6cd457bb128" + integrity sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw== + dependencies: + dotenv "16.0.3" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1943,6 +2144,11 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -1998,6 +2204,15 @@ eslint@^8.25.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + espree@^9.4.0: version "9.4.0" resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" @@ -2285,9 +2500,9 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" @@ -3798,6 +4013,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +requireindex@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -3888,16 +4108,16 @@ safe-regex-test@^1.0.0: is-regex "^1.1.4" semver@7.x, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== shebang-command@^2.0.0: version "2.0.0" @@ -4416,9 +4636,9 @@ which@^2.0.1: isexe "^2.0.0" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== wrap-ansi@^7.0.0: version "7.0.0" diff --git a/associated-token-account/program-test/Cargo.toml b/associated-token-account/program-test/Cargo.toml index 84dba1f9fc9..032a8f0ee89 100644 --- a/associated-token-account/program-test/Cargo.toml +++ b/associated-token-account/program-test/Cargo.toml @@ -11,9 +11,9 @@ version = "0.0.1" test-sbf = [] [dev-dependencies] -solana-program = "1.16.1" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -spl-associated-token-account = { version = "2.0", path = "../program", features = ["no-entrypoint"] } +solana-program = "1.17.2" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +spl-associated-token-account = { version = "2", path = "../program", features = ["no-entrypoint"] } spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } diff --git a/associated-token-account/program-test/tests/create_idempotent.rs b/associated-token-account/program-test/tests/create_idempotent.rs index e2e0f3e399f..2e54f1c3abe 100644 --- a/associated-token-account/program-test/tests/create_idempotent.rs +++ b/associated-token-account/program-test/tests/create_idempotent.rs @@ -43,7 +43,8 @@ async fn success_account_exists() { program_test_2022(token_mint_address, true).start().await; let rent = banks_client.get_rent().await.unwrap(); let expected_token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let expected_token_account_balance = rent.minimum_balance(expected_token_account_len); let instruction = create_associated_token_account_idempotent( @@ -189,7 +190,8 @@ async fn fail_non_ata() { let rent = banks_client.get_rent().await.unwrap(); let token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let token_account_balance = rent.minimum_balance(token_account_len); let wallet_address = Pubkey::new_unique(); diff --git a/associated-token-account/program-test/tests/extended_mint.rs b/associated-token-account/program-test/tests/extended_mint.rs index 0b83d399d1e..43e1b92c59e 100644 --- a/associated-token-account/program-test/tests/extended_mint.rs +++ b/associated-token-account/program-test/tests/extended_mint.rs @@ -39,7 +39,8 @@ async fn test_associated_token_account_with_transfer_fees() { let token_mint_address = mint_account.pubkey(); let mint_authority = Keypair::new(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeConfig]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeConfig]) + .unwrap(); let maximum_fee = 100; let mut transaction = Transaction::new_with_payer( &[ diff --git a/associated-token-account/program-test/tests/process_create_associated_token_account.rs b/associated-token-account/program-test/tests/process_create_associated_token_account.rs index dc2991fdc84..c3cc488499c 100644 --- a/associated-token-account/program-test/tests/process_create_associated_token_account.rs +++ b/associated-token-account/program-test/tests/process_create_associated_token_account.rs @@ -32,7 +32,8 @@ async fn test_associated_token_address() { let rent = banks_client.get_rent().await.unwrap(); let expected_token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let expected_token_account_balance = rent.minimum_balance(expected_token_account_len); // Associated account does not exist @@ -81,7 +82,8 @@ async fn test_create_with_fewer_lamports() { program_test_2022(token_mint_address, true).start().await; let rent = banks_client.get_rent().await.unwrap(); let expected_token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let expected_token_account_balance = rent.minimum_balance(expected_token_account_len); // Transfer lamports into `associated_token_address` before creating it - enough to be @@ -142,7 +144,8 @@ async fn test_create_with_excess_lamports() { let rent = banks_client.get_rent().await.unwrap(); let expected_token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let expected_token_account_balance = rent.minimum_balance(expected_token_account_len); // Transfer 1 lamport into `associated_token_address` before creating it @@ -272,7 +275,8 @@ async fn test_create_associated_token_account_using_legacy_implicit_instruction( program_test_2022(token_mint_address, true).start().await; let rent = banks_client.get_rent().await.unwrap(); let expected_token_account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap(); let expected_token_account_balance = rent.minimum_balance(expected_token_account_len); // Associated account does not exist diff --git a/associated-token-account/program-test/tests/program_test.rs b/associated-token-account/program-test/tests/program_test.rs index 3238d2176d2..64760d66009 100644 --- a/associated-token-account/program-test/tests/program_test.rs +++ b/associated-token-account/program-test/tests/program_test.rs @@ -36,7 +36,7 @@ pub fn program_test(token_mint_address: Pubkey, use_latest_spl_token: bool) -> P ); // Dial down the BPF compute budget to detect if the program gets bloated in the future - pc.set_compute_max_units(50_000); + pc.set_compute_max_units(60_000); pc } diff --git a/associated-token-account/program-test/tests/recover_nested.rs b/associated-token-account/program-test/tests/recover_nested.rs index f2d59bea779..db1f947232e 100644 --- a/associated-token-account/program-test/tests/recover_nested.rs +++ b/associated-token-account/program-test/tests/recover_nested.rs @@ -24,7 +24,7 @@ async fn create_mint(context: &mut ProgramTestContext, program_id: &Pubkey) -> ( let mint_account = Keypair::new(); let token_mint_address = mint_account.pubkey(); let mint_authority = Keypair::new(); - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let rent = context.banks_client.get_rent().await.unwrap(); let transaction = Transaction::new_signed_with_payer( &[ diff --git a/associated-token-account/program/Cargo.toml b/associated-token-account/program/Cargo.toml index 40fb615b583..ef461ccdc84 100644 --- a/associated-token-account/program/Cargo.toml +++ b/associated-token-account/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-associated-token-account" -version = "2.0.0" +version = "2.2.0" description = "Solana Program Library Associated Token Account" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -16,9 +16,9 @@ assert_matches = "1.5.0" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } thiserror = "1.0" [lib] diff --git a/binary-option/client/requirements.txt b/binary-option/client/requirements.txt index 65a36526de5..390b59ccd8d 100644 --- a/binary-option/client/requirements.txt +++ b/binary-option/client/requirements.txt @@ -1,12 +1,12 @@ attrs==21.2.0 base58==2.1.0 -certifi==2022.12.7 +certifi==2023.7.22 cffi==1.14.5 chardet==4.0.0 cheroot==8.5.2 CherryPy==18.6.0 construct==2.10.67 -cryptography==41.0.0 +cryptography==41.0.4 ed25519==1.5 idna==2.10 iniconfig==1.1.1 @@ -33,6 +33,6 @@ solana==0.10.0 tempora==4.1.1 toml==0.10.2 typing-extensions==3.10.0.0 -urllib3==1.26.6 +urllib3==1.26.18 websockets==9.1 zc.lockfile==2.0 diff --git a/binary-option/program/Cargo.toml b/binary-option/program/Cargo.toml index 91c7c9fa42f..b649b5e91ec 100644 --- a/binary-option/program/Cargo.toml +++ b/binary-option/program/Cargo.toml @@ -9,7 +9,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" thiserror = "1.0" spl-token = {version = "4.0", path = "../../token/program", features = ["no-entrypoint"]} arrayref = "0.3.7" diff --git a/binary-option/program/src/lib.rs b/binary-option/program/src/lib.rs index c7060a98f25..9c104343293 100644 --- a/binary-option/program/src/lib.rs +++ b/binary-option/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] pub mod entrypoint; pub mod error; pub mod instruction; diff --git a/binary-oracle-pair/program/Cargo.toml b/binary-oracle-pair/program/Cargo.toml index 9f469598cc0..bb2f93aeebb 100644 --- a/binary-oracle-pair/program/Cargo.toml +++ b/binary-oracle-pair/program/Cargo.toml @@ -13,15 +13,15 @@ test-sbf = [] [dependencies] num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } thiserror = "1.0" uint = "0.9" borsh = "0.10" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/ci/anchor-build.sh b/ci/anchor-build.sh new file mode 100755 index 00000000000..e30a0319a0c --- /dev/null +++ b/ci/anchor-build.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e + +source ci/rust-version.sh stable +source ci/solana-version.sh install +source ci/install-anchor.sh install + +set -x + +usage() { + exitcode=0 + if [[ -n "$1" ]]; then + exitcode=1 + echo "Error: $*" + fi + echo "Usage: $0 [program-directory]" + exit $exitcode +} + +program_directory=$1 +if [[ -z $program_directory ]]; then + usage "No program directory provided" +fi + +cd $program_directory +anchor build \ No newline at end of file diff --git a/ci/cargo-build-test.sh b/ci/cargo-build-test.sh index 49addd469f6..fa930f4e92d 100755 --- a/ci/cargo-build-test.sh +++ b/ci/cargo-build-test.sh @@ -15,8 +15,8 @@ set -x make -C examples/c # Build/test all host crates -cargo +"$rust_stable" build --workspace --exclude spl-token-cli --exclude spl-token-upgrade-cli -cargo +"$rust_stable" test --workspace --exclude spl-token-cli --exclude spl-token-upgrade-cli -- --nocapture +cargo +"$rust_stable" build --workspace --exclude spl-token-cli --exclude spl-token-upgrade-cli --exclude spl-single-pool-cli --exclude spl-transfer-hook-cli +cargo +"$rust_stable" test --workspace --exclude spl-token-cli --exclude spl-token-upgrade-cli --exclude spl-single-pool-cli --exclude spl-transfer-hook-cli -- --nocapture # Run test-client sanity check cargo +"$rust_stable" run --manifest-path=utils/test-client/Cargo.toml diff --git a/ci/do-audit.sh b/ci/do-audit.sh index e710333024e..f86ef5c8236 100755 --- a/ci/do-audit.sh +++ b/ci/do-audit.sh @@ -16,9 +16,19 @@ cargo_audit_ignores=( # https://github.com/solana-labs/solana/issues/29586 --ignore RUSTSEC-2023-0001 - # openssl: `openssl` `X509VerifyParamRef::set_host` buffer over-read + # ed25519-dalek: Double Public Key Signing Function Oracle Attack # - # Remove once SPL upgrades to Solana v1.16.2 or greater - --ignore RUSTSEC-2023-0044 + # Remove once SPL upgrades to Solana v1.17 or greater + --ignore RUSTSEC-2022-0093 + + # webpki: CPU denial of service in certificate path building + # + # No fixed upgrade is available! Only fix is switching to rustls-webpki + --ignore RUSTSEC-2023-0052 + + # tungstenite + # + # Remove once SPL upgrades to Solana v1.17 or greater + --ignore RUSTSEC-2023-0065 ) cargo +"$rust_stable" audit "${cargo_audit_ignores[@]}" diff --git a/ci/install-anchor.sh b/ci/install-anchor.sh new file mode 100755 index 00000000000..1ec3711a447 --- /dev/null +++ b/ci/install-anchor.sh @@ -0,0 +1,32 @@ +# +# This file maintains the solana versions for use by CI. +# +# Obtain the environment variables without any automatic updating: +# $ source ci/install-anchor.sh +# +# Obtain the environment variables and install update: +# $ source ci/install-anchor.sh install + +# Then to access the anchor version: +# $ echo "$anchor_cli_version" +# + +if [[ -n $ANCHOR_CLI_VERSION ]]; then + anchor_cli_version="$ANCHOR_CLI_VERSION" +else + anchor_cli_version=v0.29.0 +fi + +export anchor_cli_version="$anchor_cli_version" + +if [[ -n $1 ]]; then + case $1 in + install) + cargo install --git https://github.com/coral-xyz/anchor --tag $anchor_cli_version anchor-cli --locked + anchor --version + ;; + *) + echo "anchor-version.sh: Note: ignoring unknown argument: $1" >&2 + ;; + esac +fi diff --git a/ci/install-build-deps.sh b/ci/install-build-deps.sh index aed837db5b6..e1bcdc556bd 100755 --- a/ci/install-build-deps.sh +++ b/ci/install-build-deps.sh @@ -6,3 +6,4 @@ sudo apt update sudo apt install libudev-dev -y sudo apt install binutils-dev -y sudo apt install libunwind-dev -y +sudo apt install protobuf-compiler -y diff --git a/ci/js-test-account-compression.sh b/ci/js-test-account-compression.sh new file mode 100755 index 00000000000..57b1ac433e1 --- /dev/null +++ b/ci/js-test-account-compression.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +cd "$(dirname "$0")/.." +source ./ci/solana-version.sh install + +set -x +cd account-compression/sdk + +yarn install --pure-lockfile +yarn build +yarn test diff --git a/ci/js-test-single-pool.sh b/ci/js-test-single-pool.sh new file mode 100755 index 00000000000..34f94777e1f --- /dev/null +++ b/ci/js-test-single-pool.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -ex +cd "$(dirname "$0")/.." +source ./ci/solana-version.sh install + +cd single-pool/js +pnpm install + +cd packages/modern +pnpm run lint +pnpm build + +cd ../classic +pnpm run lint +pnpm build +pnpm test diff --git a/ci/js-test-tlv.sh b/ci/js-test-tlv.sh new file mode 100755 index 00000000000..a32aa84922a --- /dev/null +++ b/ci/js-test-tlv.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e +cd "$(dirname "$0")/.." +source ./ci/solana-version.sh install + +set -x +cd libraries/type-length-value/js + +npm install +npm run lint +npm run build +npm test diff --git a/ci/js-test-token-metadata.sh b/ci/js-test-token-metadata.sh new file mode 100755 index 00000000000..446e06d436b --- /dev/null +++ b/ci/js-test-token-metadata.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e +cd "$(dirname "$0")/.." +source ./ci/solana-version.sh install + +set -x +cd token-metadata/js + +npm install +npm run lint +npm run build +npm test diff --git a/ci/rust-version.sh b/ci/rust-version.sh index 7c39cc1e78c..9282c3494bb 100644 --- a/ci/rust-version.sh +++ b/ci/rust-version.sh @@ -26,7 +26,7 @@ fi if [[ -n $RUST_NIGHTLY_VERSION ]]; then nightly_version="$RUST_NIGHTLY_VERSION" else - nightly_version=2023-04-19 + nightly_version=2023-10-05 fi export rust_stable="$stable_version" diff --git a/ci/solana-version.sh b/ci/solana-version.sh index 3f0b5b44d90..a78996fa9e5 100755 --- a/ci/solana-version.sh +++ b/ci/solana-version.sh @@ -14,7 +14,7 @@ if [[ -n $SOLANA_VERSION ]]; then solana_version="$SOLANA_VERSION" else - solana_version=v1.16.1 + solana_version=v1.17.2 fi export solana_version="$solana_version" diff --git a/ci/warning/purge-ubuntu-runner.sh b/ci/warning/purge-ubuntu-runner.sh index 20524176a43..bcdf9073320 100644 --- a/ci/warning/purge-ubuntu-runner.sh +++ b/ci/warning/purge-ubuntu-runner.sh @@ -6,35 +6,22 @@ # ONLY RUNS IN CI. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if [[ -n "$CI" ]]; then - # Clears 4GB - sudo docker rmi $(docker image ls -aq) - # Clears 12GB + set -e + + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/share/swift + sudo rm -rf /usr/share/mysql + sudo rm -rf /usr/share/az_* + sudo rm -rf /usr/share/postgresql-common + + sudo rm -rf /opt/ghc + sudo rm -rf /opt/az + sudo rm -rf /opt/pipx + sudo rm -rf /opt/microsoft + sudo rm -rf /opt/google + sudo rm -rf /opt/hostedtoolcache + sudo rm -rf /usr/local/lib/android - # Clears 6GB - #sudo apt purge aria2 \ - # ansible \ - # azure-cli \ - # xorriso \ - # dotnet-sdk-* \ - # firefox \ - # g++-9 \ - # gfortran-9 \ - # google-chrome-stable google-cloud-sdk \ - # ant ant-optional \ - # mercurial \ - # mono-complete \ - # mysql-client libmysqlclient-dev mysql-server \ - # mssql-tools unixodbc-dev libxft-dev \ - # libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev \ - # nginx \ - # shim-signed \ - # php* \ - # libpq-dev postgresql-client \ - # powershell \ - # ruby-full \ - # sphinxsearch \ - # subversion \ - # -yq --allow-remove-essential - #sudo apt autopurge -y - #sudo apt autoclean -y + sudo rm -rf /usr/local/lib/heroku + sudo rm -rf /imagegeneration fi diff --git a/docs/package-lock.json b/docs/package-lock.json index 532ca8c1e22..0e29edab819 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -195,11 +195,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -243,19 +244,19 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -313,9 +314,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -348,9 +349,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -372,9 +373,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -396,28 +397,28 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -549,9 +550,9 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { "@babel/types": "^7.22.5" }, @@ -568,9 +569,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -611,12 +612,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -624,9 +625,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1637,9 +1638,9 @@ } }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1885,9 +1886,9 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1973,31 +1974,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", - "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2006,12 +2007,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2275,20 +2276,6 @@ "react-router": ">=5" } }, - "node_modules/@docusaurus/core/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@docusaurus/core/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -3281,9 +3268,9 @@ } }, "node_modules/@mdx-js/mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "bin": { "semver": "bin/semver" } @@ -4585,9 +4572,9 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -5665,20 +5652,6 @@ "webpack": "^5.0.0" } }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/css-minimizer-webpack-plugin": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", @@ -6637,9 +6610,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -9375,9 +9348,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -10104,9 +10077,9 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -10354,9 +10327,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -10536,20 +10509,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/postcss-merge-idents": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", @@ -11842,9 +11801,9 @@ } }, "node_modules/remark-mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "bin": { "semver": "bin/semver" } @@ -12299,9 +12258,9 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -12324,9 +12283,9 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -14626,9 +14585,9 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "engines": { "node": ">=0.10.0" } @@ -14938,11 +14897,12 @@ } }, "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -14973,18 +14933,18 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, "@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "requires": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -15027,9 +14987,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "yallist": { "version": "3.1.1", @@ -15055,9 +15015,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -15072,9 +15032,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -15092,24 +15052,24 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -15205,9 +15165,9 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "requires": { "@babel/types": "^7.22.5" } @@ -15218,9 +15178,9 @@ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.22.5", @@ -15249,19 +15209,19 @@ } }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.22.5", @@ -15886,9 +15846,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -16067,9 +16027,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -16133,39 +16093,39 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -16350,14 +16310,6 @@ "@babel/runtime": "^7.1.2" } }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -17093,9 +17045,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "source-map": { "version": "0.5.7", @@ -18109,9 +18061,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -18875,16 +18827,6 @@ "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.3.8" - }, - "dependencies": { - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "css-minimizer-webpack-plugin": { @@ -19650,9 +19592,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -21518,9 +21460,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -22018,9 +21960,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -22207,9 +22149,9 @@ } }, "postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -22310,14 +22252,6 @@ "requires": { "argparse": "^2.0.1" } - }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "requires": { - "lru-cache": "^6.0.0" - } } } }, @@ -23229,9 +23163,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "source-map": { "version": "0.5.7", @@ -23555,9 +23489,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -23571,9 +23505,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -25191,9 +25125,9 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==" }, "wrap-ansi": { "version": "7.0.0", diff --git a/docs/sidebars.js b/docs/sidebars.js index fdae41005bb..c8b4771e2c3 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -12,6 +12,7 @@ module.exports = { "token-2022/extensions", "token-2022/wallet", "token-2022/onchain", + "token-2022/presentation", ], }, "token-swap", @@ -33,6 +34,7 @@ module.exports = { "stake-pool/cli", ], }, + "single-pool", "feature-proposal", { type: "category", diff --git a/docs/src/account-compression/concepts.md b/docs/src/account-compression/concepts.md index 64483dca9cf..cd3014185ef 100644 --- a/docs/src/account-compression/concepts.md +++ b/docs/src/account-compression/concepts.md @@ -8,7 +8,7 @@ To understand concurrent merkle trees we must first briefly understand merkle tr ### Merkle Trees A merkle tree is a hash based data structure that encodes data into a tree. -The tree has nodes that are hashes of it's children and each leaf node is a hash of the data. +The tree has nodes that are hashes of its children and each leaf node is a hash of the data. Each node has a 256 bit (32 byte) string represented by Xi ∈ {0,1}^256 which is hashed using `H: {0, 1}^256 × {0, 1}^256 → {0, 1}^256`, meaning two child nodes with their 256 bit strings are hashed into one parent node with a 256 bit string. You can use any hash function that satisfies this property but we use SHA256. @@ -33,7 +33,7 @@ If you change X5 to X5' then you will have to recompute the root hash in the fol - X1' = H(X2',X3) ### Concurrent leaf replacement -We know that there can be multiple concurrent requests to write to the same state, however when the root changes while the first write is happening the second write will generate an invalid root, in other words everytime a root is modified all modifications in progress will be invalid. +We know that there can be multiple concurrent requests to write to the same state, however when the root changes while the first write is happening the second write will generate an invalid root, in other words every time a root is modified all modifications in progress will be invalid. ```txt X1' X1'' / \ / \ @@ -43,7 +43,7 @@ We know that there can be multiple concurrent requests to write to the same stat ``` In the above example let's say we try to modify `X5 -> X5'` and make another request to modify X6 -> X6''. For the first change we get root `X1'` computed using `X1' = H(H(X4,X5'),X3)`. For the second change we get root X1'' computed using `X1'' = H(H(X6'',X7),X2`). However `X1''` is not valid as `X1' != H(H(X6, X7), X2)` because the new root is actually `X1'`. -The reason this happens is because the change in the first trees path actualy changes the proofs required by the second trees change. To circumvent this problem we maintain a changelog of updates that have been made to the tree, so when `X5 -> X5'` the second mutation can actually use X2' instead of X2 which would compute to the correct root. +The reason this happens is because the change in the first trees path actually changes the proofs required by the second trees change. To circumvent this problem we maintain a changelog of updates that have been made to the tree, so when `X5 -> X5'` the second mutation can actually use X2' instead of X2 which would compute to the correct root. To swap the nodes when adding a new leaf in the second tree we do the following: - Take XOR of the leaf indices of the change log path and the new leaf in base 2 diff --git a/docs/src/account-compression/usage.md b/docs/src/account-compression/usage.md index a226498dd4a..c91046b2eb5 100644 --- a/docs/src/account-compression/usage.md +++ b/docs/src/account-compression/usage.md @@ -83,7 +83,7 @@ await sendAndConfirmTransaction(connection, tx, [payer]); 4. Replace a leaf in the tree, using a 3rd party indexer -This example assumes that some 3rd party service is indexing the the tree at `cmtKeypair.publicKey` for you, and providing MerkleProofs via some REST endpoint. +This example assumes that some 3rd party service is indexing the tree at `cmtKeypair.publicKey` for you, and providing MerkleProofs via some REST endpoint. The `getProofFromAnIndexer` function is a **placeholder** to exemplify this relationship. ```typescript @@ -109,4 +109,4 @@ Here are some examples using account compression in the wild: * Solana Program Library [tests](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/sdk/tests) -* Metaplex Program Library Compressed NFT [tests](https://github.com/metaplex-foundation/metaplex-program-library/tree/master/bubblegum/js/tests) \ No newline at end of file +* Metaplex Program Library Compressed NFT [tests](https://github.com/metaplex-foundation/metaplex-program-library/tree/master/bubblegum/js/tests) diff --git a/docs/src/confidential-token/deep-dive/overview.md b/docs/src/confidential-token/deep-dive/overview.md index 37d5bed0a66..1e7eefc5d9f 100644 --- a/docs/src/confidential-token/deep-dive/overview.md +++ b/docs/src/confidential-token/deep-dive/overview.md @@ -4,16 +4,16 @@ title: Protocol Overview In this section, we provide an overview of the underlying cryptographic protocol for the confidential Token extension. An understanding of the details that are -discussed in the following subsections are not needed to actually use the +discussed in the following subsections is not needed to actually use the confidential extension. We refer to the previous section for a quick start guide. We note that this overview exists mainly to provide the design intuition behind -the underlying cryptography that is used in the confidential extension. Some of +the underlying cryptography that is used in the confidential extension. Some parts of the description of the protocol in the overview could differ from the actual implementation. We refer to the subsequent subsections, the [source code](https://github.com/solana-labs/solana-program-library), and the -documentations within for the precise details of the underlying cryptography. +documentation within for the precise details of the underlying cryptography. ## Tokens with Encryption and Proofs @@ -188,7 +188,7 @@ token program itself, preventing it from verifying the validity of a transaction. To fix this, we require that transfer instructions include zero-knowledge proofs -that validate their correctness. Put simply, zero-knolwedge proofs consist of +that validate their correctness. Put simply, zero-knowledge proofs consist of two pair of algorithms `prove` and `verify` that work over public and private data. The `prove` algorithm generates a "proof" that certifies that some property of the public and private data is true. The `verify` algorithm checks @@ -238,7 +238,7 @@ zero-knowledge proofs. `lower_bound <= x < upper_bound`. In the confidential extension, we require that a transfer instruction includes - a range proof that certify the following: + a range proof that certifies the following: - The proof should certify that there are enough funds in the source account. Specifically, let `ct_source` be the encrypted balance of a source account @@ -299,7 +299,7 @@ decrypting transaction amounts allow for a more flexible interface. In a potential application, the decryption key for specific accounts can be shared among multiple users (e.g. regulators) that should have access to an account balance. Although these users can decrypt account balances, only the -owner of the account that have access to the owner signing key can sign a +owner of the account that has access to the owner signing key can sign a transaction that initiates a transfer of tokens. The owner of an account can update the account with a new encryption key using the `ConfigureAccount`. @@ -343,13 +343,13 @@ One way an attacker can disrupt the use of a confidential extension account is by using _front-running_. Zero-knowledge proofs are verified with respect to the encrypted balance of an account. Suppose that a user Alice generates a proof with respect to her current encrypted account balance. If another user Bob -transfers some tokesn to Alice, and Bob's transaction is processed first, then +transfers some tokens to Alice, and Bob's transaction is processed first, then Alice's transaction will be rejected by the Token program as the proof will not verify with respect to the newly updated account state. Under normal conditions, upon a rejection by the program, Alice can simply look up the newly updated ciphertext and submit a new transaction. However, if a -malicious attacker continuously flods the network with a transfer to Alice's +malicious attacker continuously floods the network with a transfer to Alice's account, then the account may theoretically become unusable. To prevent this type of attack, we modify the account data structure such that the encrypted balance of an account is divided into two separate components: the _pending_ diff --git a/docs/src/confidential-token/deep-dive/zkps.md b/docs/src/confidential-token/deep-dive/zkps.md index 16df6777953..9414baeac69 100644 --- a/docs/src/confidential-token/deep-dive/zkps.md +++ b/docs/src/confidential-token/deep-dive/zkps.md @@ -177,7 +177,7 @@ components associated with fees. If a mint is extended for fees, then transfers of tokens that pertains to the mint requires a transfer fee that is calculated as a percentage of the transfer -amount. Specifically, a transaction fee is determined by two paramters: +amount. Specifically, a transaction fee is determined by two parameters: - `bp`: The base point representing the fee rate. It is a positive integer that represents a percentage rate that is two points to the right of the decimal @@ -284,7 +284,7 @@ the following notes. [[Notes]](./validity_proof.pdf) -Validity proofs is required for the `Withdraw`, `Transfer`, and +Validity proofs are required for the `Withdraw`, `Transfer`, and `TransferWithFee` instructions. These instructions require the client to include twisted ElGamal ciphertexts as part of the instruction data. Validity proofs that are attached with these instructions certify that these ElGamal ciphertexts @@ -318,7 +318,7 @@ specified in the following notes. [[Notes]](./equality_proof.pdf). Ciphertext-commitment equality proofs are required for the `Transfer` and -`TransferWithFee` instructions. Ciphertext-ciphertext equaltiy proofs are +`TransferWithFee` instructions. Ciphertext-ciphertext equality proofs are required for the `WithdrawWithheldTokensFromMint` and `WithdrawWithheldTokensFromAccounts` instructions. diff --git a/docs/src/single-pool.md b/docs/src/single-pool.md new file mode 100644 index 00000000000..6d6202cc463 --- /dev/null +++ b/docs/src/single-pool.md @@ -0,0 +1,29 @@ +--- +title: Single-Validator Stake Pool +--- + +Trustless liquid staking for all Solana validators. + +## Overview + +The single-validator stake pool program is an upcoming SPL program that enables liquid staking with zero fees, no counterparty, and 100% capital efficiency. + +The program defines a canonical pool for every vote account, which can be initialized permissionlessly, and mints tokens in exchange for stake delegated to its designated validator. + +The program is a stripped-down adaptation of the existing multi-validator stake pool program, with approximately 80% less code, to minimize execution risk. + +## Source + +The Single Pool Program's source is available on +[GitHub](https://github.com/solana-labs/solana-program-library/tree/master/single-pool/program). + +## Security Audits + +The Single Pool Program has received two audits to ensure total safety of funds: + +* Neodyme (2023-08-08) + - Review commit hash [`735d729`](https://github.com/solana-labs/solana-program-library/commit/735d7292e35d35101750a4452d2647bdbf848e8b) + - Final report https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeSinglePoolAudit-2023-08-08.pdf +* Zellic (2023-06-21) + - Review commit hash [`9dbdc3b`](https://github.com/solana-labs/solana-program-library/commit/9dbdc3bdae31dda1dcb35346aab2d879deecf194) + - Final report https://github.com/solana-labs/security-audits/blob/master/spl/ZellicSinglePoolAudit-2023-06-21.pdf diff --git a/docs/src/stake-pool/cli.md b/docs/src/stake-pool/cli.md index fbd95c2b7bc..f9cdef997c8 100644 --- a/docs/src/stake-pool/cli.md +++ b/docs/src/stake-pool/cli.md @@ -178,7 +178,8 @@ with epoch 102, the manager will earn 10%. Additionally, to prevent a malicious manager from immediately setting the withdrawal fee to a very high amount, making it practically impossible for users to withdraw, -the stake pool program currently enforces a limit of 1.5x increase per epoch. +the stake pool program currently enforces a limit of 1.5x increase every two +epoch boundaries. For example, if the current withdrawal fee is 2.5%, the maximum settable fee is 3.75%, and will take effect after two epoch boundaries. diff --git a/docs/src/stake-pool/fees.md b/docs/src/stake-pool/fees.md index bb5c3abeca2..2ab84660a29 100644 --- a/docs/src/stake-pool/fees.md +++ b/docs/src/stake-pool/fees.md @@ -22,6 +22,10 @@ and the stake pool charges 2%, and a stake in the pool earns 100 SOL pre-commiss then that stake will actually enrich the pool by 90.16 SOL. The total rewards on that validator will be reduced by ~9.84%. +When the epoch fee is updated, the change only takes effect after two epoch +boundaries. For example, if you update the epoch fee during epoch 100, the new +fee will only be used starting in epoch 102. + ### SOL Withdraw Fee Sends a proportion of the desired withdrawal amount to the manager. @@ -30,6 +34,14 @@ For example, if a user wishes to withdraw 100 pool tokens, and the fee is set to 3%, 3 pool tokens go to the manager, and the remaining 97 tokens are converted to SOL and sent to the user. +When the SOL withdrawal fee is updated, the change only takes effect after two +epoch boundaries. For example, if you update the fee during epoch 100, the +new fee will only be used starting in epoch 102. + +Also, the fee increase is limited to 1.5x the current fee. For example, if the +current fee is 2.5%, the maximum settable fee is 3.75%, which will take effect +after two epoch boundaries. + ### Stake Withdraw Fee Sends a proportion of the desired withdrawal amount to the manager before @@ -39,6 +51,14 @@ For example, if a user wishes to withdraw 100 pool tokens, and the fee is set to 0.5%, 0.5 pool tokens go to the manager, and the remaining 99.5 tokens are converted to SOL then sent to the user as an activated stake account. +When the stake withdrawal fee is updated, the change only takes effect after two +epoch boundaries. For example, if you update the fee during epoch 100, the new +fee will only be used starting in epoch 102. + +Also, the fee increase is limited to 1.5x the current fee. For example, if the +current fee is 2.5%, the maximum settable fee is 3.75%, which will take effect +after two epoch boundaries. + ### SOL Deposit Fee Converts the entire SOL deposit into pool tokens, then sends a proportion of diff --git a/docs/src/stake-pool/overview.md b/docs/src/stake-pool/overview.md index 129b6055682..a576b959496 100644 --- a/docs/src/stake-pool/overview.md +++ b/docs/src/stake-pool/overview.md @@ -31,7 +31,7 @@ document are available at: This document is intended for the main actors of the stake pool system: * manager: creates and manages the stake pool, earns fees, can update the fee, staker, and manager -* staker: adds and removes validators to the pool, rebalances stake among validators +* staker: adds and removes validators to the pool, rebalances stake among validators, can update the staker * user: provides liquid or staked SOL into an existing stake pool In its current iteration, the stake pool accepts active stakes or SOL, so diff --git a/docs/src/token-2022.md b/docs/src/token-2022.md index 58babf78b18..d101b2d7d70 100644 --- a/docs/src/token-2022.md +++ b/docs/src/token-2022.md @@ -94,12 +94,17 @@ Mint extensions currently include: * closing mint * interest-bearing tokens * non-transferable tokens +* permanent delegate +* transfer hook +* metadata pointer +* metadata Account extensions currently include: * memo required on incoming transfers * immutable ownership * default account state +* CPI guard Extensions can be mixed and matched, which means it's possible to create a mint with only transfer fees, only interest-bearing tokens, both, or neither! @@ -118,6 +123,7 @@ To get started with Token-2022: - [Extension Guide](token-2022/extensions.mdx) - [Wallet Guide](token-2022/wallet.md) - [On-Chain Program Guide](token-2022/onchain.md) +- [Presentation about Token-2022](token-2022/presentation.md) For existing functionality in the Token Program, see the [token docs](token.mdx). The Token functionality will always apply to Token-2022. diff --git a/docs/src/token-2022/extensions.mdx b/docs/src/token-2022/extensions.mdx index c4e91b30a02..204d6dd7590 100644 --- a/docs/src/token-2022/extensions.mdx +++ b/docs/src/token-2022/extensions.mdx @@ -1336,41 +1336,40 @@ at a well-defined program-derived address for that mint and program id. This call happens after all other transfer logic, so the accounts reflect the *end* state of the transfer. +When interacting with a transfer-hook program, it's possible to send an +instruction - such as `Execute` (transfer) - to the program with only the +accounts required for the `Transfer` instruction, and any extra accounts that +the program may require are automatically resolved on-chain! This process is +explained in detail in many of the linked `README` files below under +**Resources**. + #### Resources The interface description and structs exist at -[spl-transfer-hook-interface](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface), -along with a sample minimal program implementation. - -A usable example program exists at -[spl-transfer-hook-example](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-example). -Token-2022 uses this example program in tests to ensure that it properly uses -the transfer hook interface. - -#### How to use - -Developers must implement the `Execute` instruction, and optionally the -`InitializeExtraAccountMetas` instruction to write the required additional account -pubkeys into the program-derived address defined by the mint and program id. - -Note: it's technically not required to implement `InitializeExtraAccountMetas` -at that instruction descriminator. Your program may implement multiple interfaces, -so any other instruction in your program can create the account at the program-derived -address! - -More information in the -[spl-transfer-hook-interface README](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface/README.md). - -#### Utilities +[spl-transfer-hook-interface](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface), +along with a sample minimal program implementation. You can find detailed +instructions on how to implement this interface for an on-chain program or +interact with a program that implements transfer-hook in the repository's +[README](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface/README.md). The `spl-transfer-hook-interface` library provides offchain and onchain helpers for resolving the additional accounts required. See -[invoke.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface/src/invoke.rs) +[invoke.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface/src/invoke.rs) for usage on-chain, and -[offchain.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface/src/offchain.rs) +[offchain.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface/src/offchain.rs) for fetching the additional required account metas with any async off-chain client like `BanksClient` or `RpcClient`. +A usable example program exists at +[spl-transfer-hook-example](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/example). +Token-2022 uses this example program in tests to ensure that it properly uses +the transfer hook interface. + +The example program and the interface are powered by the +[spl-tlv-account-resolution](https://github.com/solana-labs/solana-program-library/tree/master/libraries/tlv-account-resolution) +library, which is explained in detail in the repository's +[README](https://github.com/solana-labs/solana-program-library/tree/master/libraries/tlv-account-resolution/README.md) + #### Example: Create a permissioned-transfer mint @@ -1389,7 +1388,60 @@ Signature: 3ug4Ejs16jJgEm1WyBwDDxzh9xqPzQ3a2cmy1hSYiPFcLQi9U12HYF1Dbhzb2bx75SSyd -Coming soon! +```jsx +import { + clusterApiUrl, + sendAndConfirmTransaction, + Connection, + Keypair, + PublicKey, + SystemProgram, + Transaction, + LAMPORTS_PER_SOL, +} from '@solana/web3.js'; + +import { + ExtensionType, + createInitializeMintInstruction, + createInitializeTransferHookInstruction, + mintTo, + createAccount, + getMintLen, + TOKEN_2022_PROGRAM_ID, +} from '../src'; + +(async () => { + const payer = Keypair.generate(); + + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const mint = mintKeypair.publicKey; + + const extensions = [ExtensionType.TransferHook]; + const mintLen = getMintLen(extensions); + const decimals = 9; + const transferHookProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj') + + const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); + + const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL); + await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) }); + + const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + space: mintLen, + lamports: mintLamports, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookProgramId, TOKEN_2022_PROGRAM_ID), + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + ); + await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); +})(); +``` @@ -1408,6 +1460,143 @@ Signature: 3Ffw6yjseDsL3Az5n2LjdwXXwVPYxDF3JUU1JC1KGAEb1LE68S9VN4ebtAyvKeYMHvhjd +```js +await updateTransferHook( + connection, + payer, mint, + newTransferHookProgramId, + payer.publicKey, + [], + undefined, + TOKEN_2022_PROGRAM_ID +); +``` + + + + +### Metadata Pointer + +With the potential proliferation of multiple metadata programs, a mint can have +multiple different accounts all claiming to describe the mint. + +To make it easy for clients to distinguish, the metadata pointer extension allows +a token creator to designate an address that describes the canonical metadata. +As you'll see in the "Metadata" section, this address can be the mint itself! + +To avoid phony mints claiming to be stablecoins, however, a client must check +that the mint and the metadata both point to each other. + +#### Example: Create a mint with a metadata pointer to an external account + + + + +```console +$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --metadata-address 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj +Creating token HFg1FFaj4PqFHmkYrqbZsarNJEZT436aXAXgQFMJihwc under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb + +Address: HFg1FFaj4PqFHmkYrqbZsarNJEZT436aXAXgQFMJihwc +Decimals: 9 + +Signature: 3ug4Ejs16jJgEm1WyBwDDxzh9xqPzQ3a2cmy1hSYiPFcLQi9U12HYF1Dbhzb2bx75SSydfU6W4e11dGUXaPbJqVc +``` + + + + +Coming soon! + + + + +### Metadata + +To facilitate token-metadata usage, Token-2022 allows a mint creator to include +their token's metadata directly in the mint account. + +Token-2022 implements all of the instructions from the +[spl-token-metadata-interface](https://github.com/solana-labs/solana-program-library/tree/master/token-metadata/interface). + +The metadata extension should work directly with the metadata-pointer extension. +During mint creation, you should also add the metadata-pointer extension, pointed +at the mint itself. + +The tools do this for you automatically. + +#### Example: Create a mint with metadata + + + + +```console +$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-metadata +Creating token 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb +To initialize metadata inside the mint, please run `spl-token initialize-metadata 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS `, and sign with the mint authority + +Address: 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS +Decimals: 9 + +Signature: 2BZH8KE7zVcBj7Mmnu6uCM9NT4ey7qHasZmEk6Bt3tyx1wKCXS3JtcgEvrXXEMFB5numQgA9wvR67o2Z4YQdEw7m + +$ spl-token initialize-metadata 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS MyTokenName TOKEN http://my.token --update-authority 3pGiHDDek35npQuyWQ7FGcWxqJdHvVPDHDDmBFs2YxQj +Signature: 2H16XtBqdwSbvvq8g5o2jhy4TknP6zgt71KHawEdyPvNuvusQrV4dPccUrMqjFeNTbk75AtzmzUVueH3yWiTjBCG +``` + + + + +Coming soon! + + + + +#### Example: Update a field + + + + +```console +$ spl-token update-metadata 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS name YourToken +Signature: 2H16XtBqdwSbvvq8g5o2jhy4TknP6zgt71KHawEdyPvNuvusQrV4dPccUrMqjFeNTbk75AtzmzUVueH3yWiTjBCG +``` + + + +Coming soon! + + + + +#### Example: Add a custom field + + + + +```console +$ spl-token update-metadata 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS new-field new-value +Signature: 31uerYNa6yhb21k5CCX69k7RLUKEhJEV99UadEpPnZtWWpykwr7vkTFkuFeJ7AaEyQPrepe8m8xr4N23JEAeuTRY +``` + + + +Coming soon! + + + + +#### Example: Remove a custom field + + + + +```console +$ spl-token update-metadata 5K8RVdjpY3CHujyKjQ7RkyiCJqTG8Kba9krNfpZnmvpS new-field --remove +Signature: 52s1mxRqnr2jcZNvcmcgsQuXfVyT2w1TuRsEE3J6YwEZBu74BbFcHh2DvwnJG7qC7Cy6C5ZrTfnoPREFjFS7kXjF +``` + + + Coming soon! diff --git a/docs/src/token-2022/onchain.md b/docs/src/token-2022/onchain.md index e3c0af82090..bf750fb48b5 100644 --- a/docs/src/token-2022/onchain.md +++ b/docs/src/token-2022/onchain.md @@ -147,7 +147,7 @@ use spl_token_2022::{extension::ExtensionType, instruction::*, state::Mint}; use solana_sdk::{system_instruction, transaction::Transaction}; // Calculate the space required using the `ExtensionType` -let space = ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); +let space = ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); // get the Rent object and calculate the rent required let rent_required = rent.minimum_balance(space); @@ -175,7 +175,7 @@ use spl_token_2022::{extension::ExtensionType, instruction::*, state::Account}; use solana_sdk::{system_instruction, transaction::Transaction}; // Calculate the space required using the `ExtensionType` -let space = ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); +let space = ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); // get the Rent object and calculate the rent required let rent_required = rent.minimum_balance(space); @@ -498,15 +498,15 @@ extension to the token accounts. Instead of: ```rust -let mint_space = ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); -let account_space = ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); +let mint_space = ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); +let account_space = ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]).unwrap(); ``` We'll do: ```rust -let mint_space = ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority, ExtensionType::TransferFeeConfig]).unwrap(); -let account_space = ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner, ExtensionType::TransferFeeAmount]).unwrap(); +let mint_space = ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority, ExtensionType::TransferFeeConfig]).unwrap(); +let account_space = ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner, ExtensionType::TransferFeeAmount]).unwrap(); ``` And during initialization of the mint, we'll add in the instruction to initialize @@ -725,7 +725,7 @@ use spl_token_2022::{extension::ExtensionType, instruction::*, state::Mint}; use solana_sdk::{system_instruction, transaction::Transaction}; // Calculate the space required using the `ExtensionType` -let space = ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); +let space = ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); // get the Rent object and calculate the rent required let rent_required = rent.minimum_balance(space); diff --git a/docs/src/token-2022/presentation.md b/docs/src/token-2022/presentation.md new file mode 100644 index 00000000000..21382553177 --- /dev/null +++ b/docs/src/token-2022/presentation.md @@ -0,0 +1,330 @@ +--- +title: Presentation +--- + +- Why a new token program? +- What are extensions? +- How can I get you excited with an FAQ? + +--- + +### Why + +- SPL Token works and is battle-tested +- ...but it needs more protocol-level functionality, without impacting existing tokens +- Let's deploy a new and separate token program +- ...even though it's no longer 2022! + +--- + +### Wait, are you sure about this? + +- Adopting a separate token program is tricky +- ...but extremely valuable to the ecosystem +- Going from 1 to 2 is hard, but 2 to N is easy + +--- + +### Are you aware that it's 2023? + +Yes. + +--- + +### Ok... how does it work? + +- Token-2022 is a superset of Token: structures and instructions have the same ABI +- Opt-in to *extensions* on mints and accounts +- New data is written after the 165th byte + +--- + +### Cool, but can I even use this? + +- Yes! Out on all networks *for testing* +- `solana` tools version >= 1.14.17 +- `@solana/spl-token` version >= 0.3 +- `spl-token-cli` version >= 2.2 + +--- + +### Who supports it? + +The base is mostly there. + +- RPC indexes Token-2022 +- Anchor +- Wallets: Backpack, others coming +- DeFi Protocols: limited to spl-token-swap +- Metadata: Metaplex can't, so we're making our own + +--- + +### That's great! Is it safe? + +- 4 audits +- 1 more after WIP features +- Currently upgradeable +- Officially recommended after 1.16 on mainnet +- May be frozen ~6 months after that + +--- + +### I'll bite: what are the extensions for accounts? + +- Confidential transfers +- CPI guard +- Memo required on transfer +- Immutable ownership + +--- + +### Not bad, what are the extensions for mints? + +- Confidential transfers +- Transfer fees +- Closing mint +- Interest-bearing tokens +- Non-transferable tokens +- Default account state +- Permanent delegate +- Transfer-hook +- Metadata pointer + metadata (WIP) + +--- + +### Wow that's a lot! + +Yeah. + +--- + +### I don't get what they're for. + +Let's learn with a game! + +- Describe a token design +- Think about how to do it with Token-2022 +- I give the answer + +Hint: the answers are in the CLI docs at https://spl.solana.com/token-2022/extensions + +--- + +### Question 1 + +I heard about compressed NFTs, so how can I make a token that can be compressed, +decompressed, and recompressed with an off-chain merkle tree? + +--- + +### Answer 1 + +Create a mint with the close mint authority extension, so you can close and +re-open the mint account when the supply is 0. + +--- + +### Question 2 + +I want to send my token without anyone knowing how much I have or how much I transferred. + +--- + +### Answer 2 + +Add the confidential transfer extension to your mint! + +Although the first deposit is public, transfer amounts are encrypted and +validated through zero-knowledge proofs. + +* Used to require larger transaction sizes, but instead we're splitting +up the proofs! + +--- + +### Question 3 + +I run a stake pool / lending protocol, and I want the pool token amount to go up +over time to approximate the value of the token. + +--- + +### Answer 3 + +Create a mint with the interest-bearing extension, and have the protocol update +the interest rate every epoch. + +--- + +### Question 4 + +I'm creating a bank-like payments system, and I want to create legible monthly +statements for my clients. + +And I don't want them to get rugged by sketchy protocols. + +--- + +### Answer 4 + +Enforce that all client token accounts require memos on incoming transfers. +Clients can figure out the motive for all funds coming into their account. + +Also add the CPI guard extension, to force dapp transfers to go through a delegate. + +--- + +### Question 5 + +For my game, I only want players to hold my token, and I don't want them +to dump it on an exchange. + +--- + +### Answer 5 + +Create the mint with the default account state extension, set to `frozen`. Players +must go through your program or service to unfreeze their account. + +--- + +### Question 6 + +My DAO needs a privileged token for council members. + +I don't want them to sell or move the tokens, and the DAO must be able to +revoke the token if they behave poorly. + +--- + +### Answer 6 + +Create a mint with: +- permanent delegation to the DAO, so it can burn any token +- non-transferable, so members can't move them +- Bonus: non-transferable forces immutable ownership + +--- + +### Question 7 + +There's definitely a lot of new features, but I just want to program my own +token. + +--- + +### Answer 7 + +This isn't possible currently. We need to develop a suite of interfaces and move +everyone to using them. + +In the meantime, you can configure your token-2022 mint to call into a program that +implements the "transfer hook" interface. + +More info at https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface + +--- + +### Question 8 + +You mentioned something about metadata. Does this mean there's going to be more +than one metadata program? That sounds like chaos. + +--- + +### Answer 8 + +It could be! That's why the "metadata pointer" extension in token-2022 lets you +specify which account holds the metadata for your mint. + +For safety, you *must* make sure that the mint and metadata point at each other. + +--- + +### Question 9 + +Can't we just put the metadata in the mint? + +--- + +### Answer 9 + +Yes! With the WIP "metadata" extension, you just put everything in the mint. + +--- + +### Question 10 + +These features sound awesome, but I already have lots of token holders, +so how can I migrate them to Token-2022? + +--- + +### Answer 10 + +Create a new mint with Token-2022, and have them use the `spl-token-upgrade` +program to convert. + +- Stateless protocol with an escrow account +- Mint new tokens to the escrow +- Protocol burns old tokens and gives new tokens + +Fun fact: you can use this between any two mints! + +--- + +### Question 11 + +Yeah, hi, same as number 10, but I don't want to burn tokens. + +--- + +### Answer 11 + +That's fine! The WIP `token-wrap` program allows you to wrap between any two mints. + +Note: the default wrapping program does not add extensions, but can be forked +into a new program if you want to wrap your token with extensions. + +--- + +### Question 12 + +I have an on-chain program (smart contract), how can I add support for Token-2022? + +--- + +### Answer 12 + +That's awesome! If you only process one token in an instruction, it's easy. + +If you use multiple token programs at once (e.g. trading), it's trickier since +you need both programs in your instruction. + +Extensive docs and examples at https://spl.solana.com/token-2022/onchain + +--- + +### Question 13 + +I work on a wallet, so how can I show and transfer Token-2022 tokens? + +--- + +### Answer 13 + +Nice! It's pretty easy to add support. + +Docs and examples at https://spl.solana.com/token-2022/wallet + +--- + +### I'm a bit overwhelmed + +No problem, we're done, here are your links: + +- Token-2022: https://spl.solana.com/token-2022 +- Token-upgrade: https://spl.solana.com/token-upgrade + +Thanks for listening! diff --git a/docs/src/token-2022/status.md b/docs/src/token-2022/status.md index 40f1253c173..180ffe2eeb5 100644 --- a/docs/src/token-2022/status.md +++ b/docs/src/token-2022/status.md @@ -10,11 +10,11 @@ development purposes ONLY**. Here is the general program timeline and rough ETAs: -| Issue | ETA | -| --- | --- | -| Code-complete & final audit | Summer 2023 | -| Mainnet recommendation | Fall 2023 (depends on v1.16) | -| Freeze program | 2024 | +| Issue | ETA | +| --------------------------- | ---------------------------- | +| Code-complete & final audit | Summer 2023 | +| Mainnet recommendation | Fall 2023 (depends on v1.16) | +| Freeze program | 2024 | More information: https://github.com/orgs/solana-labs/projects/34 @@ -29,15 +29,17 @@ More information: https://github.com/solana-labs/solana/issues/29612 ### Zero-knowledge proof split -To fit within the current transaction size limits, the zero knowledge proofs must -be split into their component parts and uploaded to the chain through multiple -transactions. Once the proofs exist, the user can issue a transfer and clean up -the proofs. +In order to use confidential tokens, the cluster must run at least version 1.16 +with the ZK Token proof program enabled. + +More information: https://github.com/solana-labs/solana/pull/32613 -After splitting the proofs in the zero-knowledge token SDK, the token-2022 program -must properly consume the new proof format. +The ZK Token proof program was recently updated to support the splitting of +the longer zero-knowledge proofs into smaller components. The +token-2022 program is in the process of active development to properly process +the new proof format. -More information: https://github.com/solana-labs/solana/pull/30816 +More information: https://github.com/solana-labs/solana-program-library/issues/4817 ## Future work diff --git a/examples/rust/cross-program-invocation/Cargo.toml b/examples/rust/cross-program-invocation/Cargo.toml index 446574334d8..68822b46bbb 100644 --- a/examples/rust/cross-program-invocation/Cargo.toml +++ b/examples/rust/cross-program-invocation/Cargo.toml @@ -13,11 +13,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/examples/rust/custom-heap/Cargo.toml b/examples/rust/custom-heap/Cargo.toml index 13c6a24b7c1..1dccf426376 100644 --- a/examples/rust/custom-heap/Cargo.toml +++ b/examples/rust/custom-heap/Cargo.toml @@ -15,11 +15,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/examples/rust/logging/Cargo.toml b/examples/rust/logging/Cargo.toml index afe7992aaf3..7133d002bb4 100644 --- a/examples/rust/logging/Cargo.toml +++ b/examples/rust/logging/Cargo.toml @@ -13,11 +13,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/examples/rust/sysvar/Cargo.toml b/examples/rust/sysvar/Cargo.toml index 947310c69b4..381f89b5529 100644 --- a/examples/rust/sysvar/Cargo.toml +++ b/examples/rust/sysvar/Cargo.toml @@ -13,11 +13,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/examples/rust/transfer-lamports/Cargo.toml b/examples/rust/transfer-lamports/Cargo.toml index 336086a3b78..59cd303abda 100644 --- a/examples/rust/transfer-lamports/Cargo.toml +++ b/examples/rust/transfer-lamports/Cargo.toml @@ -12,11 +12,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/examples/rust/transfer-lamports/src/processor.rs b/examples/rust/transfer-lamports/src/processor.rs index 2a6eb8c638a..5c8bb94ef44 100644 --- a/examples/rust/transfer-lamports/src/processor.rs +++ b/examples/rust/transfer-lamports/src/processor.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! Program instruction processor use solana_program::{ diff --git a/examples/rust/transfer-tokens/Cargo.toml b/examples/rust/transfer-tokens/Cargo.toml index 88804df2425..69dd538d2a6 100644 --- a/examples/rust/transfer-tokens/Cargo.toml +++ b/examples/rust/transfer-tokens/Cargo.toml @@ -12,12 +12,12 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../../token/program", features = [ "no-entrypoint" ] } [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/feature-gate/README.md b/feature-gate/README.md new file mode 100644 index 00000000000..c0dbaf5787c --- /dev/null +++ b/feature-gate/README.md @@ -0,0 +1,9 @@ +# Feature Gate Program + +This program serves to manage new features on Solana. + +It serves one purpose: revoking features that are pending activation. + +More information & documentation will follow as this program matures, but you +can follow the discussions +[here](https://github.com/solana-labs/solana/issues/32780)! diff --git a/feature-gate/program/Cargo.toml b/feature-gate/program/Cargo.toml new file mode 100644 index 00000000000..6ae1b5245af --- /dev/null +++ b/feature-gate/program/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "spl-feature-gate" +version = "0.1.0" +description = "Solana Program Library Feature Gate Program" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[features] +no-entrypoint = [] +test-sbf = [] + +[dependencies] +num_enum = "0.7.1" +solana-program = "1.17.2" +spl-program-error = { version = "0.3.0", path = "../../libraries/program-error" } + +[dev-dependencies] +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/feature-gate/program/src/entrypoint.rs b/feature-gate/program/src/entrypoint.rs new file mode 100644 index 00000000000..c261a918a42 --- /dev/null +++ b/feature-gate/program/src/entrypoint.rs @@ -0,0 +1,17 @@ +//! Program entrypoint + +use { + crate::processor, + solana_program::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, + }, +}; + +entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + input: &[u8], +) -> ProgramResult { + processor::process(program_id, accounts, input) +} diff --git a/feature-gate/program/src/error.rs b/feature-gate/program/src/error.rs new file mode 100644 index 00000000000..731c61213a4 --- /dev/null +++ b/feature-gate/program/src/error.rs @@ -0,0 +1,14 @@ +//! Program error types + +use spl_program_error::*; + +/// Program specific errors +#[spl_program_error] +pub enum FeatureGateError { + /// Operation overflowed + #[error("Operation overflowed")] + Overflow, + /// Feature already activated + #[error("Feature already activated")] + FeatureAlreadyActivated, +} diff --git a/feature-gate/program/src/instruction.rs b/feature-gate/program/src/instruction.rs new file mode 100644 index 00000000000..4fa73a514e1 --- /dev/null +++ b/feature-gate/program/src/instruction.rs @@ -0,0 +1,77 @@ +//! Program instructions + +use { + num_enum::{IntoPrimitive, TryFromPrimitive}, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, + }, +}; + +/// Feature Gate program instructions +#[derive(Clone, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum FeatureGateInstruction { + /// Revoke a pending feature activation. + /// + /// A "pending" feature activation is a feature account that has been + /// allocated and assigned, but hasn't yet been updated by the runtime + /// with an `activation_slot`. + /// + /// Features that _have_ been activated by the runtime cannot be revoked. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[w+s]` Feature account + /// 1. `[w]` Destination (for rent lamports) + RevokePendingActivation, +} +impl FeatureGateInstruction { + /// Unpacks a byte buffer into a + /// [FeatureGateInstruction](enum.FeatureGateInstruction.html). + pub fn unpack(input: &[u8]) -> Result { + if input.len() != 1 { + return Err(ProgramError::InvalidInstructionData); + } + Self::try_from(input[0]).map_err(|_| ProgramError::InvalidInstructionData) + } + + /// Packs a [FeatureGateInstruction](enum.FeatureGateInstruction.html) into + /// a byte buffer. + pub fn pack(&self) -> Vec { + vec![self.to_owned().into()] + } +} + +/// Creates a 'RevokePendingActivation' instruction. +pub fn revoke_pending_activation(feature_id: &Pubkey, destination: &Pubkey) -> Instruction { + let accounts = vec![ + AccountMeta::new(*feature_id, true), + AccountMeta::new(*destination, false), + ]; + + let data = FeatureGateInstruction::RevokePendingActivation.pack(); + + Instruction { + program_id: crate::id(), + accounts, + data, + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn test_pack_unpack(instruction: &FeatureGateInstruction) { + let packed = instruction.pack(); + let unpacked = FeatureGateInstruction::unpack(&packed).unwrap(); + assert_eq!(instruction, &unpacked); + } + + #[test] + fn test_pack_unpack_revoke_pending_activation() { + test_pack_unpack(&FeatureGateInstruction::RevokePendingActivation); + } +} diff --git a/feature-gate/program/src/lib.rs b/feature-gate/program/src/lib.rs new file mode 100644 index 00000000000..e6be6428a15 --- /dev/null +++ b/feature-gate/program/src/lib.rs @@ -0,0 +1,16 @@ +//! Feature Gate program + +#![deny(missing_docs)] +#![cfg_attr(not(test), forbid(unsafe_code))] + +#[cfg(not(feature = "no-entrypoint"))] +mod entrypoint; +pub mod error; +pub mod instruction; +pub mod processor; + +// Export current SDK types for downstream users building with a different SDK +// version +pub use solana_program; + +solana_program::declare_id!("Feature111111111111111111111111111111111111"); diff --git a/feature-gate/program/src/processor.rs b/feature-gate/program/src/processor.rs new file mode 100644 index 00000000000..574ad143071 --- /dev/null +++ b/feature-gate/program/src/processor.rs @@ -0,0 +1,62 @@ +//! Program state processor + +use { + crate::{error::FeatureGateError, instruction::FeatureGateInstruction}, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + feature::Feature, + msg, + program_error::ProgramError, + pubkey::Pubkey, + system_program, + }, +}; + +/// Processes a [RevokePendingActivation](enum.FeatureGateInstruction.html) +/// instruction. +pub fn process_revoke_pending_activation( + _program_id: &Pubkey, + accounts: &[AccountInfo], +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let feature_info = next_account_info(account_info_iter)?; + let destination_info = next_account_info(account_info_iter)?; + + if !feature_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + // This will also check the program ID + if Feature::from_account_info(feature_info)? + .activated_at + .is_some() + { + return Err(FeatureGateError::FeatureAlreadyActivated.into()); + } + + let new_destination_lamports = feature_info + .lamports() + .checked_add(destination_info.lamports()) + .ok_or::(FeatureGateError::Overflow.into())?; + + **feature_info.try_borrow_mut_lamports()? = 0; + **destination_info.try_borrow_mut_lamports()? = new_destination_lamports; + + feature_info.realloc(0, true)?; + feature_info.assign(&system_program::id()); + + Ok(()) +} + +/// Processes an [Instruction](enum.Instruction.html). +pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = FeatureGateInstruction::unpack(input)?; + match instruction { + FeatureGateInstruction::RevokePendingActivation => { + msg!("Instruction: RevokePendingActivation"); + process_revoke_pending_activation(program_id, accounts) + } + } +} diff --git a/feature-gate/program/tests/functional.rs b/feature-gate/program/tests/functional.rs new file mode 100644 index 00000000000..e586e7bc459 --- /dev/null +++ b/feature-gate/program/tests/functional.rs @@ -0,0 +1,146 @@ +#![cfg(feature = "test-sbf")] + +use { + solana_program::instruction::InstructionError, + solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext}, + solana_sdk::{ + account::Account as SolanaAccount, + feature::{activate_with_lamports, Feature}, + pubkey::Pubkey, + signature::{Keypair, Signer}, + transaction::{Transaction, TransactionError}, + }, + spl_feature_gate::{error::FeatureGateError, instruction::revoke_pending_activation}, +}; + +async fn setup_pending_feature( + context: &mut ProgramTestContext, + feature_keypair: &Keypair, + rent_lamports: u64, +) { + let transaction = Transaction::new_signed_with_payer( + &activate_with_lamports( + &feature_keypair.pubkey(), + &context.payer.pubkey(), + rent_lamports, + ), + Some(&context.payer.pubkey()), + &[&context.payer, feature_keypair], + context.last_blockhash, + ); + + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); +} + +#[tokio::test] +async fn test_revoke_pending_activation() { + let feature_keypair = Keypair::new(); + let destination = Pubkey::new_unique(); + let mock_active_feature_keypair = Keypair::new(); + + let mut program_test = ProgramTest::new( + "spl_feature_gate", + spl_feature_gate::id(), + processor!(spl_feature_gate::processor::process), + ); + + // Add a mock _active_ feature for testing later + program_test.add_account( + mock_active_feature_keypair.pubkey(), + SolanaAccount { + lamports: 500_000_000, + owner: spl_feature_gate::id(), + data: vec![ + 1, // `Some()` + 45, 0, 0, 0, 0, 0, 0, 0, // Random slot `u64` + ], + ..SolanaAccount::default() + }, + ); + + let mut context = program_test.start_with_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + let rent_lamports = rent.minimum_balance(Feature::size_of()); // For checking account balance later + + setup_pending_feature(&mut context, &feature_keypair, rent_lamports).await; + + // Fail: feature not signer + let mut revoke_ix = revoke_pending_activation(&feature_keypair.pubkey(), &destination); + revoke_ix.accounts[0].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[revoke_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + error, + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ); + + // Fail: feature is already active + let transaction = Transaction::new_signed_with_payer( + &[revoke_pending_activation( + &mock_active_feature_keypair.pubkey(), + &destination, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &mock_active_feature_keypair], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + error, + TransactionError::InstructionError( + 0, + InstructionError::Custom(FeatureGateError::FeatureAlreadyActivated as u32) + ) + ); + + // Success: Revoke a feature activation + let transaction = Transaction::new_signed_with_payer( + &[revoke_pending_activation( + &feature_keypair.pubkey(), + &destination, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &feature_keypair], + context.last_blockhash, + ); + + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Confirm feature account was closed and destination account received lamports + let feature_account = context + .banks_client + .get_account(feature_keypair.pubkey()) + .await + .unwrap(); + assert!(feature_account.is_none()); + let destination_account = context + .banks_client + .get_account(destination) + .await + .unwrap() + .unwrap(); + assert_eq!(destination_account.lamports, rent_lamports); +} diff --git a/feature-proposal/cli/Cargo.toml b/feature-proposal/cli/Cargo.toml index e77799b732b..428475c28b2 100644 --- a/feature-proposal/cli/Cargo.toml +++ b/feature-proposal/cli/Cargo.toml @@ -8,13 +8,13 @@ license = "Apache-2.0" edition = "2021" [dependencies] -chrono = "0.4.26" +chrono = "0.4.31" clap = "2.33.3" -solana-clap-utils = "1.16.1" -solana-cli-config = "1.16.1" -solana-client = "1.16.1" -solana-logger = "1.16.1" -solana-sdk = "1.16.1" +solana-clap-utils = "1.17.2" +solana-cli-config = "1.17.2" +solana-client = "1.17.2" +solana-logger = "1.17.2" +solana-sdk = "1.17.2" spl-feature-proposal = { version = "1.0", path = "../program", features = ["no-entrypoint"] } [[bin]] diff --git a/feature-proposal/cli/src/main.rs b/feature-proposal/cli/src/main.rs index 49046f52879..451cf7c4fe5 100644 --- a/feature-proposal/cli/src/main.rs +++ b/feature-proposal/cli/src/main.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use { chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}, clap::{ @@ -250,8 +250,8 @@ fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String { format!( "{} (UnixTimestamp: {})", match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) { - Some(ndt) => - DateTime::::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true), + Some(ndt) => DateTime::::from_naive_utc_and_offset(ndt, Utc) + .to_rfc3339_opts(SecondsFormat::Secs, true), None => "unknown".to_string(), }, unix_timestamp, diff --git a/feature-proposal/program/Cargo.toml b/feature-proposal/program/Cargo.toml index 63cd3ca44c8..eb278e1d982 100644 --- a/feature-proposal/program/Cargo.toml +++ b/feature-proposal/program/Cargo.toml @@ -13,12 +13,12 @@ test-sbf = [] [dependencies] borsh = "0.10" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/feature-proposal/program/src/instruction.rs b/feature-proposal/program/src/instruction.rs index bb3c6e61f16..b73c0c8c575 100644 --- a/feature-proposal/program/src/instruction.rs +++ b/feature-proposal/program/src/instruction.rs @@ -160,7 +160,7 @@ mod tests { fn test_get_packed_len() { assert_eq!( FeatureProposalInstruction::get_packed_len(), - solana_program::borsh::get_packed_len::() + solana_program::borsh0_10::get_packed_len::() ) } diff --git a/feature-proposal/program/src/state.rs b/feature-proposal/program/src/state.rs index f1c1cd259ee..559d0b12488 100644 --- a/feature-proposal/program/src/state.rs +++ b/feature-proposal/program/src/state.rs @@ -64,7 +64,7 @@ mod tests { fn test_get_packed_len() { assert_eq!( FeatureProposal::get_packed_len(), - solana_program::borsh::get_packed_len::() + solana_program::borsh0_10::get_packed_len::() ); } diff --git a/governance/addin-api/Cargo.toml b/governance/addin-api/Cargo.toml index bea8b1a2690..e86dd8d6679 100644 --- a/governance/addin-api/Cargo.toml +++ b/governance/addin-api/Cargo.toml @@ -10,4 +10,4 @@ edition = "2021" [dependencies] borsh = "0.10" spl-governance-tools= { version = "0.1.3", path ="../tools"} -solana-program = "1.16.1" +solana-program = "1.17.2" diff --git a/governance/addin-mock/program/Cargo.toml b/governance/addin-mock/program/Cargo.toml index 1b6a594b900..8e464c3bd67 100644 --- a/governance/addin-mock/program/Cargo.toml +++ b/governance/addin-mock/program/Cargo.toml @@ -17,9 +17,9 @@ bincode = "1.3.2" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../../token/program", features = [ "no-entrypoint" ] } spl-governance-addin-api= { version = "0.1.3", path ="../../addin-api"} spl-governance-tools= { version = "0.1.3", path ="../../tools"} @@ -28,9 +28,9 @@ thiserror = "1.0" [dev-dependencies] assert_matches = "1.5.0" -proptest = "1.2" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +proptest = "1.3" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" spl-governance-test-sdk = { version = "0.1.3", path ="../../test-sdk"} diff --git a/governance/chat/program/Cargo.toml b/governance/chat/program/Cargo.toml index 792c150a43c..4824686b38d 100644 --- a/governance/chat/program/Cargo.toml +++ b/governance/chat/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-governance-chat" -version = "0.2.7" +version = "0.2.8" description = "Solana Program Library Governance Chat Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -17,23 +17,27 @@ bincode = "1.3.2" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" -spl-token = { version = "4.0", path = "../../../token/program", features = [ "no-entrypoint" ] } -spl-governance= { version = "3.1.1", path ="../../program", features = [ "no-entrypoint" ]} -spl-governance-tools= { version = "0.1.3", path ="../../tools"} -spl-governance-addin-api= { version = "0.1.3", path ="../../addin-api"} +solana-program = "1.17.2" +spl-token = { version = "4.0", path = "../../../token/program", features = [ + "no-entrypoint", +] } +spl-governance = { version = "3.1.1", path = "../../program", features = [ + "no-entrypoint", +] } +spl-governance-tools = { version = "0.1.3", path = "../../tools" } +spl-governance-addin-api = { version = "0.1.3", path = "../../addin-api" } thiserror = "1.0" [dev-dependencies] assert_matches = "1.5.0" -proptest = "1.2" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -spl-governance-test-sdk = { version = "0.1.3", path ="../../test-sdk"} -spl-governance-addin-mock = { version = "0.1.3", path ="../../addin-mock/program"} +proptest = "1.3" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +spl-governance-test-sdk = { version = "0.1.3", path = "../../test-sdk" } +spl-governance-addin-mock = { version = "0.1.3", path = "../../addin-mock/program" } [lib] diff --git a/governance/chat/program/src/lib.rs b/governance/chat/program/src/lib.rs index e0a37edaf87..6d882a08ac4 100644 --- a/governance/chat/program/src/lib.rs +++ b/governance/chat/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] //! Governance Chat program diff --git a/governance/program/Cargo.toml b/governance/program/Cargo.toml index 79ceb1b82eb..d1f0ca4990a 100644 --- a/governance/program/Cargo.toml +++ b/governance/program/Cargo.toml @@ -17,9 +17,9 @@ bincode = "1.3.2" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } spl-governance-tools= { version = "0.1.3", path ="../tools"} spl-governance-addin-api= { version = "0.1.3", path ="../addin-api"} @@ -28,9 +28,9 @@ thiserror = "1.0" [dev-dependencies] assert_matches = "1.5.0" base64 = "0.21" -proptest = "1.2" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +proptest = "1.3" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" spl-governance-test-sdk = { version = "0.1.3", path ="../test-sdk"} spl-governance-addin-mock = { version = "0.1.3", path ="../addin-mock/program"} diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index c17456dde1e..3345705583d 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -498,6 +498,22 @@ pub enum GovernanceError { /// Invalid multi choice proposal parameters #[error("Invalid multi choice proposal parameters")] InvalidMultiChoiceProposalParameters, // 620 + + /// Invalid Governance for RequiredSignatory + #[error("Invalid Governance for RequiredSignatory")] + InvalidGovernanceForRequiredSignatory, + + /// SignatoryRecord already exists + #[error("Signatory Record has already been created")] + SignatoryRecordAlreadyExists, + + /// Instruction has been removed + #[error("Instruction has been removed")] + InstructionDeprecated, + + /// Proposal is missing signatories required by its governance + #[error("Proposal is missing required signatories")] + MissingRequiredSignatories, } impl PrintProgramError for GovernanceError { diff --git a/governance/program/src/instruction.rs b/governance/program/src/instruction.rs index 536a29e1fc7..d0861ee61d2 100644 --- a/governance/program/src/instruction.rs +++ b/governance/program/src/instruction.rs @@ -18,6 +18,7 @@ use crate::{ }, realm::{GoverningTokenConfigArgs, SetRealmAuthorityAction}, realm_config::get_realm_config_address, + required_signatory::get_required_signatory_address, signatory_record::get_signatory_record_address, token_owner_record::get_token_owner_record_address, vote_record::{get_vote_record_address, Vote}, @@ -65,7 +66,7 @@ pub enum GovernanceInstruction { name: String, #[allow(dead_code)] - /// Realm config args + /// Realm config args config_args: RealmConfigArgs, }, @@ -122,7 +123,7 @@ pub enum GovernanceInstruction { /// 0. `[]` Realm account the created Governance belongs to /// 1. `[writable]` Account Governance account. PDA seeds: ['account-governance', realm, governed_account] /// 2. `[]` Account governed by this Governance - /// Note: The account doesn't have to exist and can be only used as a unique identifier for the Governance account + /// Note: The account doesn't have to exist and can be only used as a unique identifier for the Governance account /// 3. `[]` Governing TokenOwnerRecord account (Used only if not signed by RealmAuthority) /// 4. `[signer]` Payer /// 5. `[]` System program @@ -208,31 +209,24 @@ pub enum GovernanceInstruction { /// Adds a signatory to the Proposal which means this Proposal can't leave Draft state until yet another Signatory signs /// - /// 0. `[writable]` Proposal account - /// 1. `[]` TokenOwnerRecord account of the Proposal owner - /// 2. `[signer]` Governance Authority (Token Owner or Governance Delegate) - /// 3. `[writable]` Signatory Record Account - /// 4. `[signer]` Payer - /// 5. `[]` System program - /// 6. `[]` Rent sysvar + /// 0. `[]` Governance account + /// 1. `[writable]` Proposal account associated with the governance + /// 2. `[writable]` Signatory Record Account + /// 3. `[signer]` Payer + /// 4. `[]` System program + /// Either: + /// - 5. `[]` TokenOwnerRecord account of the Proposal owner + /// 6. `[signer]` Governance Authority (Token Owner or Governance Delegate) + /// + /// - 5. `[]` RequiredSignatory account associated with the governance. AddSignatory { #[allow(dead_code)] /// Signatory to add to the Proposal signatory: Pubkey, }, - /// Removes a Signatory from the Proposal - /// - /// 0. `[writable]` Proposal account - /// 1. `[]` TokenOwnerRecord account of the Proposal owner - /// 2. `[signer]` Governance Authority (Token Owner or Governance Delegate) - /// 3. `[writable]` Signatory Record Account - /// 4. `[writable]` Beneficiary Account which would receive lamports from the disposed Signatory Record Account - RemoveSignatory { - #[allow(dead_code)] - /// Signatory to remove from the Proposal - signatory: Pubkey, - }, + /// Formerly RemoveSignatory. Exists for backwards-compatibility. + Legacy1, /// Inserts Transaction with a set of instructions for the Proposal at the given index position /// New Transaction must be inserted at the end of the range indicated by Proposal transactions_next_index @@ -324,10 +318,10 @@ pub enum GovernanceInstruction { /// Finalizes vote in case the Vote was not automatically tipped within max_voting_time period /// - /// 0. `[]` Realm account + /// 0. `[]` Realm account /// 1. `[writable]` Governance account /// 2. `[writable]` Proposal account - /// 3. `[writable]` TokenOwnerRecord of the Proposal owner + /// 3. `[writable]` TokenOwnerRecord of the Proposal owner /// 4. `[]` Governing Token Mint /// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 6. `[]` Optional Max Voter Weight Record @@ -363,11 +357,11 @@ pub enum GovernanceInstruction { /// Creates Mint Governance account which governs a mint /// - /// 0. `[]` Realm account the created Governance belongs to + /// 0. `[]` Realm account the created Governance belongs to /// 1. `[writable]` Mint Governance account. PDA seeds: ['mint-governance', realm, governed_mint] /// 2. `[writable]` Mint governed by this Governance account /// 3. `[signer]` Current Mint authority (MintTokens and optionally FreezeAccount) - /// 4. `[]` Governing TokenOwnerRecord account (Used only if not signed by RealmAuthority) + /// 4. `[]` Governing TokenOwnerRecord account (Used only if not signed by RealmAuthority) /// 5. `[signer]` Payer /// 6. `[]` SPL Token program /// 7. `[]` System program @@ -389,18 +383,18 @@ pub enum GovernanceInstruction { /// Creates Token Governance account which governs a token account /// - /// 0. `[]` Realm account the created Governance belongs to + /// 0. `[]` Realm account the created Governance belongs to /// 1. `[writable]` Token Governance account. PDA seeds: ['token-governance', realm, governed_token] /// 2. `[writable]` Token account governed by this Governance account /// 3. `[signer]` Current token account authority (AccountOwner and optionally CloseAccount) - /// 4. `[]` Governing TokenOwnerRecord account (Used only if not signed by RealmAuthority) + /// 4. `[]` Governing TokenOwnerRecord account (Used only if not signed by RealmAuthority) /// 5. `[signer]` Payer /// 6. `[]` SPL Token program /// 7. `[]` System program /// 8. `[]` Sysvar Rent /// 9. `[signer]` Governance authority /// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] - /// 11. `[]` Optional Voter Weight Record + /// 11. `[]` Optional Voter Weight Record CreateTokenGovernance { #[allow(dead_code)] /// Governance config @@ -415,7 +409,7 @@ pub enum GovernanceInstruction { /// Sets GovernanceConfig for a Governance /// - /// 0. `[]` Realm account the Governance account belongs to + /// 0. `[]` Realm account the Governance account belongs to /// 1. `[writable, signer]` The Governance account the config is for SetGovernanceConfig { #[allow(dead_code)] @@ -430,14 +424,14 @@ pub enum GovernanceInstruction { /// /// 0. `[writable]` Proposal account /// 1. `[]` TokenOwnerRecord account of the Proposal owner - /// 2. `[signer]` Governance Authority (Token Owner or Governance Delegate) + /// 2. `[signer]` Governance Authority (Token Owner or Governance Delegate) /// 3. `[writable]` ProposalTransaction account to flag FlagTransactionError, /// Sets new Realm authority /// /// 0. `[writable]` Realm account - /// 1. `[signer]` Current Realm authority + /// 1. `[signer]` Current Realm authority /// 2. `[]` New realm authority. Must be one of the realm governances when set SetRealmAuthority { #[allow(dead_code)] @@ -447,7 +441,7 @@ pub enum GovernanceInstruction { /// Sets realm config /// 0. `[writable]` Realm account - /// 1. `[signer]` Realm authority + /// 1. `[signer]` Realm authority /// 2. `[]` Council Token Mint - optional /// Note: In the current version it's only possible to remove council mint (set it to None) /// After setting council to None it won't be possible to withdraw the tokens from the Realm any longer @@ -457,11 +451,11 @@ pub enum GovernanceInstruction { /// 4. `[]` System /// 5. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm] /// - /// 6. `[]` Optional Community Voter Weight Addin Program Id - /// 7. `[]` Optional Max Community Voter Weight Addin Program Id + /// 6. `[]` Optional Community Voter Weight Addin Program Id + /// 7. `[]` Optional Max Community Voter Weight Addin Program Id /// - /// 8. `[]` Optional Council Voter Weight Addin Program Id - /// 9. `[]` Optional Max Council Voter Weight Addin Program Id + /// 8. `[]` Optional Council Voter Weight Addin Program Id + /// 9. `[]` Optional Max Council Voter Weight Addin Program Id /// /// 10. `[signer]` Optional Payer. Required if RealmConfig doesn't exist and needs to be created SetRealmConfig { @@ -476,7 +470,7 @@ pub enum GovernanceInstruction { /// 0. `[]` Realm account /// 1. `[]` Governing Token Owner account /// 2. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] - /// 3. `[]` Governing Token Mint + /// 3. `[]` Governing Token Mint /// 4. `[signer]` Payer /// 5. `[]` System CreateTokenOwnerRecord {}, @@ -534,6 +528,25 @@ pub enum GovernanceInstruction { /// 1. `[]` TokenOwnerRecord account of the Proposal owner /// 2. `[signer]` CompleteProposal authority (Token Owner or Delegate) CompleteProposal {}, + + /// Adds a required signatory to the Governance, which will be applied to all proposals created with it + /// + /// 0. `[writable, signer]` The Governance account the config is for + /// 1. `[writable]` RequiredSignatory Account + /// 2. `[signer]` Payer + /// 3. `[]` System program + AddRequiredSignatory { + #[allow(dead_code)] + /// Required signatory to add to the Governance + signatory: Pubkey, + }, + + /// Removes a required signatory from the Governance + /// + /// 0. `[writable, signer]` The Governance account the config is for + /// 1. `[writable]` RequiredSignatory Account + /// 2. `[writable]` Beneficiary Account which would receive lamports from the disposed RequiredSignatory Account + RemoveRequiredSignatory, } /// Creates CreateRealm instruction @@ -970,24 +983,39 @@ pub fn create_proposal( pub fn add_signatory( program_id: &Pubkey, // Accounts + governance: &Pubkey, proposal: &Pubkey, - token_owner_record: &Pubkey, - governance_authority: &Pubkey, + add_signatory_authority: &AddSignatoryAuthority, payer: &Pubkey, // Args signatory: &Pubkey, ) -> Instruction { let signatory_record_address = get_signatory_record_address(program_id, proposal, signatory); - let accounts = vec![ + let mut accounts = vec![ + AccountMeta::new_readonly(*governance, false), AccountMeta::new(*proposal, false), - AccountMeta::new_readonly(*token_owner_record, false), - AccountMeta::new_readonly(*governance_authority, true), AccountMeta::new(signatory_record_address, false), AccountMeta::new(*payer, true), AccountMeta::new_readonly(system_program::id(), false), ]; + match add_signatory_authority { + AddSignatoryAuthority::ProposalOwner { + governance_authority, + token_owner_record, + } => { + accounts.push(AccountMeta::new_readonly(*token_owner_record, false)); + accounts.push(AccountMeta::new_readonly(*governance_authority, true)); + } + AddSignatoryAuthority::None => { + accounts.push(AccountMeta::new_readonly( + get_required_signatory_address(program_id, governance, signatory), + false, + )); + } + }; + let instruction = GovernanceInstruction::AddSignatory { signatory: *signatory, }; @@ -999,35 +1027,18 @@ pub fn add_signatory( } } -/// Creates RemoveSignatory instruction -pub fn remove_signatory( - program_id: &Pubkey, - // Accounts - proposal: &Pubkey, - token_owner_record: &Pubkey, - governance_authority: &Pubkey, - signatory: &Pubkey, - beneficiary: &Pubkey, -) -> Instruction { - let signatory_record_address = get_signatory_record_address(program_id, proposal, signatory); - - let accounts = vec![ - AccountMeta::new(*proposal, false), - AccountMeta::new_readonly(*token_owner_record, false), - AccountMeta::new_readonly(*governance_authority, true), - AccountMeta::new(signatory_record_address, false), - AccountMeta::new(*beneficiary, false), - ]; - - let instruction = GovernanceInstruction::RemoveSignatory { - signatory: *signatory, - }; - - Instruction { - program_id: *program_id, - accounts, - data: instruction.try_to_vec().unwrap(), - } +#[derive(Debug, Copy, Clone)] +/// Enum to specify the authority by which the instruction should add a signatory +pub enum AddSignatoryAuthority { + /// Proposal owners can add optional signatories to a proposal + ProposalOwner { + /// Token owner or its delegate + governance_authority: Pubkey, + /// Token owner record of the Proposal owner + token_owner_record: Pubkey, + }, + /// Anyone can add signatories that are required by the governance to a proposal + None, } /// Creates SignOffProposal instruction @@ -1614,6 +1625,62 @@ pub fn revoke_governing_tokens( } } +/// Creates AddRequiredSignatory instruction +pub fn add_required_signatory( + program_id: &Pubkey, + // Accounts + governance: &Pubkey, + payer: &Pubkey, + // Args + signatory: &Pubkey, +) -> Instruction { + let required_signatory_address = + get_required_signatory_address(program_id, governance, signatory); + + let accounts = vec![ + AccountMeta::new(*governance, true), + AccountMeta::new(required_signatory_address, false), + AccountMeta::new(*payer, true), + AccountMeta::new_readonly(system_program::id(), false), + ]; + + let instruction = GovernanceInstruction::AddRequiredSignatory { + signatory: *signatory, + }; + + Instruction { + program_id: *program_id, + accounts, + data: instruction.try_to_vec().unwrap(), + } +} + +/// Creates RemoveRequiredSignatory instruction +pub fn remove_required_signatory( + program_id: &Pubkey, + // Accounts + governance: &Pubkey, + signatory: &Pubkey, + beneficiary: &Pubkey, +) -> Instruction { + let required_signatory_address = + get_required_signatory_address(program_id, governance, signatory); + + let accounts = vec![ + AccountMeta::new(*governance, true), + AccountMeta::new(required_signatory_address, false), + AccountMeta::new(*beneficiary, false), + ]; + + let instruction = GovernanceInstruction::RemoveRequiredSignatory; + + Instruction { + program_id: *program_id, + accounts, + data: instruction.try_to_vec().unwrap(), + } +} + /// Adds accounts specified by GoverningTokenConfigAccountArgs /// and returns GoverningTokenConfigArgs pub fn with_governing_token_config_args( diff --git a/governance/program/src/lib.rs b/governance/program/src/lib.rs index add231510b2..ae30d26e75e 100644 --- a/governance/program/src/lib.rs +++ b/governance/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] //! A Governance program for the Solana blockchain. diff --git a/governance/program/src/processor/mod.rs b/governance/program/src/processor/mod.rs index 5b537e870da..9b8a84b2588 100644 --- a/governance/program/src/processor/mod.rs +++ b/governance/program/src/processor/mod.rs @@ -1,5 +1,6 @@ //! Program processor +mod process_add_required_signatory; mod process_add_signatory; mod process_cancel_proposal; mod process_cast_vote; @@ -19,7 +20,7 @@ mod process_flag_transaction_error; mod process_insert_transaction; mod process_refund_proposal_deposit; mod process_relinquish_vote; -mod process_remove_signatory; +mod process_remove_required_signatory; mod process_remove_transaction; mod process_revoke_governing_tokens; mod process_set_governance_config; @@ -30,8 +31,9 @@ mod process_sign_off_proposal; mod process_update_program_metadata; mod process_withdraw_governing_tokens; -use crate::instruction::GovernanceInstruction; +use crate::{error::GovernanceError, instruction::GovernanceInstruction}; +use process_add_required_signatory::*; use process_add_signatory::*; use process_cancel_proposal::*; use process_cast_vote::*; @@ -51,7 +53,7 @@ use process_flag_transaction_error::*; use process_insert_transaction::*; use process_refund_proposal_deposit::*; use process_relinquish_vote::*; -use process_remove_signatory::*; +use process_remove_required_signatory::*; use process_remove_transaction::*; use process_revoke_governing_tokens::*; use process_set_governance_config::*; @@ -63,7 +65,7 @@ use process_update_program_metadata::*; use process_withdraw_governing_tokens::*; use solana_program::{ - account_info::AccountInfo, borsh::try_from_slice_unchecked, entrypoint::ProgramResult, msg, + account_info::AccountInfo, borsh0_10::try_from_slice_unchecked, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, }; @@ -164,8 +166,8 @@ pub fn process_instruction( GovernanceInstruction::AddSignatory { signatory } => { process_add_signatory(program_id, accounts, signatory) } - GovernanceInstruction::RemoveSignatory { signatory } => { - process_remove_signatory(program_id, accounts, signatory) + GovernanceInstruction::Legacy1 => { + Err(GovernanceError::InstructionDeprecated.into()) // No-op } GovernanceInstruction::SignOffProposal {} => { process_sign_off_proposal(program_id, accounts) @@ -233,5 +235,12 @@ pub fn process_instruction( GovernanceInstruction::CompleteProposal {} => { process_complete_proposal(program_id, accounts) } + + GovernanceInstruction::AddRequiredSignatory { signatory } => { + process_add_required_signatory(program_id, accounts, signatory) + } + GovernanceInstruction::RemoveRequiredSignatory => { + process_remove_required_signatory(program_id, accounts) + } } } diff --git a/governance/program/src/processor/process_add_required_signatory.rs b/governance/program/src/processor/process_add_required_signatory.rs new file mode 100644 index 00000000000..897939f6bcf --- /dev/null +++ b/governance/program/src/processor/process_add_required_signatory.rs @@ -0,0 +1,69 @@ +//! Program state processor + +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + pubkey::Pubkey, + rent::Rent, + sysvar::Sysvar, +}; +use spl_governance_tools::account::create_and_serialize_account_signed; + +use crate::{ + error::GovernanceError, + state::{ + enums::GovernanceAccountType, + governance::get_governance_data, + required_signatory::{get_required_signatory_address_seeds, RequiredSignatory}, + }, +}; + +/// Processes AddRequiredSignatory instruction +pub fn process_add_required_signatory( + program_id: &Pubkey, + accounts: &[AccountInfo], + signatory: Pubkey, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let governance_info = next_account_info(account_info_iter)?; // 0 + + let required_signatory_info = next_account_info(account_info_iter)?; // 1 + + let payer_info = next_account_info(account_info_iter)?; // 2 + let system_info = next_account_info(account_info_iter)?; // 3 + + let rent = Rent::get()?; + + // Only governance PDA via a proposal can authorize change to its own config + if !governance_info.is_signer { + return Err(GovernanceError::GovernancePdaMustSign.into()); + }; + + let mut governance_data = get_governance_data(program_id, governance_info)?; + governance_data.required_signatories_count = governance_data + .required_signatories_count + .checked_add(1) + .unwrap(); + governance_data.serialize(&mut governance_info.data.borrow_mut()[..])?; + + let signatory_record_data = RequiredSignatory { + signatory, + account_type: GovernanceAccountType::RequiredSignatory, + governance: *governance_info.key, + account_version: 0, + }; + + create_and_serialize_account_signed::( + payer_info, + required_signatory_info, + &signatory_record_data, + &get_required_signatory_address_seeds(governance_info.key, &signatory), + program_id, + system_info, + &rent, + 0, + )?; + + Ok(()) +} diff --git a/governance/program/src/processor/process_add_signatory.rs b/governance/program/src/processor/process_add_signatory.rs index 80785da1462..4cb96491123 100644 --- a/governance/program/src/processor/process_add_signatory.rs +++ b/governance/program/src/processor/process_add_signatory.rs @@ -9,11 +9,16 @@ use solana_program::{ }; use spl_governance_tools::account::create_and_serialize_account_signed; -use crate::state::{ - enums::GovernanceAccountType, - proposal::get_proposal_data, - signatory_record::{get_signatory_record_address_seeds, SignatoryRecordV2}, - token_owner_record::get_token_owner_record_data_for_proposal_owner, +use crate::{ + error::GovernanceError, + state::{ + enums::GovernanceAccountType, + governance::get_governance_data, + proposal::get_proposal_data_for_governance, + required_signatory::get_required_signatory_data_for_governance, + signatory_record::{get_signatory_record_address_seeds, SignatoryRecordV2}, + token_owner_record::get_token_owner_record_data_for_proposal_owner, + }, }; /// Processes AddSignatory instruction @@ -24,27 +29,50 @@ pub fn process_add_signatory( ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); - let proposal_info = next_account_info(account_info_iter)?; // 0 - let token_owner_record_info = next_account_info(account_info_iter)?; // 1 - let governance_authority_info = next_account_info(account_info_iter)?; // 2 + let governance_info = next_account_info(account_info_iter)?; // 0 + let proposal_info = next_account_info(account_info_iter)?; // 1 + let signatory_record_info = next_account_info(account_info_iter)?; // 2 - let signatory_record_info = next_account_info(account_info_iter)?; // 3 + let payer_info = next_account_info(account_info_iter)?; // 3 + let system_info = next_account_info(account_info_iter)?; // 4 - let payer_info = next_account_info(account_info_iter)?; // 4 - let system_info = next_account_info(account_info_iter)?; // 5 + let governance_data = get_governance_data(program_id, governance_info)?; - let rent = Rent::get()?; - - let mut proposal_data = get_proposal_data(program_id, proposal_info)?; + let mut proposal_data = + get_proposal_data_for_governance(program_id, proposal_info, governance_info.key)?; proposal_data.assert_can_edit_signatories()?; - let token_owner_record_data = get_token_owner_record_data_for_proposal_owner( - program_id, - token_owner_record_info, - &proposal_data.token_owner_record, - )?; + if !signatory_record_info.data_is_empty() { + return Err(GovernanceError::SignatoryRecordAlreadyExists.into()); + } + + // All required signatories must be added before additional signatories can be added + if proposal_data.signatories_count < governance_data.required_signatories_count { + let required_signatory_info = next_account_info(account_info_iter)?; // 5 + let required_signatory_data = get_required_signatory_data_for_governance( + program_id, + required_signatory_info, + governance_info.key, + )?; - token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?; + if required_signatory_data.signatory != signatory { + return Err(GovernanceError::InvalidSignatoryAddress.into()); + } + } else { + let token_owner_record_info = next_account_info(account_info_iter)?; // 5 + let governance_authority_info = next_account_info(account_info_iter)?; // 6 + + let token_owner_record_data = get_token_owner_record_data_for_proposal_owner( + program_id, + token_owner_record_info, + &proposal_data.token_owner_record, + )?; + + token_owner_record_data + .assert_token_owner_or_delegate_is_signer(governance_authority_info)?; + } + + let rent = Rent::get()?; let signatory_record_data = SignatoryRecordV2 { account_type: GovernanceAccountType::SignatoryRecordV2, diff --git a/governance/program/src/processor/process_create_governance.rs b/governance/program/src/processor/process_create_governance.rs index aa7c00b2971..ec3c8a73f9a 100644 --- a/governance/program/src/processor/process_create_governance.rs +++ b/governance/program/src/processor/process_create_governance.rs @@ -9,7 +9,7 @@ use crate::{ }, realm::get_realm_data, }, - tools::structs::Reserved120, + tools::structs::Reserved119, }; use solana_program::{ account_info::{next_account_info, AccountInfo}, @@ -60,7 +60,8 @@ pub fn process_create_governance( governed_account: *governed_account_info.key, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; diff --git a/governance/program/src/processor/process_create_mint_governance.rs b/governance/program/src/processor/process_create_mint_governance.rs index 1cd35c98ff2..73ac74a6466 100644 --- a/governance/program/src/processor/process_create_mint_governance.rs +++ b/governance/program/src/processor/process_create_mint_governance.rs @@ -11,7 +11,7 @@ use crate::{ }, tools::{ spl_token::{assert_spl_token_mint_authority_is_signer, set_spl_token_account_authority}, - structs::Reserved120, + structs::Reserved119, }, }; use solana_program::{ @@ -70,7 +70,8 @@ pub fn process_create_mint_governance( governed_account: *governed_mint_info.key, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; diff --git a/governance/program/src/processor/process_create_program_governance.rs b/governance/program/src/processor/process_create_program_governance.rs index 0f24ddb0256..e23a381540e 100644 --- a/governance/program/src/processor/process_create_program_governance.rs +++ b/governance/program/src/processor/process_create_program_governance.rs @@ -14,7 +14,7 @@ use crate::{ bpf_loader_upgradeable::{ assert_program_upgrade_authority_is_signer, set_program_upgrade_authority, }, - structs::Reserved120, + structs::Reserved119, }, }; use solana_program::{ @@ -72,7 +72,8 @@ pub fn process_create_program_governance( governed_account: *governed_program_info.key, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; diff --git a/governance/program/src/processor/process_create_token_governance.rs b/governance/program/src/processor/process_create_token_governance.rs index ab5e4081944..33427a604f9 100644 --- a/governance/program/src/processor/process_create_token_governance.rs +++ b/governance/program/src/processor/process_create_token_governance.rs @@ -11,7 +11,7 @@ use crate::{ }, tools::{ spl_token::{assert_spl_token_owner_is_signer, set_spl_token_account_authority}, - structs::Reserved120, + structs::Reserved119, }, }; use solana_program::{ @@ -70,7 +70,8 @@ pub fn process_create_token_governance( governed_account: *governed_token_info.key, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; diff --git a/governance/program/src/processor/process_execute_transaction.rs b/governance/program/src/processor/process_execute_transaction.rs index 1cc13046565..a5ba79c7552 100644 --- a/governance/program/src/processor/process_execute_transaction.rs +++ b/governance/program/src/processor/process_execute_transaction.rs @@ -88,7 +88,7 @@ pub fn process_execute_transaction(program_id: &Pubkey, accounts: &[AccountInfo] proposal_data.state = ProposalState::Executing; } - let mut option = &mut proposal_data.options[proposal_transaction_data.option_index as usize]; + let option = &mut proposal_data.options[proposal_transaction_data.option_index as usize]; option.transactions_executed_count = option.transactions_executed_count.checked_add(1).unwrap(); // Checking for Executing and ExecutingWithErrors states because instruction can still be executed after being flagged with error diff --git a/governance/program/src/processor/process_remove_required_signatory.rs b/governance/program/src/processor/process_remove_required_signatory.rs new file mode 100644 index 00000000000..05a564c906c --- /dev/null +++ b/governance/program/src/processor/process_remove_required_signatory.rs @@ -0,0 +1,44 @@ +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + pubkey::Pubkey, +}; +use spl_governance_tools::account::dispose_account; + +use crate::{ + error::GovernanceError, state::governance::get_governance_data, + state::required_signatory::get_required_signatory_data_for_governance, +}; + +pub fn process_remove_required_signatory( + program_id: &Pubkey, + accounts: &[AccountInfo], +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let governance_info = next_account_info(account_info_iter)?; // 0 + let required_signatory_info = next_account_info(account_info_iter)?; // 1 + let beneficiary_info = next_account_info(account_info_iter)?; // 2 + + if !governance_info.is_signer { + return Err(GovernanceError::GovernancePdaMustSign.into()); + }; + + let mut governance_data = get_governance_data(program_id, governance_info)?; + + get_required_signatory_data_for_governance( + program_id, + required_signatory_info, + governance_info.key, + )?; + + governance_data.required_signatories_count = governance_data + .required_signatories_count + .checked_sub(1) + .unwrap(); + governance_data.serialize(&mut governance_info.data.borrow_mut()[..])?; + + dispose_account(required_signatory_info, beneficiary_info)?; + + Ok(()) +} diff --git a/governance/program/src/processor/process_remove_signatory.rs b/governance/program/src/processor/process_remove_signatory.rs deleted file mode 100644 index 9a44b8aa12d..00000000000 --- a/governance/program/src/processor/process_remove_signatory.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Program state processor - -use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - pubkey::Pubkey, -}; -use spl_governance_tools::account::dispose_account; - -use crate::state::{ - proposal::get_proposal_data, signatory_record::get_signatory_record_data_for_seeds, - token_owner_record::get_token_owner_record_data_for_proposal_owner, -}; - -/// Processes RemoveSignatory instruction -pub fn process_remove_signatory( - program_id: &Pubkey, - accounts: &[AccountInfo], - signatory: Pubkey, -) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - - let proposal_info = next_account_info(account_info_iter)?; // 0 - let token_owner_record_info = next_account_info(account_info_iter)?; // 1 - let governance_authority_info = next_account_info(account_info_iter)?; // 2 - - let signatory_record_info = next_account_info(account_info_iter)?; // 3 - let beneficiary_info = next_account_info(account_info_iter)?; // 4 - - let mut proposal_data = get_proposal_data(program_id, proposal_info)?; - proposal_data.assert_can_edit_signatories()?; - - let token_owner_record_data = get_token_owner_record_data_for_proposal_owner( - program_id, - token_owner_record_info, - &proposal_data.token_owner_record, - )?; - - token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?; - - let signatory_record_data = get_signatory_record_data_for_seeds( - program_id, - signatory_record_info, - proposal_info.key, - &signatory, - )?; - signatory_record_data.assert_can_remove_signatory()?; - - proposal_data.signatories_count = proposal_data.signatories_count.checked_sub(1).unwrap(); - - proposal_data.serialize(&mut proposal_info.data.borrow_mut()[..])?; - - dispose_account(signatory_record_info, beneficiary_info)?; - - Ok(()) -} diff --git a/governance/program/src/processor/process_remove_transaction.rs b/governance/program/src/processor/process_remove_transaction.rs index 9115ad602b3..e89df934476 100644 --- a/governance/program/src/processor/process_remove_transaction.rs +++ b/governance/program/src/processor/process_remove_transaction.rs @@ -42,7 +42,7 @@ pub fn process_remove_transaction(program_id: &Pubkey, accounts: &[AccountInfo]) dispose_account(proposal_transaction_info, beneficiary_info)?; - let mut option = &mut proposal_data.options[proposal_transaction_data.option_index as usize]; + let option = &mut proposal_data.options[proposal_transaction_data.option_index as usize]; option.transactions_count = option.transactions_count.checked_sub(1).unwrap(); proposal_data.serialize(&mut proposal_info.data.borrow_mut()[..])?; diff --git a/governance/program/src/processor/process_sign_off_proposal.rs b/governance/program/src/processor/process_sign_off_proposal.rs index da4d827f6c5..45046601212 100644 --- a/governance/program/src/processor/process_sign_off_proposal.rs +++ b/governance/program/src/processor/process_sign_off_proposal.rs @@ -15,6 +15,8 @@ use crate::state::{ token_owner_record::get_token_owner_record_data_for_proposal_owner, }; +use crate::error::GovernanceError; + /// Processes SignOffProposal instruction pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -29,10 +31,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) assert_is_valid_realm(program_id, realm_info)?; - // Governance account data is no longer used in the current version but we still have to load it to validate Realm -> Governance -> Proposal relationship - // It could be replaced with PDA check but the account is going to be needed in future versions once we support mandatory signatories - // and hence keeping it as it is - let _governance_data = + let governance_data = get_governance_data_for_realm(program_id, governance_info, realm_info.key)?; let mut proposal_data = @@ -40,6 +39,12 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) proposal_data.assert_can_sign_off()?; + if governance_data.required_signatories_count > 0 + && proposal_data.signatories_count < governance_data.required_signatories_count + { + return Err(GovernanceError::MissingRequiredSignatories.into()); + } + // If the owner of the proposal hasn't appointed any signatories then can sign off the proposal themself if proposal_data.signatories_count == 0 { let proposal_owner_record_info = next_account_info(account_info_iter)?; // 4 diff --git a/governance/program/src/state/enums.rs b/governance/program/src/state/enums.rs index ff4c7711ab1..c36b80e8afd 100644 --- a/governance/program/src/state/enums.rs +++ b/governance/program/src/state/enums.rs @@ -91,6 +91,9 @@ pub enum GovernanceAccountType { /// Proposal deposit account ProposalDeposit, + + /// Required signatory account + RequiredSignatory, } /// What state a Proposal is in diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 4c9edafff22..f7bcf788ab7 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -9,7 +9,7 @@ use crate::{ realm::{assert_is_valid_realm, RealmV2}, vote_record::VoteKind, }, - tools::structs::Reserved120, + tools::structs::Reserved119, }; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ @@ -104,7 +104,10 @@ pub struct GovernanceV2 { /// Reserved space for versions v2 and onwards /// Note 1: V1 accounts must be resized before using this space /// Note 2: The reserved space should be used from the end to also allow the config to grow if needed - pub reserved_v2: Reserved120, + pub reserved_v2: Reserved119, + + /// The number of required signatories for proposals in the Governance + pub required_signatories_count: u8, /// The number of active proposals where active means Draft, SigningOff or Voting state /// @@ -146,7 +149,8 @@ pub fn is_governance_v2_account_type(account_type: &GovernanceAccountType) -> bo | GovernanceAccountType::VoteRecordV1 | GovernanceAccountType::VoteRecordV2 | GovernanceAccountType::ProgramMetadata - | GovernanceAccountType::ProposalDeposit => false, + | GovernanceAccountType::ProposalDeposit + | GovernanceAccountType::RequiredSignatory => false, } } @@ -180,7 +184,8 @@ pub fn try_get_governance_v2_type_for_v1( | GovernanceAccountType::VoteRecordV1 | GovernanceAccountType::VoteRecordV2 | GovernanceAccountType::ProgramMetadata - | GovernanceAccountType::ProposalDeposit => None, + | GovernanceAccountType::ProposalDeposit + | GovernanceAccountType::RequiredSignatory => None, } } @@ -227,7 +232,8 @@ impl GovernanceV2 { | GovernanceAccountType::ProposalDeposit | GovernanceAccountType::RealmV2 | GovernanceAccountType::TokenOwnerRecordV2 - | GovernanceAccountType::SignatoryRecordV2 => { + | GovernanceAccountType::SignatoryRecordV2 + | GovernanceAccountType::RequiredSignatory => { return Err(GovernanceToolsError::InvalidAccountType.into()) } }; @@ -243,7 +249,7 @@ impl GovernanceV2 { // V1 account can't be resized and we have to translate it back to the original format // If reserved_v2 is used it must be individually assessed for GovernanceV1 account backward compatibility impact - if self.reserved_v2 != Reserved120::default() { + if self.reserved_v2 != Reserved119::default() { panic!("Extended data not supported by GovernanceV1") } @@ -388,7 +394,8 @@ pub fn get_governance_data( governed_account: governance_data_v1.governed_account, reserved1: 0, config: governance_data_v1.config, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, // GovernanceV1 layout doesn't support active_proposal_count // For any legacy GovernanceV1 account it's not preserved until the account layout is migrated to GovernanceV2 in CreateProposal active_proposal_count: 0, @@ -650,8 +657,9 @@ mod test { governed_account: Pubkey::new_unique(), reserved1: 0, config: create_test_governance_config(), - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), active_proposal_count: 10, + required_signatories_count: 0, } } diff --git a/governance/program/src/state/legacy.rs b/governance/program/src/state/legacy.rs index 0e3fa69d88c..558010d654e 100644 --- a/governance/program/src/state/legacy.rs +++ b/governance/program/src/state/legacy.rs @@ -152,7 +152,8 @@ pub fn is_governance_v1_account_type(account_type: &GovernanceAccountType) -> bo | GovernanceAccountType::VoteRecordV1 | GovernanceAccountType::VoteRecordV2 | GovernanceAccountType::ProgramMetadata - | GovernanceAccountType::ProposalDeposit => false, + | GovernanceAccountType::ProposalDeposit + | GovernanceAccountType::RequiredSignatory => false, } } diff --git a/governance/program/src/state/mod.rs b/governance/program/src/state/mod.rs index 3d61e817fe6..aa9ebf56be4 100644 --- a/governance/program/src/state/mod.rs +++ b/governance/program/src/state/mod.rs @@ -10,6 +10,7 @@ pub mod proposal_deposit; pub mod proposal_transaction; pub mod realm; pub mod realm_config; +pub mod required_signatory; pub mod signatory_record; pub mod token_owner_record; pub mod vote_record; diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index acce26f4fcd..3eff2d92fc1 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -687,7 +687,7 @@ impl ProposalV2 { return None; }; - let mut yes_option = &mut self.options[0]; + let yes_option = &mut self.options[0]; let yes_vote_weight = yes_option.vote_weight; let deny_vote_weight = self.deny_vote_weight.unwrap(); diff --git a/governance/program/src/state/realm.rs b/governance/program/src/state/realm.rs index 9c980dcdecf..3027b770234 100644 --- a/governance/program/src/state/realm.rs +++ b/governance/program/src/state/realm.rs @@ -191,7 +191,8 @@ pub fn is_realm_account_type(account_type: &GovernanceAccountType) -> bool { | GovernanceAccountType::VoteRecordV1 | GovernanceAccountType::VoteRecordV2 | GovernanceAccountType::ProgramMetadata - | GovernanceAccountType::ProposalDeposit => false, + | GovernanceAccountType::ProposalDeposit + | GovernanceAccountType::RequiredSignatory => false, } } @@ -457,7 +458,7 @@ pub fn assert_valid_realm_config_args( mod test { use crate::instruction::GovernanceInstruction; - use solana_program::borsh::try_from_slice_unchecked; + use solana_program::borsh0_10::try_from_slice_unchecked; use super::*; diff --git a/governance/program/src/state/required_signatory.rs b/governance/program/src/state/required_signatory.rs new file mode 100644 index 00000000000..bd0a302c95b --- /dev/null +++ b/governance/program/src/state/required_signatory.rs @@ -0,0 +1,73 @@ +//! RequiredSignatory account +use crate::{error::GovernanceError, state::enums::GovernanceAccountType}; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use solana_program::{ + account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, + pubkey::Pubkey, +}; +use spl_governance_tools::account::{get_account_data, AccountMaxSize}; + +/// Required signatory +#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct RequiredSignatory { + /// Account type + pub account_type: GovernanceAccountType, + + /// Account version + pub account_version: u8, + + /// Governance this required signatory belongs to + pub governance: Pubkey, + + /// Address of required signatory + pub signatory: Pubkey, +} + +impl AccountMaxSize for RequiredSignatory {} + +impl IsInitialized for RequiredSignatory { + fn is_initialized(&self) -> bool { + self.account_type == GovernanceAccountType::RequiredSignatory + } +} + +/// Deserializes RequiredSignatory account, checks the owner program, and asserts that required signatory belongs to the given governance +pub fn get_required_signatory_data_for_governance( + program_id: &Pubkey, + required_signatory_info: &AccountInfo, + governance: &Pubkey, +) -> Result { + let required_signatory_data = + get_account_data::(program_id, required_signatory_info)?; + + if required_signatory_data.governance != *governance { + return Err(GovernanceError::InvalidGovernanceForRequiredSignatory.into()); + } + + Ok(required_signatory_data) +} + +/// Returns RequiredSignatory PDA seeds +pub fn get_required_signatory_address_seeds<'a>( + governance: &'a Pubkey, + signatory: &'a Pubkey, +) -> [&'a [u8]; 3] { + [ + b"required-signatory".as_ref(), + governance.as_ref(), + signatory.as_ref(), + ] +} + +/// Returns RequiredSignatory PDA address +pub fn get_required_signatory_address<'a>( + program_id: &Pubkey, + governance: &'a Pubkey, + signatory: &'a Pubkey, +) -> Pubkey { + Pubkey::find_program_address( + &get_required_signatory_address_seeds(governance, signatory), + program_id, + ) + .0 +} diff --git a/governance/program/src/state/token_owner_record.rs b/governance/program/src/state/token_owner_record.rs index 700eb31ee24..846dcd9b0bf 100644 --- a/governance/program/src/state/token_owner_record.rs +++ b/governance/program/src/state/token_owner_record.rs @@ -422,7 +422,7 @@ pub fn get_token_owner_record_data_for_proposal_owner( #[cfg(test)] mod test { - use solana_program::{borsh::get_packed_len, stake_history::Epoch}; + use solana_program::{borsh0_10::get_packed_len, stake_history::Epoch}; use super::*; diff --git a/governance/program/src/tools/structs.rs b/governance/program/src/tools/structs.rs index 236c4d643f9..80c07173cf7 100644 --- a/governance/program/src/tools/structs.rs +++ b/governance/program/src/tools/structs.rs @@ -23,23 +23,23 @@ impl Default for Reserved110 { } } -/// Reserved 120 bytes +/// Reserved 119 bytes #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct Reserved120 { +pub struct Reserved119 { /// Reserved 64 bytes pub reserved64: [u8; 64], /// Reserved 32 bytes pub reserved32: [u8; 32], - /// Reserved 4 bytes - pub reserved24: [u8; 24], + /// Reserved 19 bytes + pub reserved23: [u8; 23], } -impl Default for Reserved120 { +impl Default for Reserved119 { fn default() -> Self { Self { reserved64: [0; 64], reserved32: [0; 32], - reserved24: [0; 24], + reserved23: [0; 23], } } } diff --git a/governance/program/tests/process_add_required_signatory.rs b/governance/program/tests/process_add_required_signatory.rs new file mode 100644 index 00000000000..0f0d161600e --- /dev/null +++ b/governance/program/tests/process_add_required_signatory.rs @@ -0,0 +1,194 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; + +use solana_program_test::tokio; + +use num_traits::cast::ToPrimitive; +use program_test::*; +use solana_program::{ + program_error::ProgramError, pubkey::Pubkey, system_instruction::SystemError, +}; +use solana_sdk::signature::Signer; +use spl_governance::error::GovernanceError; +use spl_governance::instruction::add_required_signatory; + +#[tokio::test] +async fn test_add_required_signatory() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory = Pubkey::new_unique(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal_by_owner(&proposal_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + // Act + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // Assert + let governance_account = governance_test + .get_governance_account(&governance_cookie.address) + .await; + + assert_eq!(1, governance_account.required_signatories_count); +} + +#[tokio::test] +pub async fn add_same_required_signatory_to_governance_twice_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, realm_cookie, signatory) = + governance_test + .with_governance_with_required_signatory() + .await; + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &proposal_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + // Act + let err = governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!( + err, + ProgramError::Custom(SystemError::AccountAlreadyInUse.to_u32().unwrap()) + ); +} + +#[tokio::test] +pub async fn add_required_signatory_to_governance_without_governance_signer_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory = Pubkey::new_unique(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut gwr_ix = add_required_signatory( + &governance_test.program_id, + &governance_cookie.address, + &governance_test.bench.payer.pubkey(), + &signatory, + ); + + gwr_ix.accounts[0].is_signer = false; + + // Act + let err = governance_test + .bench + .process_transaction(&[gwr_ix], Some(&[])) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::GovernancePdaMustSign.into()); +} diff --git a/governance/program/tests/process_add_signatory.rs b/governance/program/tests/process_add_signatory.rs index 6715cc562ab..f7107e23160 100644 --- a/governance/program/tests/process_add_signatory.rs +++ b/governance/program/tests/process_add_signatory.rs @@ -2,11 +2,17 @@ mod program_test; +use solana_program::program_error::ProgramError; use solana_program_test::tokio; +use borsh::BorshSerialize; use program_test::*; +use solana_sdk::{pubkey::Pubkey, signature::Signer}; -use spl_governance::error::GovernanceError; +use spl_governance::{ + error::GovernanceError, + instruction::{add_signatory, AddSignatoryAuthority, GovernanceInstruction}, +}; #[tokio::test] async fn test_add_signatory() { @@ -37,7 +43,11 @@ async fn test_add_signatory() { // Act let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -91,7 +101,11 @@ async fn test_add_signatory_with_owner_or_delegate_must_sign_error() { // Act let err = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .err() .unwrap(); @@ -139,7 +153,11 @@ async fn test_add_signatory_with_invalid_proposal_owner_error() { // Act let err = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .err() .unwrap(); @@ -147,3 +165,308 @@ async fn test_add_signatory_with_invalid_proposal_owner_error() { // Assert assert_eq!(err, GovernanceError::InvalidProposalOwnerAccount.into()); } + +#[tokio::test] +async fn test_add_signatory_for_required_signatory() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + let signatory = Pubkey::new_unique(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Act + let new_signatory_record_cookie = governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory, + ) + .await + .unwrap(); + + // Assert + let signatory_account = governance_test + .get_signatory_record_account(&new_signatory_record_cookie.address) + .await; + + assert_eq!(signatory_account.signatory, signatory); + assert_eq!(signatory_account.proposal, new_proposal_cookie.address); + assert!(!signatory_account.signed_off); + + let new_proposal_account = governance_test + .get_proposal_account(&new_proposal_cookie.address) + .await; + + assert_eq!(new_proposal_account.signatories_count, 1); +} + +#[tokio::test] +async fn test_add_signatory_for_required_signatory_multiple_times_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + let signatory = Pubkey::new_unique(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory, + ) + .await + .unwrap(); + governance_test.advance_clock().await; + + // Act + let err = governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory, + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::SignatoryRecordAlreadyExists.into()); +} + +#[tokio::test] +pub async fn test_add_optional_signatory_before_all_required_signatories_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, _, _) = governance_test + .with_governance_with_required_signatory() + .await; + + let proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, ProgramError::UninitializedAccount); +} + +#[tokio::test] +pub async fn test_add_optional_signatory_to_proposal_with_required_signatories() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, _, signatory) = governance_test + .with_governance_with_required_signatory() + .await; + + let proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + // Act + governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // Assert + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + assert_eq!(proposal_account.signatories_count, 2); +} + +#[tokio::test] +pub async fn test_add_non_matching_required_signatory_to_proposal_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, _, signatory) = governance_test + .with_governance_with_required_signatory() + .await; + + let proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let mut create_signatory_record_ix = add_signatory( + &governance_test.program_id, + &governance_cookie.address, + &proposal_cookie.address, + &AddSignatoryAuthority::None, + &governance_test.bench.payer.pubkey(), + &signatory.pubkey(), + ); + + create_signatory_record_ix.data = GovernanceInstruction::AddSignatory { + signatory: Pubkey::new_unique(), + } + .try_to_vec() + .unwrap(); + + // Act + let err = governance_test + .bench + .process_transaction(&[create_signatory_record_ix], Some(&[])) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::InvalidSignatoryAddress.into()); +} diff --git a/governance/program/tests/process_complete_proposal.rs b/governance/program/tests/process_complete_proposal.rs index 7721c182091..b42e4ca5425 100644 --- a/governance/program/tests/process_complete_proposal.rs +++ b/governance/program/tests/process_complete_proposal.rs @@ -30,7 +30,7 @@ async fn test_complete_proposal() { .await .unwrap(); - let mut proposal_cookie = governance_test + let proposal_cookie = governance_test .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) .await .unwrap(); @@ -50,7 +50,7 @@ async fn test_complete_proposal() { // Act governance_test - .complete_proposal(&mut proposal_cookie, &token_owner_record_cookie) + .complete_proposal(&proposal_cookie, &token_owner_record_cookie) .await .unwrap(); @@ -85,7 +85,7 @@ async fn test_complete_proposal_with_wrong_state_error() { .await .unwrap(); - let mut proposal_cookie = governance_test + let proposal_cookie = governance_test .with_proposal(&token_owner_record_cookie, &mut token_governance_cookie) .await .unwrap(); @@ -98,7 +98,7 @@ async fn test_complete_proposal_with_wrong_state_error() { // Act let err = governance_test - .complete_proposal(&mut proposal_cookie, &token_owner_record_cookie) + .complete_proposal(&proposal_cookie, &token_owner_record_cookie) .await .err() .unwrap(); @@ -134,7 +134,11 @@ async fn test_complete_proposal_with_completed_state_transaction_exists_error() .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &token_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -168,7 +172,7 @@ async fn test_complete_proposal_with_completed_state_transaction_exists_error() // Act let err = governance_test - .complete_proposal(&mut proposal_cookie, &token_owner_record_cookie) + .complete_proposal(&proposal_cookie, &token_owner_record_cookie) .await .err() .unwrap(); @@ -198,7 +202,7 @@ async fn test_complete_proposal_with_owner_or_delegate_must_sign_error() { .await .unwrap(); - let mut proposal_cookie = governance_test + let proposal_cookie = governance_test .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) .await .unwrap(); @@ -217,7 +221,7 @@ async fn test_complete_proposal_with_owner_or_delegate_must_sign_error() { // Act let err = governance_test - .complete_proposal(&mut proposal_cookie, &token_owner_record_cookie) + .complete_proposal(&proposal_cookie, &token_owner_record_cookie) .await .err() .unwrap(); diff --git a/governance/program/tests/process_create_native_treasury.rs b/governance/program/tests/process_create_native_treasury.rs index 6e8fb96e3ef..b85fee7e827 100644 --- a/governance/program/tests/process_create_native_treasury.rs +++ b/governance/program/tests/process_create_native_treasury.rs @@ -74,7 +74,11 @@ async fn test_execute_transfer_from_native_treasury() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); diff --git a/governance/program/tests/process_execute_transaction.rs b/governance/program/tests/process_execute_transaction.rs index 7958ae3e1ba..97b3381426b 100644 --- a/governance/program/tests/process_execute_transaction.rs +++ b/governance/program/tests/process_execute_transaction.rs @@ -43,7 +43,11 @@ async fn test_execute_mint_transaction() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -144,7 +148,11 @@ async fn test_execute_transfer_transaction() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &token_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -243,7 +251,11 @@ async fn test_execute_upgrade_program_transaction() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &program_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -369,12 +381,20 @@ async fn test_execute_proposal_transaction_with_invalid_state_errors() { .unwrap(); let signatory_record_cookie1 = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); let signatory_record_cookie2 = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -534,7 +554,11 @@ async fn test_execute_proposal_transaction_for_other_proposal_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -620,7 +644,11 @@ async fn test_execute_mint_transaction_twice_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -708,7 +736,11 @@ async fn test_execute_transaction_with_create_proposal_and_execute_in_single_slo .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); diff --git a/governance/program/tests/process_flag_transaction_error.rs b/governance/program/tests/process_flag_transaction_error.rs index aa4c39bd1f5..30f5f55ab56 100644 --- a/governance/program/tests/process_flag_transaction_error.rs +++ b/governance/program/tests/process_flag_transaction_error.rs @@ -38,7 +38,11 @@ async fn test_execute_flag_transaction_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -127,7 +131,11 @@ async fn test_execute_proposal_transaction_after_flagged_with_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -219,7 +227,11 @@ async fn test_execute_second_transaction_after_first_transaction_flagged_with_er .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -307,7 +319,11 @@ async fn test_flag_transaction_error_with_proposal_transaction_already_executed_ .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &mint_governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -394,7 +410,11 @@ async fn test_flag_transaction_error_with_owner_or_delegate_must_sign_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); diff --git a/governance/program/tests/process_remove_required_signatory.rs b/governance/program/tests/process_remove_required_signatory.rs new file mode 100644 index 00000000000..6c001de7833 --- /dev/null +++ b/governance/program/tests/process_remove_required_signatory.rs @@ -0,0 +1,207 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; + +use solana_program_test::tokio; + +use program_test::*; +use solana_program::pubkey::Pubkey; +use spl_governance::instruction::remove_required_signatory; + +use solana_sdk::signature::Signer; +use spl_governance::error::GovernanceError; +use spl_governance_tools::error::GovernanceToolsError; + +#[tokio::test] +async fn test_remove_required_signatory() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, realm_cookie, signatory) = + governance_test + .with_governance_with_required_signatory() + .await; + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let beneficiary = Pubkey::new_unique(); + + let proposal_transaction_cookie = governance_test + .with_remove_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory.pubkey(), + &beneficiary, + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &proposal_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + // Act + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // Assert + let after_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + let proposal_account = governance_test + .get_proposal_account(&after_proposal_cookie.address) + .await; + + assert_eq!(0, proposal_account.signatories_count); + + let governance_account = governance_test + .get_governance_account(&governance_cookie.address) + .await; + + assert_eq!(0, governance_account.required_signatories_count); +} + +#[tokio::test] +async fn test_remove_non_existing_required_signatory_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let (token_owner_record_cookie, mut governance_cookie, realm_cookie, signatory) = + governance_test + .with_governance_with_required_signatory() + .await; + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let beneficiary = Pubkey::new_unique(); + + let proposal_transaction_cookie = governance_test + .with_remove_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &Pubkey::new_unique(), + &beneficiary, + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &proposal_cookie, + &signatory, + ) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp past hold_up_time + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + // Act + let err = governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceToolsError::AccountDoesNotExist.into()); +} + +#[tokio::test] +pub async fn remove_required_signatory_from_governance_without_governance_signer_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory = Pubkey::new_unique(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut gwr_ix = remove_required_signatory( + &governance_test.program_id, + &governance_cookie.address, + &signatory, + &governance_test.bench.payer.pubkey(), + ); + + gwr_ix.accounts[0].is_signer = false; + + // Act + let err = governance_test + .bench + .process_transaction(&[gwr_ix], Some(&[])) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::GovernancePdaMustSign.into()); +} diff --git a/governance/program/tests/process_remove_signatory.rs b/governance/program/tests/process_remove_signatory.rs deleted file mode 100644 index 0fc04ce4766..00000000000 --- a/governance/program/tests/process_remove_signatory.rs +++ /dev/null @@ -1,293 +0,0 @@ -#![cfg(feature = "test-sbf")] - -mod program_test; - -use solana_program_test::tokio; - -use program_test::*; - -use spl_governance::{error::GovernanceError, state::enums::ProposalState}; - -#[tokio::test] -async fn test_remove_signatory() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - - let realm_cookie = governance_test.with_realm().await; - let governed_account_cookie = governance_test.with_governed_account().await; - - let token_owner_record_cookie = governance_test - .with_community_token_deposit(&realm_cookie) - .await - .unwrap(); - - let mut governance_cookie = governance_test - .with_governance( - &realm_cookie, - &governed_account_cookie, - &token_owner_record_cookie, - ) - .await - .unwrap(); - - let proposal_cookie = governance_test - .with_proposal(&token_owner_record_cookie, &mut governance_cookie) - .await - .unwrap(); - - let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - // Act - governance_test - .remove_signatory( - &proposal_cookie, - &token_owner_record_cookie, - &signatory_record_cookie, - ) - .await - .unwrap(); - - // Assert - let proposal_account = governance_test - .get_proposal_account(&proposal_cookie.address) - .await; - - assert_eq!(0, proposal_account.signatories_count); - assert_eq!(ProposalState::Draft, proposal_account.state); - - let signatory_account = governance_test - .bench - .get_account(&signatory_record_cookie.address) - .await; - - assert_eq!(None, signatory_account); -} - -#[tokio::test] -async fn test_remove_signatory_with_owner_or_delegate_must_sign_error() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - - let realm_cookie = governance_test.with_realm().await; - let governed_account_cookie = governance_test.with_governed_account().await; - - let mut token_owner_record_cookie = governance_test - .with_community_token_deposit(&realm_cookie) - .await - .unwrap(); - - let mut governance_cookie = governance_test - .with_governance( - &realm_cookie, - &governed_account_cookie, - &token_owner_record_cookie, - ) - .await - .unwrap(); - - let proposal_cookie = governance_test - .with_proposal(&token_owner_record_cookie, &mut governance_cookie) - .await - .unwrap(); - - let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - let other_token_owner_record_cookie = governance_test - .with_council_token_deposit(&realm_cookie) - .await - .unwrap(); - - token_owner_record_cookie.token_owner = other_token_owner_record_cookie.token_owner; - - // Act - let err = governance_test - .remove_signatory( - &proposal_cookie, - &token_owner_record_cookie, - &signatory_record_cookie, - ) - .await - .err() - .unwrap(); - - // Assert - assert_eq!( - err, - GovernanceError::GoverningTokenOwnerOrDelegateMustSign.into() - ); -} - -#[tokio::test] -async fn test_remove_signatory_with_invalid_proposal_owner_error() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - - let realm_cookie = governance_test.with_realm().await; - let governed_account_cookie = governance_test.with_governed_account().await; - - let mut token_owner_record_cookie = governance_test - .with_community_token_deposit(&realm_cookie) - .await - .unwrap(); - - let mut governance_cookie = governance_test - .with_governance( - &realm_cookie, - &governed_account_cookie, - &token_owner_record_cookie, - ) - .await - .unwrap(); - - let proposal_cookie = governance_test - .with_proposal(&token_owner_record_cookie, &mut governance_cookie) - .await - .unwrap(); - - let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - let other_token_owner_record_cookie = governance_test - .with_council_token_deposit(&realm_cookie) - .await - .unwrap(); - - token_owner_record_cookie.address = other_token_owner_record_cookie.address; - - // Act - let err = governance_test - .remove_signatory( - &proposal_cookie, - &token_owner_record_cookie, - &signatory_record_cookie, - ) - .await - .err() - .unwrap(); - - // Assert - assert_eq!(err, GovernanceError::InvalidProposalOwnerAccount.into()); -} - -#[tokio::test] -async fn test_remove_signatory_with_not_editable_error() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - - let realm_cookie = governance_test.with_realm().await; - let governed_account_cookie = governance_test.with_governed_account().await; - - let token_owner_record_cookie = governance_test - .with_community_token_deposit(&realm_cookie) - .await - .unwrap(); - - let mut governance_cookie = governance_test - .with_governance( - &realm_cookie, - &governed_account_cookie, - &token_owner_record_cookie, - ) - .await - .unwrap(); - - let proposal_cookie = governance_test - .with_proposal(&token_owner_record_cookie, &mut governance_cookie) - .await - .unwrap(); - - let signatory_record_cookie1 = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - let signatory_record_cookie2 = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - governance_test - .sign_off_proposal(&proposal_cookie, &signatory_record_cookie1) - .await - .unwrap(); - - // Act - let err = governance_test - .remove_signatory( - &proposal_cookie, - &token_owner_record_cookie, - &signatory_record_cookie2, - ) - .await - .err() - .unwrap(); - - // Assert - assert_eq!( - err, - GovernanceError::InvalidStateCannotEditSignatories.into() - ); -} - -#[tokio::test] -async fn test_remove_signatory_with_already_signed_error() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - - let realm_cookie = governance_test.with_realm().await; - let governed_account_cookie = governance_test.with_governed_account().await; - - let token_owner_record_cookie = governance_test - .with_community_token_deposit(&realm_cookie) - .await - .unwrap(); - - let mut governance_cookie = governance_test - .with_governance( - &realm_cookie, - &governed_account_cookie, - &token_owner_record_cookie, - ) - .await - .unwrap(); - - let proposal_cookie = governance_test - .with_proposal(&token_owner_record_cookie, &mut governance_cookie) - .await - .unwrap(); - - let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) - .await - .unwrap(); - - governance_test - .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) - .await - .unwrap(); - - // Act - let err = governance_test - .remove_signatory( - &proposal_cookie, - &token_owner_record_cookie, - &signatory_record_cookie, - ) - .await - .err() - .unwrap(); - - // Assert - assert_eq!( - err, - GovernanceError::InvalidStateCannotEditSignatories.into() - ); -} diff --git a/governance/program/tests/process_remove_transaction.rs b/governance/program/tests/process_remove_transaction.rs index 576d97fd4f5..6aec9c71e7a 100644 --- a/governance/program/tests/process_remove_transaction.rs +++ b/governance/program/tests/process_remove_transaction.rs @@ -43,7 +43,7 @@ async fn test_remove_transaction() { governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie, ) @@ -111,7 +111,7 @@ async fn test_replace_transaction() { governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie, ) @@ -184,7 +184,7 @@ async fn test_remove_front_transaction() { governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie, ) @@ -251,7 +251,7 @@ async fn test_remove_transaction_with_owner_or_delegate_must_sign_error() { // Act let err = governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie, ) @@ -306,7 +306,7 @@ async fn test_remove_transaction_with_proposal_not_editable_error() { // Act let err = governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie, ) @@ -371,7 +371,7 @@ async fn test_remove_transaction_with_proposal_transaction_from_other_proposal_e // Act let err = governance_test .remove_transaction( - &mut proposal_cookie, + &proposal_cookie, &token_owner_record_cookie, &proposal_transaction_cookie2, ) diff --git a/governance/program/tests/process_set_governance_config.rs b/governance/program/tests/process_set_governance_config.rs index 546e7adcffb..410580f1398 100644 --- a/governance/program/tests/process_set_governance_config.rs +++ b/governance/program/tests/process_set_governance_config.rs @@ -40,7 +40,11 @@ async fn test_set_governance_config() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -176,7 +180,11 @@ async fn test_set_governance_config_with_invalid_governance_authority_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); diff --git a/governance/program/tests/process_sign_off_proposal.rs b/governance/program/tests/process_sign_off_proposal.rs index f5a48724e87..1f4c3204628 100644 --- a/governance/program/tests/process_sign_off_proposal.rs +++ b/governance/program/tests/process_sign_off_proposal.rs @@ -6,6 +6,7 @@ use solana_program::pubkey::Pubkey; use solana_program_test::tokio; use program_test::*; +use solana_sdk::signature::{Keypair, Signer}; use spl_governance::{error::GovernanceError, state::enums::ProposalState}; use spl_governance_tools::error::GovernanceToolsError; @@ -37,7 +38,11 @@ async fn test_sign_off_proposal() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -96,7 +101,11 @@ async fn test_sign_off_proposal_with_signatory_must_sign_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -283,7 +292,11 @@ async fn test_sign_off_proposal_by_owner_with_existing_signatories_error() { .unwrap(); governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -329,7 +342,11 @@ async fn test_sign_off_proposal_with_non_existing_governance_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -377,7 +394,11 @@ async fn test_sign_off_proposal_with_non_existing_realm_error() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -395,3 +416,553 @@ async fn test_sign_off_proposal_with_non_existing_realm_error() { assert_eq!(err, GovernanceToolsError::AccountDoesNotExist.into()); } + +#[tokio::test] +async fn test_sign_off_proposal_with_required_signatory() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory = Keypair::new(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + // Act + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &new_proposal_cookie, + &signatory, + ) + .await + .unwrap(); + + // Assert + let proposal_account = governance_test + .get_proposal_account(&new_proposal_cookie.address) + .await; + + assert_eq!(1, proposal_account.signatories_signed_off_count); + assert_eq!(ProposalState::Voting, proposal_account.state); +} + +#[tokio::test] +async fn test_partial_sign_off_proposal_with_two_governance_signatories() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory_1 = Keypair::new(); + let signatory_2 = Keypair::new(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie_1 = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + let proposal_transaction_cookie_2 = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_2.pubkey(), + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie_2.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie_1) + .await + .unwrap(); + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie_2) + .await + .unwrap(); + + // End setup proposal + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory_2.pubkey(), + ) + .await + .unwrap(); + + // Act + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &new_proposal_cookie, + &signatory_1, + ) + .await + .unwrap(); + + // Assert + let proposal_account = governance_test + .get_proposal_account(&new_proposal_cookie.address) + .await; + + assert_eq!(1, proposal_account.signatories_signed_off_count); + assert_eq!(ProposalState::SigningOff, proposal_account.state); +} + +#[tokio::test] +async fn test_repeat_sign_off_proposal_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory_1 = Keypair::new(); + let signatory_2 = Keypair::new(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // Proposal to create required signatory 1 + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // Proposal to create required signatory 2 + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_2.pubkey(), + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &proposal_cookie, + &signatory_1, + ) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // End setup proposals + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory_2.pubkey(), + ) + .await + .unwrap(); + + // Sign off 1 + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &new_proposal_cookie, + &signatory_1, + ) + .await + .unwrap(); + governance_test.advance_clock().await; + + // Act + let err = governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &new_proposal_cookie, + &signatory_1, + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::SignatoryAlreadySignedOff.into()); +} + +#[tokio::test] +async fn test_sign_off_without_all_required_signatories_err() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let signatory_1 = Keypair::new(); + let signatory_2 = Keypair::new(); + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // Proposal to create required signatory 1 + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = governance_test + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // Proposal to create required signatory 2 + let mut proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let proposal_transaction_cookie = governance_test + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory_2.pubkey(), + ) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &proposal_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &proposal_cookie, + &signatory_1, + ) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test + .advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + governance_test + .execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + // End setup proposals + + let new_proposal_cookie = governance_test + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_signatory_record_for_required_signatory( + &new_proposal_cookie, + &governance_cookie, + &signatory_1.pubkey(), + ) + .await + .unwrap(); + + // Act + let err = governance_test + .do_required_signoff( + &realm_cookie, + &governance_cookie, + &new_proposal_cookie, + &signatory_1, + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::MissingRequiredSignatories.into()); +} diff --git a/governance/program/tests/program_test/cookies.rs b/governance/program/tests/program_test/cookies.rs index 67cc6ac9091..2cda808ac10 100644 --- a/governance/program/tests/program_test/cookies.rs +++ b/governance/program/tests/program_test/cookies.rs @@ -157,7 +157,7 @@ pub struct ProposalDepositCookie { pub struct SignatoryRecordCookie { pub address: Pubkey, pub account: SignatoryRecordV2, - pub signatory: Keypair, + pub signatory: Option, } #[derive(Debug)] diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index cec8c221281..525668263bb 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use std::str::FromStr; use borsh::BorshSerialize; @@ -18,14 +18,15 @@ use solana_sdk::signature::{Keypair, Signer}; use spl_governance::{ instruction::{ - add_signatory, cancel_proposal, cast_vote, complete_proposal, create_governance, - create_mint_governance, create_native_treasury, create_program_governance, create_proposal, - create_realm, create_token_governance, create_token_owner_record, deposit_governing_tokens, - execute_transaction, finalize_vote, flag_transaction_error, insert_transaction, - refund_proposal_deposit, relinquish_vote, remove_signatory, remove_transaction, - revoke_governing_tokens, set_governance_config, set_governance_delegate, - set_realm_authority, set_realm_config, sign_off_proposal, upgrade_program_metadata, - withdraw_governing_tokens, + add_required_signatory, add_signatory, cancel_proposal, cast_vote, complete_proposal, + create_governance, create_mint_governance, create_native_treasury, + create_program_governance, create_proposal, create_realm, create_token_governance, + create_token_owner_record, deposit_governing_tokens, execute_transaction, finalize_vote, + flag_transaction_error, insert_transaction, refund_proposal_deposit, relinquish_vote, + remove_required_signatory, remove_transaction, revoke_governing_tokens, + set_governance_config, set_governance_delegate, set_realm_authority, set_realm_config, + sign_off_proposal, upgrade_program_metadata, withdraw_governing_tokens, + AddSignatoryAuthority, }, processor::process_instruction, state::{ @@ -50,6 +51,7 @@ use spl_governance::{ GoverningTokenConfigAccountArgs, RealmConfig, RealmV2, SetRealmAuthorityAction, }, realm_config::{get_realm_config_address, GoverningTokenConfig, RealmConfigAccount}, + required_signatory::RequiredSignatory, signatory_record::{get_signatory_record_address, SignatoryRecordV2}, token_owner_record::{ get_token_owner_record_address, TokenOwnerRecordV2, TOKEN_OWNER_RECORD_LAYOUT_VERSION, @@ -58,7 +60,7 @@ use spl_governance::{ }, tools::{ bpf_loader_upgradeable::get_program_data_address, - structs::{Reserved110, Reserved120}, + structs::{Reserved110, Reserved119}, }, }; use spl_governance_addin_api::{ @@ -1501,7 +1503,8 @@ impl GovernanceProgramTest { governed_account: governed_account_cookie.address, config: governance_config.clone(), reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; @@ -1671,7 +1674,8 @@ impl GovernanceProgramTest { governed_account: governed_program_cookie.address, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; @@ -1792,7 +1796,8 @@ impl GovernanceProgramTest { governed_account: governed_mint_cookie.address, config: governance_config.clone(), reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; @@ -1873,7 +1878,8 @@ impl GovernanceProgramTest { governed_account: governed_token_cookie.address, config, reserved1: 0, - reserved_v2: Reserved120::default(), + reserved_v2: Reserved119::default(), + required_signatories_count: 0, active_proposal_count: 0, }; @@ -1935,7 +1941,11 @@ impl GovernanceProgramTest { .await?; let signatory_record_cookie = self - .with_signatory(&proposal_cookie, token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + governance_cookie, + token_owner_record_cookie, + ) .await?; self.sign_off_proposal(&proposal_cookie, &signatory_record_cookie) @@ -2067,6 +2077,7 @@ impl GovernanceProgramTest { vote_threshold: None, reserved: [0; 64], + reserved1: 0, }; @@ -2107,15 +2118,19 @@ impl GovernanceProgramTest { pub async fn with_signatory( &mut self, proposal_cookie: &ProposalCookie, + governance_cookie: &GovernanceCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { let signatory = Keypair::new(); let add_signatory_ix = add_signatory( &self.program_id, + &governance_cookie.address, &proposal_cookie.address, - &token_owner_record_cookie.address, - &token_owner_record_cookie.token_owner.pubkey(), + &AddSignatoryAuthority::ProposalOwner { + token_owner_record: token_owner_record_cookie.address, + governance_authority: token_owner_record_cookie.token_owner.pubkey(), + }, &self.bench.payer.pubkey(), &signatory.pubkey(), ); @@ -2144,37 +2159,12 @@ impl GovernanceProgramTest { let signatory_record_cookie = SignatoryRecordCookie { address: signatory_record_address, account: signatory_record_data, - signatory, + signatory: Some(signatory), }; Ok(signatory_record_cookie) } - #[allow(dead_code)] - pub async fn remove_signatory( - &mut self, - proposal_cookie: &ProposalCookie, - token_owner_record_cookie: &TokenOwnerRecordCookie, - signatory_record_cookie: &SignatoryRecordCookie, - ) -> Result<(), ProgramError> { - let remove_signatory_ix = remove_signatory( - &self.program_id, - &proposal_cookie.address, - &token_owner_record_cookie.address, - &token_owner_record_cookie.token_owner.pubkey(), - &signatory_record_cookie.account.signatory, - &token_owner_record_cookie.token_owner.pubkey(), - ); - - self.bench - .process_transaction( - &[remove_signatory_ix], - Some(&[&token_owner_record_cookie.token_owner]), - ) - .await?; - - Ok(()) - } #[allow(dead_code)] pub async fn sign_off_proposal_by_owner( &mut self, @@ -2247,13 +2237,13 @@ impl GovernanceProgramTest { &proposal_cookie.realm, &proposal_cookie.account.governance, &proposal_cookie.address, - &signatory_record_cookie.signatory.pubkey(), + &signatory_record_cookie.signatory.as_ref().unwrap().pubkey(), None, ); instruction_override(&mut sign_off_proposal_ix); - let default_signers = &[&signatory_record_cookie.signatory]; + let default_signers = &[signatory_record_cookie.signatory.as_ref().unwrap()]; let signers = signers_override.unwrap_or(default_signers); self.bench @@ -2739,7 +2729,7 @@ impl GovernanceProgramTest { let hold_up_time = hold_up_time.unwrap_or(15); let instruction_data: InstructionData = instruction.clone().into(); - let mut yes_option = &mut proposal_cookie.account.options[0]; + let yes_option = &mut proposal_cookie.account.options[0]; let transaction_index = index.unwrap_or(yes_option.transactions_next_index); @@ -2804,10 +2794,202 @@ impl GovernanceProgramTest { } #[allow(dead_code)] - pub async fn remove_transaction( + pub async fn with_add_required_signatory_transaction( + &mut self, + proposal_cookie: &mut ProposalCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, + governance: &GovernanceCookie, + signatory: &Pubkey, + ) -> Result { + let mut gwr_ix = add_required_signatory( + &self.program_id, + &governance.address, + &self.bench.payer.pubkey(), + signatory, + ); + + self.with_proposal_transaction( + proposal_cookie, + token_owner_record_cookie, + 0, + None, + &mut gwr_ix, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn with_remove_required_signatory_transaction( &mut self, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, + governance: &GovernanceCookie, + signatory: &Pubkey, + beneficiary: &Pubkey, + ) -> Result { + let mut ix = remove_required_signatory( + &self.program_id, + &governance.address, + signatory, + beneficiary, + ); + + self.with_proposal_transaction( + proposal_cookie, + token_owner_record_cookie, + 0, + None, + &mut ix, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn do_required_signoff( + &mut self, + realm_cookie: &RealmCookie, + governance_cookie: &GovernanceCookie, + proposal_cookie: &ProposalCookie, + signatory: &Keypair, + ) -> Result<(), ProgramError> { + let ix = sign_off_proposal( + &self.program_id, + &realm_cookie.address, + &governance_cookie.address, + &proposal_cookie.address, + &signatory.pubkey(), + None, + ); + + self.bench + .process_transaction(&[ix], Some(&[signatory])) + .await?; + + Ok(()) + } + + #[allow(dead_code)] + pub async fn with_signatory_record_for_required_signatory( + &mut self, + proposal_cookie: &ProposalCookie, + governance: &GovernanceCookie, + signatory: &Pubkey, + ) -> Result { + let create_signatory_record_ix = add_signatory( + &self.program_id, + &governance.address, + &proposal_cookie.address, + &AddSignatoryAuthority::None, + &self.bench.payer.pubkey(), + signatory, + ); + + self.bench + .process_transaction(&[create_signatory_record_ix], Some(&[])) + .await?; + + let signatory_record_address = + get_signatory_record_address(&self.program_id, &proposal_cookie.address, signatory); + + let signatory_record_data = SignatoryRecordV2 { + account_type: GovernanceAccountType::SignatoryRecordV2, + proposal: proposal_cookie.address, + signatory: *signatory, + signed_off: false, + reserved_v2: [0; 8], + }; + + let signatory_record_cookie = SignatoryRecordCookie { + address: signatory_record_address, + account: signatory_record_data, + signatory: None, + }; + + Ok(signatory_record_cookie) + } + + #[allow(dead_code)] + pub async fn with_governance_with_required_signatory( + &mut self, + ) -> ( + TokenOwnerRecordCookie, + GovernanceCookie, + RealmCookie, + Keypair, + ) { + let realm_cookie = self.with_realm().await; + let governed_account_cookie = self.with_governed_account().await; + + let signatory = Keypair::new(); + + let token_owner_record_cookie = self + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = self + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let mut proposal_cookie = self + .with_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + let signatory_record_cookie = self + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_transaction_cookie = self + .with_add_required_signatory_transaction( + &mut proposal_cookie, + &token_owner_record_cookie, + &governance_cookie, + &signatory.pubkey(), + ) + .await + .unwrap(); + + self.sign_off_proposal(&proposal_cookie, &signatory_record_cookie) + .await + .unwrap(); + + self.with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + self.advance_clock_by_min_timespan(proposal_transaction_cookie.account.hold_up_time as u64) + .await; + + self.execute_proposal_transaction(&proposal_cookie, &proposal_transaction_cookie) + .await + .unwrap(); + + ( + token_owner_record_cookie, + governance_cookie, + realm_cookie, + signatory, + ) + } + + #[allow(dead_code)] + pub async fn remove_transaction( + &mut self, + proposal_cookie: &ProposalCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, proposal_transaction_cookie: &ProposalTransactionCookie, ) -> Result<(), ProgramError> { let remove_transaction_ix = remove_transaction( @@ -2945,6 +3127,16 @@ impl GovernanceProgramTest { .await } + #[allow(dead_code)] + pub async fn get_required_signatory_account( + &mut self, + required_signatory_address: &Pubkey, + ) -> RequiredSignatory { + self.bench + .get_borsh_account::(required_signatory_address) + .await + } + #[allow(dead_code)] pub async fn get_signatory_record_account( &mut self, @@ -3147,7 +3339,7 @@ impl GovernanceProgramTest { #[allow(dead_code)] pub async fn complete_proposal( &mut self, - proposal_cookie: &mut ProposalCookie, + proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result<(), ProgramError> { let complete_proposal_authority = token_owner_record_cookie.get_governance_authority(); diff --git a/governance/program/tests/use_proposals_with_multiple_options.rs b/governance/program/tests/use_proposals_with_multiple_options.rs index 987aa38916e..05da555becf 100644 --- a/governance/program/tests/use_proposals_with_multiple_options.rs +++ b/governance/program/tests/use_proposals_with_multiple_options.rs @@ -272,7 +272,11 @@ async fn test_vote_on_none_executable_single_choice_proposal_with_multiple_optio .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -375,7 +379,11 @@ async fn test_vote_on_none_executable_multi_choice_proposal_with_multiple_option .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie, + ) .await .unwrap(); @@ -505,7 +513,11 @@ async fn test_vote_on_executable_proposal_with_multiple_options_and_partial_succ .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie1) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie1, + ) .await .unwrap(); @@ -704,7 +716,11 @@ async fn test_execute_proposal_with_multiple_options_and_partial_success() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie1) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie1, + ) .await .unwrap(); @@ -913,7 +929,11 @@ async fn test_try_execute_proposal_with_multiple_options_and_full_deny() { .unwrap(); let signatory_record_cookie = governance_test - .with_signatory(&proposal_cookie, &token_owner_record_cookie1) + .with_signatory( + &proposal_cookie, + &governance_cookie, + &token_owner_record_cookie1, + ) .await .unwrap(); diff --git a/governance/test-sdk/Cargo.toml b/governance/test-sdk/Cargo.toml index 47d85bb46d1..5a20bec2d3b 100644 --- a/governance/test-sdk/Cargo.toml +++ b/governance/test-sdk/Cargo.toml @@ -14,10 +14,10 @@ borsh = "0.10" lazy_static = "1.4.0" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program = "1.17.2" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } thiserror = "1.0" diff --git a/governance/test-sdk/src/lib.rs b/governance/test-sdk/src/lib.rs index 3cf7a72b554..2587f4c55a4 100644 --- a/governance/test-sdk/src/lib.rs +++ b/governance/test-sdk/src/lib.rs @@ -1,10 +1,10 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use std::borrow::Borrow; use borsh::{BorshDeserialize, BorshSerialize}; use cookies::{TokenAccountCookie, WalletCookie}; use solana_program::{ - borsh::try_from_slice_unchecked, clock::Clock, instruction::Instruction, + borsh0_10::try_from_slice_unchecked, clock::Clock, instruction::Instruction, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent, stake_history::Epoch, system_instruction, system_program, sysvar, }; diff --git a/governance/tools/Cargo.toml b/governance/tools/Cargo.toml index e9596ae1818..a6085dc2336 100644 --- a/governance/tools/Cargo.toml +++ b/governance/tools/Cargo.toml @@ -13,8 +13,8 @@ bincode = "1.3.2" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } thiserror = "1.0" diff --git a/governance/tools/src/account.rs b/governance/tools/src/account.rs index c2b504fa324..a5e267b241b 100644 --- a/governance/tools/src/account.rs +++ b/governance/tools/src/account.rs @@ -3,7 +3,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{ account_info::AccountInfo, - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, msg, program::invoke, program::invoke_signed, diff --git a/instruction-padding/program/Cargo.toml b/instruction-padding/program/Cargo.toml index c3813ef5e6d..2e6f2d9eb5f 100644 --- a/instruction-padding/program/Cargo.toml +++ b/instruction-padding/program/Cargo.toml @@ -13,12 +13,12 @@ no-entrypoint = [] test-sbf = [] [dependencies] -num_enum = "0.6.1" -solana-program = "1.16.1" +num_enum = "0.7.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/instruction-padding/program/src/instruction.rs b/instruction-padding/program/src/instruction.rs index fd092cfcfda..0c666509206 100644 --- a/instruction-padding/program/src/instruction.rs +++ b/instruction-padding/program/src/instruction.rs @@ -89,7 +89,7 @@ pub fn noop( return Err(ProgramError::InvalidAccountData); } let mut accounts = Vec::with_capacity(num_accounts); - accounts.extend(padding_accounts.into_iter()); + accounts.extend(padding_accounts); Ok(Instruction { program_id, @@ -128,7 +128,7 @@ pub fn wrap_instruction( .try_into() .map_err(|_| ProgramError::InvalidInstructionData)?; data.extend(data_size.to_le_bytes().iter()); - data.extend(instruction.data.into_iter()); + data.extend(instruction.data); for i in 0..padding_data { data.push(i.checked_rem(u8::MAX as u32).unwrap() as u8); } @@ -146,9 +146,9 @@ pub fn wrap_instruction( return Err(ProgramError::InvalidAccountData); } let mut accounts = Vec::with_capacity(num_accounts); - accounts.extend(instruction.accounts.into_iter()); + accounts.extend(instruction.accounts); accounts.push(AccountMeta::new_readonly(instruction.program_id, false)); - accounts.extend(padding_accounts.into_iter()); + accounts.extend(padding_accounts); Ok(Instruction { program_id, diff --git a/libraries/concurrent-merkle-tree/Cargo.toml b/libraries/concurrent-merkle-tree/Cargo.toml index cdb750a6b0d..d541ae8fe8a 100644 --- a/libraries/concurrent-merkle-tree/Cargo.toml +++ b/libraries/concurrent-merkle-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-concurrent-merkle-tree" -version = "0.1.3" +version = "0.2.0" description = "Solana Program Library Concurrent Merkle Tree" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -12,9 +12,9 @@ log = [] sol-log = [ "log" ] [dependencies] -solana-program = "1.10.33" -bytemuck = "1.13.1" -thiserror = "1.0.40" +solana-program = "1.16" +bytemuck = "1.14" +thiserror = "1.0.50" [dev-dependencies] rand_distr = "0.4.3" diff --git a/libraries/concurrent-merkle-tree/src/concurrent_merkle_tree.rs b/libraries/concurrent-merkle-tree/src/concurrent_merkle_tree.rs index 5bd5c5ee854..ea75537284d 100644 --- a/libraries/concurrent-merkle-tree/src/concurrent_merkle_tree.rs +++ b/libraries/concurrent-merkle-tree/src/concurrent_merkle_tree.rs @@ -46,6 +46,7 @@ fn check_leaf_index(leaf_index: u32, max_depth: usize) -> Result<(), ConcurrentM /// An additional key property of ConcurrentMerkleTree is support for [append](ConcurrentMerkleTree::append) operations, /// which do not require any proofs to be passed. This is accomplished by keeping track of the /// proof to the rightmost leaf in the tree (`rightmost_proof`). +#[repr(C)] #[derive(Copy, Clone)] pub struct ConcurrentMerkleTree { pub sequence_number: u64, @@ -99,13 +100,13 @@ impl return Err(ConcurrentMerkleTreeError::TreeAlreadyInitialized); } let mut rightmost_proof = Path::default(); - let mut empty_node_cache = Box::new([Node::default(); MAX_DEPTH]); + let empty_node_cache = [Node::default(); MAX_DEPTH]; for (i, node) in rightmost_proof.proof.iter_mut().enumerate() { - *node = empty_node_cached::(i as u32, &mut empty_node_cache); + *node = empty_node_cached::(i as u32, &empty_node_cache); } let mut path = [Node::default(); MAX_DEPTH]; for (i, node) in path.iter_mut().enumerate() { - *node = empty_node_cached::(i as u32, &mut empty_node_cache); + *node = empty_node_cached::(i as u32, &empty_node_cache); } self.change_logs[0].root = empty_node(MAX_DEPTH as u32); self.change_logs[0].path = path; @@ -160,10 +161,8 @@ impl if !self.is_initialized() { return Err(ConcurrentMerkleTreeError::TreeNotInitialized); } - let mut empty_node_cache = Box::new([EMPTY; MAX_DEPTH]); - if self.get_root() - != empty_node_cached::(MAX_DEPTH as u32, &mut empty_node_cache) - { + let empty_node_cache = [EMPTY; MAX_DEPTH]; + if self.get_root() != empty_node_cached::(MAX_DEPTH as u32, &empty_node_cache) { return Err(ConcurrentMerkleTreeError::TreeNonEmpty); } Ok(()) @@ -261,14 +260,14 @@ impl let intersection = self.rightmost_proof.index.trailing_zeros() as usize; let mut change_list = [EMPTY; MAX_DEPTH]; let mut intersection_node = self.rightmost_proof.leaf; - let mut empty_node_cache = Box::new([Node::default(); MAX_DEPTH]); + let empty_node_cache = [Node::default(); MAX_DEPTH]; for (i, cl_item) in change_list.iter_mut().enumerate().take(MAX_DEPTH) { *cl_item = node; match i { i if i < intersection => { // Compute proof to the appended node from empty nodes - let sibling = empty_node_cached::(i as u32, &mut empty_node_cache); + let sibling = empty_node_cached::(i as u32, &empty_node_cache); hash_to_parent( &mut intersection_node, &self.rightmost_proof.proof[i], diff --git a/libraries/concurrent-merkle-tree/src/lib.rs b/libraries/concurrent-merkle-tree/src/lib.rs index 7df3f09308c..cb0c290caf5 100644 --- a/libraries/concurrent-merkle-tree/src/lib.rs +++ b/libraries/concurrent-merkle-tree/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! # Concurrent Merkle Tree //! //! This crate is a Solana-optimized implementation of the diff --git a/libraries/concurrent-merkle-tree/src/node.rs b/libraries/concurrent-merkle-tree/src/node.rs index 85fcf7199e2..876ce61f095 100644 --- a/libraries/concurrent-merkle-tree/src/node.rs +++ b/libraries/concurrent-merkle-tree/src/node.rs @@ -8,11 +8,11 @@ pub const EMPTY: Node = [0_u8; 32]; /// Calculates the hash of empty nodes up to level i pub fn empty_node(level: u32) -> Node { - empty_node_cached::<0>(level, &mut Box::new([])) + empty_node_cached::<0>(level, &[]) } /// Calculates and caches the hash of empty nodes up to level i -pub fn empty_node_cached(level: u32, cache: &mut Box<[Node; N]>) -> Node { +pub fn empty_node_cached(level: u32, cache: &[Node; N]) -> Node { let mut data = EMPTY; if level != 0 { let target = (level - 1) as usize; diff --git a/libraries/concurrent-merkle-tree/tests/tests.rs b/libraries/concurrent-merkle-tree/tests/tests.rs index 9f0d8acbaad..e1f2aa5248f 100644 --- a/libraries/concurrent-merkle-tree/tests/tests.rs +++ b/libraries/concurrent-merkle-tree/tests/tests.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use rand::thread_rng; use rand::{self, Rng}; use spl_concurrent_merkle_tree::concurrent_merkle_tree::ConcurrentMerkleTree; diff --git a/libraries/discriminator/Cargo.toml b/libraries/discriminator/Cargo.toml index 90072db8c08..6b4f2bf67dc 100644 --- a/libraries/discriminator/Cargo.toml +++ b/libraries/discriminator/Cargo.toml @@ -12,8 +12,8 @@ borsh = ["dep:borsh"] [dependencies] borsh = { version = "0.10", optional = true } -bytemuck = { version = "1.13.1", features = ["derive"] } -solana-program = "1.16.1" +bytemuck = { version = "1.14.0", features = ["derive"] } +solana-program = "1.17.2" spl-discriminator-derive = { version = "0.1.0", path = "./derive" } [lib] diff --git a/libraries/discriminator/README.md b/libraries/discriminator/README.md index 104783b6b08..8d31cc70667 100644 --- a/libraries/discriminator/README.md +++ b/libraries/discriminator/README.md @@ -40,11 +40,11 @@ pub trait SplDiscriminate { The `SplDiscriminate` derive macro is a particularly useful tool for those who wish to derive their 8-byte discriminator from a particular string literal. Typically, you would have to run a hash function against the string literal, then copy the first 8 bytes, and then hard-code those bytes into a statement like the one above. -Instead, you can simply annotate a struct or enum with `SplDiscriminate` and provide a **namespace** via the `discriminator_namespace` attribute, and the macro will automatically derive the 8-byte discriminator for you! +Instead, you can simply annotate a struct or enum with `SplDiscriminate` and provide a **hash input** via the `discriminator_hash_input` attribute, and the macro will automatically derive the 8-byte discriminator for you! ```rust -#[derive(SplDiscriminate)] // Implements `SplDiscriminate` for your struct/enum using your declared string literal namespace -#[discriminator_namespace("some_discriminator_namespace")] +#[derive(SplDiscriminate)] // Implements `SplDiscriminate` for your struct/enum using your declared string literal hash_input +#[discriminator_hash_input("some_discriminator_hash_input")] pub struct MyInstruction1 { arg1: String, arg2: u8, diff --git a/libraries/discriminator/derive/Cargo.toml b/libraries/discriminator/derive/Cargo.toml index bfdc1680655..ef6c62d53f7 100644 --- a/libraries/discriminator/derive/Cargo.toml +++ b/libraries/discriminator/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-discriminator-derive" -version = "0.1.0" +version = "0.1.1" description = "Derive macro library for the `spl-discriminator` library" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -16,4 +16,4 @@ syn = { version = "2.0", features = ["full"] } proc-macro = true [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] \ No newline at end of file +targets = ["x86_64-unknown-linux-gnu"] diff --git a/libraries/discriminator/syn/Cargo.toml b/libraries/discriminator/syn/Cargo.toml index be3e0ed6a8c..bd5c0a30620 100644 --- a/libraries/discriminator/syn/Cargo.toml +++ b/libraries/discriminator/syn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-discriminator-syn" -version = "0.1.0" +version = "0.1.1" description = "Token parsing and generating library for the `spl-discriminator` library" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -10,12 +10,9 @@ edition = "2021" [dependencies] proc-macro2 = "1.0" quote = "1.0" -solana-program = "1.16.1" +sha2 = "0.10" syn = { version = "2.0", features = ["full"] } thiserror = "1.0" -[lib] -crate-type = ["cdylib", "lib"] - [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] \ No newline at end of file +targets = ["x86_64-unknown-linux-gnu"] diff --git a/libraries/discriminator/syn/src/lib.rs b/libraries/discriminator/syn/src/lib.rs index e8810beb911..db555916573 100644 --- a/libraries/discriminator/syn/src/lib.rs +++ b/libraries/discriminator/syn/src/lib.rs @@ -10,7 +10,7 @@ use { crate::{error::SplDiscriminateError, parser::parse_hash_input}, proc_macro2::{Span, TokenStream}, quote::{quote, ToTokens}, - solana_program::hash, + sha2::{Digest, Sha256}, syn::{parse::Parse, Generics, Ident, Item, ItemEnum, ItemStruct, LitByteStr, WhereClause}, }; @@ -102,7 +102,7 @@ impl From<&SplDiscriminateBuilder> for TokenStream { /// Returns the bytes for the TLV hash_input discriminator fn get_discriminator_bytes(hash_input: &str) -> LitByteStr { LitByteStr::new( - &hash::hashv(&[hash_input.as_bytes()]).to_bytes()[..8], + &Sha256::digest(hash_input.as_bytes())[..8], Span::call_site(), ) } diff --git a/libraries/math/Cargo.toml b/libraries/math/Cargo.toml index 03a86601450..495ac124835 100644 --- a/libraries/math/Cargo.toml +++ b/libraries/math/Cargo.toml @@ -15,15 +15,15 @@ test-sbf = [] borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" thiserror = "1.0" uint = "0.9" [dev-dependencies] -proptest = "1.2.0" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -libm = "0.2.7" +proptest = "1.3.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +libm = "0.2.8" [lib] crate-type = ["cdylib", "lib"] diff --git a/libraries/math/src/approximations.rs b/libraries/math/src/approximations.rs index 4b6bb218d77..6a784d20bf2 100644 --- a/libraries/math/src/approximations.rs +++ b/libraries/math/src/approximations.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! Approximation calculations use { diff --git a/libraries/math/src/instruction.rs b/libraries/math/src/instruction.rs index adb768a4498..dd45073cb99 100644 --- a/libraries/math/src/instruction.rs +++ b/libraries/math/src/instruction.rs @@ -97,6 +97,16 @@ pub enum MathInstruction { argument: f32, }, + /// Pow two float values + /// + /// No accounts required for this instruction + F64Pow { + /// The base + base: f64, + /// The exponent + exponent: f64, + }, + /// Don't do anything for comparison /// /// No accounts required for this instruction @@ -219,6 +229,17 @@ pub fn f32_normal_cdf(argument: f32) -> Instruction { } } +/// Create F64Pow instruction +pub fn f64_pow(base: f64, exponent: f64) -> Instruction { + Instruction { + program_id: id(), + accounts: vec![], + data: MathInstruction::F64Pow { base, exponent } + .try_to_vec() + .unwrap(), + } +} + /// Create Noop instruction pub fn noop() -> Instruction { Instruction { diff --git a/libraries/math/src/precise_number.rs b/libraries/math/src/precise_number.rs index e17498bd8a4..cf2c1eeb8de 100644 --- a/libraries/math/src/precise_number.rs +++ b/libraries/math/src/precise_number.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! Defines PreciseNumber, a U256 wrapper with float-like operations use crate::uint::U256; diff --git a/libraries/math/src/processor.rs b/libraries/math/src/processor.rs index 3b307889be8..ebdd2823a53 100644 --- a/libraries/math/src/processor.rs +++ b/libraries/math/src/processor.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! Program state processor use { @@ -145,6 +145,18 @@ pub fn process_instruction( msg!("{}", result as u64); Ok(()) } + MathInstruction::F64Pow { base, exponent } => { + msg!("Calculating f64 Pow"); + sol_log_compute_units(); + let result = base.powi(exponent as i32); + sol_log_compute_units(); + msg!("{}", result as u64); + sol_log_compute_units(); + let result = base.powf(exponent); + sol_log_compute_units(); + msg!("{}", result as u64); + Ok(()) + } MathInstruction::Noop => { msg!("Do nothing"); msg!("{}", 0_u64); diff --git a/libraries/math/src/uint.rs b/libraries/math/src/uint.rs index b1610ccdd2d..da7adc00d20 100644 --- a/libraries/math/src/uint.rs +++ b/libraries/math/src/uint.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] //! Large uint types // required for clippy diff --git a/libraries/math/tests/instruction_count.rs b/libraries/math/tests/instruction_count.rs index e2353e6da3b..bf97d1e806f 100644 --- a/libraries/math/tests/instruction_count.rs +++ b/libraries/math/tests/instruction_count.rs @@ -194,6 +194,22 @@ async fn test_f32_normal_cdf() { banks_client.process_transaction(transaction).await.unwrap(); } +#[tokio::test] +async fn test_f64_pow() { + let mut pc = ProgramTest::new("spl_math", id(), processor!(process_instruction)); + + pc.set_compute_max_units(30_000); + + let (mut banks_client, payer, recent_blockhash) = pc.start().await; + + let mut transaction = Transaction::new_with_payer( + &[instruction::f64_pow(50_f64, 10.5_f64)], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer], recent_blockhash); + banks_client.process_transaction(transaction).await.unwrap(); +} + #[tokio::test] async fn test_noop() { let mut pc = ProgramTest::new("spl_math", id(), processor!(process_instruction)); diff --git a/libraries/merkle-tree-reference/Cargo.toml b/libraries/merkle-tree-reference/Cargo.toml index 0e08bb65d16..847f448bdea 100644 --- a/libraries/merkle-tree-reference/Cargo.toml +++ b/libraries/merkle-tree-reference/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] solana-program = "1.10.33" -thiserror = "1.0.40" +thiserror = "1.0.50" [lib] crate-type = ["cdylib", "lib"] diff --git a/libraries/merkle-tree-reference/src/lib.rs b/libraries/merkle-tree-reference/src/lib.rs index 23f8019cc54..0828517b574 100644 --- a/libraries/merkle-tree-reference/src/lib.rs +++ b/libraries/merkle-tree-reference/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use solana_program::keccak::hashv; use std::cell::RefCell; use std::collections::VecDeque; diff --git a/libraries/pod/Cargo.toml b/libraries/pod/Cargo.toml new file mode 100644 index 00000000000..0b59ce1f03a --- /dev/null +++ b/libraries/pod/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "spl-pod" +version = "0.1.0" +description = "Solana Program Library Plain Old Data (Pod)" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[features] +serde-traits = ["dep:serde", "dep:base64"] +borsh = ["dep:borsh"] + +[dependencies] +base64 = { version = "0.21.5", optional = true } +borsh = { version = "0.10", optional = true } +bytemuck = { version = "1.14.0" } +serde = { version = "1.0.190", optional = true } +solana-program = "1.17.2" +solana-zk-token-sdk = "1.17.2" +spl-program-error = { version = "0.3", path = "../program-error" } + +[dev-dependencies] +serde_json = "1.0.108" + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/libraries/pod/README.md b/libraries/pod/README.md new file mode 100644 index 00000000000..984718b5cbf --- /dev/null +++ b/libraries/pod/README.md @@ -0,0 +1,3 @@ +# Pod + +This library contains types shared by SPL libraries that implement the `Pod` trait from `bytemuck` and utils for working with these types. diff --git a/libraries/pod/src/bytemuck.rs b/libraries/pod/src/bytemuck.rs new file mode 100644 index 00000000000..adb12845933 --- /dev/null +++ b/libraries/pod/src/bytemuck.rs @@ -0,0 +1,53 @@ +//! wrappers for bytemuck functions + +use {bytemuck::Pod, solana_program::program_error::ProgramError}; + +/// On-chain size of a `Pod` type +pub const fn pod_get_packed_len() -> usize { + std::mem::size_of::() +} + +/// Convert a `Pod` into a slice of bytes (zero copy) +pub fn pod_bytes_of(t: &T) -> &[u8] { + bytemuck::bytes_of(t) +} + +/// Convert a slice of bytes into a `Pod` (zero copy) +pub fn pod_from_bytes(bytes: &[u8]) -> Result<&T, ProgramError> { + bytemuck::try_from_bytes(bytes).map_err(|_| ProgramError::InvalidArgument) +} + +/// Maybe convert a slice of bytes into a `Pod` (zero copy) +/// +/// Returns `None` if the slice is empty, or else `Err` if input length is not equal to +/// `pod_get_packed_len::()`. +/// This function exists primarily because `Option` is not a `Pod`. +pub fn pod_maybe_from_bytes(bytes: &[u8]) -> Result, ProgramError> { + if bytes.is_empty() { + Ok(None) + } else { + bytemuck::try_from_bytes(bytes) + .map(Some) + .map_err(|_| ProgramError::InvalidArgument) + } +} + +/// Convert a slice of bytes into a mutable `Pod` (zero copy) +pub fn pod_from_bytes_mut(bytes: &mut [u8]) -> Result<&mut T, ProgramError> { + bytemuck::try_from_bytes_mut(bytes).map_err(|_| ProgramError::InvalidArgument) +} + +/// Convert a slice of bytes into a `Pod` slice (zero copy) +pub fn pod_slice_from_bytes(bytes: &[u8]) -> Result<&[T], ProgramError> { + bytemuck::try_cast_slice(bytes).map_err(|_| ProgramError::InvalidArgument) +} + +/// Convert a slice of bytes into a mutable `Pod` slice (zero copy) +pub fn pod_slice_from_bytes_mut(bytes: &mut [u8]) -> Result<&mut [T], ProgramError> { + bytemuck::try_cast_slice_mut(bytes).map_err(|_| ProgramError::InvalidArgument) +} + +/// Convert a `Pod` slice into a single slice of bytes +pub fn pod_slice_to_bytes(slice: &[T]) -> &[u8] { + bytemuck::cast_slice(slice) +} diff --git a/libraries/pod/src/error.rs b/libraries/pod/src/error.rs new file mode 100644 index 00000000000..35740a43c2a --- /dev/null +++ b/libraries/pod/src/error.rs @@ -0,0 +1,16 @@ +//! Error types +use spl_program_error::*; + +/// Errors that may be returned by the spl-pod library. +#[spl_program_error] +pub enum PodSliceError { + /// Error in checked math operation + #[error("Error in checked math operation")] + CalculationFailure, + /// Provided byte buffer too small for expected type + #[error("Provided byte buffer too small for expected type")] + BufferTooSmall, + /// Provided byte buffer too large for expected type + #[error("Provided byte buffer too large for expected type")] + BufferTooLarge, +} diff --git a/libraries/pod/src/lib.rs b/libraries/pod/src/lib.rs new file mode 100644 index 00000000000..00cb6be8597 --- /dev/null +++ b/libraries/pod/src/lib.rs @@ -0,0 +1,11 @@ +//! Crate containing `Pod` types and `bytemuck` utils used in SPL + +pub mod bytemuck; +pub mod error; +pub mod optional_keys; +pub mod primitives; +pub mod slice; + +// Export current sdk types for downstream users building with a different sdk +// version +pub use solana_program; diff --git a/libraries/pod/src/optional_keys.rs b/libraries/pod/src/optional_keys.rs new file mode 100644 index 00000000000..3622929615f --- /dev/null +++ b/libraries/pod/src/optional_keys.rs @@ -0,0 +1,349 @@ +//! Optional pubkeys that can be used a `Pod`s +use { + bytemuck::{Pod, Zeroable}, + solana_program::{program_error::ProgramError, program_option::COption, pubkey::Pubkey}, + solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, +}; + +#[cfg(feature = "serde-traits")] +use { + base64::{prelude::BASE64_STANDARD, Engine}, + serde::de::{Error, Unexpected, Visitor}, + serde::{Deserialize, Deserializer, Serialize, Serializer}, + std::{convert::TryFrom, fmt, str::FromStr}, +}; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; + +/// A Pubkey that encodes `None` as all `0`, meant to be usable as a Pod type, similar to all +/// NonZero* number types from the bytemuck library. +#[cfg_attr( + feature = "borsh", + derive(BorshDeserialize, BorshSerialize, BorshSchema) +)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct OptionalNonZeroPubkey(Pubkey); +impl TryFrom> for OptionalNonZeroPubkey { + type Error = ProgramError; + fn try_from(p: Option) -> Result { + match p { + None => Ok(Self(Pubkey::default())), + Some(pubkey) => { + if pubkey == Pubkey::default() { + Err(ProgramError::InvalidArgument) + } else { + Ok(Self(pubkey)) + } + } + } + } +} +impl TryFrom> for OptionalNonZeroPubkey { + type Error = ProgramError; + fn try_from(p: COption) -> Result { + match p { + COption::None => Ok(Self(Pubkey::default())), + COption::Some(pubkey) => { + if pubkey == Pubkey::default() { + Err(ProgramError::InvalidArgument) + } else { + Ok(Self(pubkey)) + } + } + } + } +} +impl From for Option { + fn from(p: OptionalNonZeroPubkey) -> Self { + if p.0 == Pubkey::default() { + None + } else { + Some(p.0) + } + } +} +impl From for COption { + fn from(p: OptionalNonZeroPubkey) -> Self { + if p.0 == Pubkey::default() { + COption::None + } else { + COption::Some(p.0) + } + } +} + +#[cfg(feature = "serde-traits")] +impl Serialize for OptionalNonZeroPubkey { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + if self.0 == Pubkey::default() { + s.serialize_none() + } else { + s.serialize_some(&self.0.to_string()) + } + } +} + +#[cfg(feature = "serde-traits")] +/// Visitor for deserializing OptionalNonZeroPubkey +struct OptionalNonZeroPubkeyVisitor; + +#[cfg(feature = "serde-traits")] +impl<'de> Visitor<'de> for OptionalNonZeroPubkeyVisitor { + type Value = OptionalNonZeroPubkey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a Pubkey in base58 or `null`") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let pkey = Pubkey::from_str(&v) + .map_err(|_| Error::invalid_value(Unexpected::Str(v), &"value string"))?; + + OptionalNonZeroPubkey::try_from(Some(pkey)) + .map_err(|_| Error::custom("Failed to convert from pubkey")) + } + + fn visit_unit(self) -> Result + where + E: Error, + { + OptionalNonZeroPubkey::try_from(None).map_err(|e| Error::custom(e.to_string())) + } +} + +#[cfg(feature = "serde-traits")] +impl<'de> Deserialize<'de> for OptionalNonZeroPubkey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(OptionalNonZeroPubkeyVisitor) + } +} + +/// An ElGamalPubkey that encodes `None` as all `0`, meant to be usable as a Pod type. +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct OptionalNonZeroElGamalPubkey(ElGamalPubkey); +impl OptionalNonZeroElGamalPubkey { + /// Checks equality between an OptionalNonZeroElGamalPubkey and an ElGamalPubkey when + /// interpreted as bytes. + pub fn equals(&self, other: &ElGamalPubkey) -> bool { + &self.0 == other + } +} +impl TryFrom> for OptionalNonZeroElGamalPubkey { + type Error = ProgramError; + fn try_from(p: Option) -> Result { + match p { + None => Ok(Self(ElGamalPubkey::default())), + Some(elgamal_pubkey) => { + if elgamal_pubkey == ElGamalPubkey::default() { + Err(ProgramError::InvalidArgument) + } else { + Ok(Self(elgamal_pubkey)) + } + } + } + } +} +impl From for Option { + fn from(p: OptionalNonZeroElGamalPubkey) -> Self { + if p.0 == ElGamalPubkey::default() { + None + } else { + Some(p.0) + } + } +} + +#[cfg(any(feature = "serde-traits", test))] +const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN: usize = 32; + +#[cfg(feature = "serde-traits")] +impl Serialize for OptionalNonZeroElGamalPubkey { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + if self.0 == ElGamalPubkey::default() { + s.serialize_none() + } else { + s.serialize_some(&self.0.to_string()) + } + } +} + +#[cfg(feature = "serde-traits")] +struct OptionalNonZeroElGamalPubkeyVisitor; + +#[cfg(feature = "serde-traits")] +impl<'de> Visitor<'de> for OptionalNonZeroElGamalPubkeyVisitor { + type Value = OptionalNonZeroElGamalPubkey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an ElGamal public key as base64 or `null`") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let bytes = BASE64_STANDARD.decode(v).map_err(Error::custom)?; + + if bytes.len() != OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN { + return Err(Error::custom(format!( + "Length of base64 decoded bytes is not {}", + OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN + ))); + } + + let mut array = [0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]; + array.copy_from_slice(&bytes[0..OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]); + let elgamal_pubkey = ElGamalPubkey(array); + OptionalNonZeroElGamalPubkey::try_from(Some(elgamal_pubkey)).map_err(Error::custom) + } + + fn visit_unit(self) -> Result + where + E: Error, + { + Ok(OptionalNonZeroElGamalPubkey::default()) + } +} + +#[cfg(feature = "serde-traits")] +impl<'de> Deserialize<'de> for OptionalNonZeroElGamalPubkey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(OptionalNonZeroElGamalPubkeyVisitor) + } +} + +#[cfg(test)] +mod tests { + use {super::*, crate::bytemuck::pod_from_bytes, solana_program::pubkey::PUBKEY_BYTES}; + + #[test] + fn test_pod_non_zero_option() { + assert_eq!( + Some(Pubkey::new_from_array([1; PUBKEY_BYTES])), + Option::::from( + *pod_from_bytes::(&[1; PUBKEY_BYTES]).unwrap() + ) + ); + assert_eq!( + None, + Option::::from( + *pod_from_bytes::(&[0; PUBKEY_BYTES]).unwrap() + ) + ); + assert_eq!( + pod_from_bytes::(&[]).unwrap_err(), + ProgramError::InvalidArgument + ); + assert_eq!( + pod_from_bytes::(&[0; 1]).unwrap_err(), + ProgramError::InvalidArgument + ); + assert_eq!( + pod_from_bytes::(&[1; 1]).unwrap_err(), + ProgramError::InvalidArgument + ); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_non_zero_option_serde_some() { + let optional_non_zero_pubkey_some = + OptionalNonZeroPubkey(Pubkey::new_from_array([1; PUBKEY_BYTES])); + let serialized_some = serde_json::to_string(&optional_non_zero_pubkey_some).unwrap(); + assert_eq!( + &serialized_some, + "\"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi\"" + ); + + let deserialized_some = + serde_json::from_str::(&serialized_some).unwrap(); + assert_eq!(optional_non_zero_pubkey_some, deserialized_some); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_non_zero_option_serde_none() { + let optional_non_zero_pubkey_none = + OptionalNonZeroPubkey(Pubkey::new_from_array([0; PUBKEY_BYTES])); + let serialized_none = serde_json::to_string(&optional_non_zero_pubkey_none).unwrap(); + assert_eq!(&serialized_none, "null"); + + let deserialized_none = + serde_json::from_str::(&serialized_none).unwrap(); + assert_eq!(optional_non_zero_pubkey_none, deserialized_none); + } + + #[test] + fn test_pod_non_zero_elgamal_option() { + assert_eq!( + Some(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])), + Option::::from(OptionalNonZeroElGamalPubkey(ElGamalPubkey( + [1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] + ))) + ); + assert_eq!( + None, + Option::::from(OptionalNonZeroElGamalPubkey(ElGamalPubkey( + [0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] + ))) + ); + + assert_eq!( + OptionalNonZeroElGamalPubkey(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])), + *pod_from_bytes::( + &[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] + ) + .unwrap() + ); + assert!(pod_from_bytes::(&[]).is_err()); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_non_zero_elgamal_option_serde_some() { + let optional_non_zero_elgamal_pubkey_some = + OptionalNonZeroElGamalPubkey(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])); + let serialized_some = + serde_json::to_string(&optional_non_zero_elgamal_pubkey_some).unwrap(); + assert_eq!( + &serialized_some, + "\"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=\"" + ); + + let deserialized_some = + serde_json::from_str::(&serialized_some).unwrap(); + assert_eq!(optional_non_zero_elgamal_pubkey_some, deserialized_some); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_non_zero_elgamal_option_serde_none() { + let optional_non_zero_elgamal_pubkey_none = + OptionalNonZeroElGamalPubkey(ElGamalPubkey([0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])); + let serialized_none = + serde_json::to_string(&optional_non_zero_elgamal_pubkey_none).unwrap(); + assert_eq!(&serialized_none, "null"); + + let deserialized_none = + serde_json::from_str::(&serialized_none).unwrap(); + assert_eq!(optional_non_zero_elgamal_pubkey_none, deserialized_none); + } +} diff --git a/libraries/pod/src/primitives.rs b/libraries/pod/src/primitives.rs new file mode 100644 index 00000000000..636d48afdfa --- /dev/null +++ b/libraries/pod/src/primitives.rs @@ -0,0 +1,224 @@ +//! primitive types that can be used in `Pod`s +use bytemuck::{Pod, Zeroable}; + +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "borsh")] +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; + +/// The standard `bool` is not a `Pod`, define a replacement that is +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "bool", into = "bool"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodBool(u8); + +impl From for PodBool { + fn from(b: bool) -> Self { + Self(if b { 1 } else { 0 }) + } +} + +impl From<&bool> for PodBool { + fn from(b: &bool) -> Self { + Self(if *b { 1 } else { 0 }) + } +} + +impl From<&PodBool> for bool { + fn from(b: &PodBool) -> Self { + b.0 != 0 + } +} + +impl From for bool { + fn from(b: PodBool) -> Self { + b.0 != 0 + } +} + +/// Simple macro for implementing conversion functions between Pod* ints and standard ints. +/// +/// The standard int types can cause alignment issues when placed in a `Pod`, +/// so these replacements are usable in all `Pod`s. +#[macro_export] +macro_rules! impl_int_conversion { + ($P:ty, $I:ty) => { + impl From<$I> for $P { + fn from(n: $I) -> Self { + Self(n.to_le_bytes()) + } + } + impl From<$P> for $I { + fn from(pod: $P) -> Self { + Self::from_le_bytes(pod.0) + } + } + }; +} + +/// `u16` type that can be used in `Pod`s +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "u16", into = "u16"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodU16([u8; 2]); +impl_int_conversion!(PodU16, u16); + +/// `i16` type that can be used in Pods +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "i16", into = "i16"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodI16([u8; 2]); +impl_int_conversion!(PodI16, i16); + +/// `u32` type that can be used in `Pod`s +#[cfg_attr( + feature = "borsh", + derive(BorshDeserialize, BorshSerialize, BorshSchema) +)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "u32", into = "u32"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodU32([u8; 4]); +impl_int_conversion!(PodU32, u32); + +/// `u64` type that can be used in Pods +#[cfg_attr( + feature = "borsh", + derive(BorshDeserialize, BorshSerialize, BorshSchema) +)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "u64", into = "u64"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodU64([u8; 8]); +impl_int_conversion!(PodU64, u64); + +/// `i64` type that can be used in Pods +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(from = "i64", into = "i64"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodI64([u8; 8]); +impl_int_conversion!(PodI64, i64); + +#[cfg(test)] +mod tests { + use {super::*, crate::bytemuck::pod_from_bytes}; + + #[test] + fn test_pod_bool() { + assert!(pod_from_bytes::(&[]).is_err()); + assert!(pod_from_bytes::(&[0, 0]).is_err()); + + for i in 0..=u8::MAX { + assert_eq!(i != 0, bool::from(pod_from_bytes::(&[i]).unwrap())); + } + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_bool_serde() { + let pod_false: PodBool = false.into(); + let pod_true: PodBool = true.into(); + + let serialized_false = serde_json::to_string(&pod_false).unwrap(); + let serialized_true = serde_json::to_string(&pod_true).unwrap(); + assert_eq!(&serialized_false, "false"); + assert_eq!(&serialized_true, "true"); + + let deserialized_false = serde_json::from_str::(&serialized_false).unwrap(); + let deserialized_true = serde_json::from_str::(&serialized_true).unwrap(); + assert_eq!(pod_false, deserialized_false); + assert_eq!(pod_true, deserialized_true); + } + + #[test] + fn test_pod_u16() { + assert!(pod_from_bytes::(&[]).is_err()); + assert_eq!(1u16, u16::from(*pod_from_bytes::(&[1, 0]).unwrap())); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_u16_serde() { + let pod_u16: PodU16 = u16::MAX.into(); + + let serialized = serde_json::to_string(&pod_u16).unwrap(); + assert_eq!(&serialized, "65535"); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(pod_u16, deserialized); + } + + #[test] + fn test_pod_i16() { + assert!(pod_from_bytes::(&[]).is_err()); + assert_eq!( + -1i16, + i16::from(*pod_from_bytes::(&[255, 255]).unwrap()) + ); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_i16_serde() { + let pod_i16: PodI16 = i16::MAX.into(); + + println!("pod_i16 {:?}", pod_i16); + + let serialized = serde_json::to_string(&pod_i16).unwrap(); + assert_eq!(&serialized, "32767"); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(pod_i16, deserialized); + } + + #[test] + fn test_pod_u64() { + assert!(pod_from_bytes::(&[]).is_err()); + assert_eq!( + 1u64, + u64::from(*pod_from_bytes::(&[1, 0, 0, 0, 0, 0, 0, 0]).unwrap()) + ); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_u64_serde() { + let pod_u64: PodU64 = u64::MAX.into(); + + let serialized = serde_json::to_string(&pod_u64).unwrap(); + assert_eq!(&serialized, "18446744073709551615"); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(pod_u64, deserialized); + } + + #[test] + fn test_pod_i64() { + assert!(pod_from_bytes::(&[]).is_err()); + assert_eq!( + -1i64, + i64::from( + *pod_from_bytes::(&[255, 255, 255, 255, 255, 255, 255, 255]).unwrap() + ) + ); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn test_pod_i64_serde() { + let pod_i64: PodI64 = i64::MAX.into(); + + let serialized = serde_json::to_string(&pod_i64).unwrap(); + assert_eq!(&serialized, "9223372036854775807"); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(pod_i64, deserialized); + } +} diff --git a/libraries/pod/src/slice.rs b/libraries/pod/src/slice.rs new file mode 100644 index 00000000000..ac912dc3005 --- /dev/null +++ b/libraries/pod/src/slice.rs @@ -0,0 +1,216 @@ +//! Special types for working with slices of `Pod`s + +use { + crate::{ + bytemuck::{ + pod_from_bytes, pod_from_bytes_mut, pod_slice_from_bytes, pod_slice_from_bytes_mut, + }, + error::PodSliceError, + primitives::PodU32, + }, + bytemuck::Pod, + solana_program::program_error::ProgramError, +}; + +const LENGTH_SIZE: usize = std::mem::size_of::(); +/// Special type for using a slice of `Pod`s in a zero-copy way +pub struct PodSlice<'data, T: Pod> { + length: &'data PodU32, + data: &'data [T], +} +impl<'data, T: Pod> PodSlice<'data, T> { + /// Unpack the buffer into a slice + pub fn unpack<'a>(data: &'a [u8]) -> Result + where + 'a: 'data, + { + if data.len() < LENGTH_SIZE { + return Err(PodSliceError::BufferTooSmall.into()); + } + let (length, data) = data.split_at(LENGTH_SIZE); + let length = pod_from_bytes::(length)?; + let _max_length = max_len_for_type::(data.len())?; + let data = pod_slice_from_bytes(data)?; + Ok(Self { length, data }) + } + + /// Get the slice data + pub fn data(&self) -> &[T] { + let length = u32::from(*self.length) as usize; + &self.data[..length] + } + + /// Get the amount of bytes used by `num_items` + pub fn size_of(num_items: usize) -> Result { + std::mem::size_of::() + .checked_mul(num_items) + .and_then(|len| len.checked_add(LENGTH_SIZE)) + .ok_or_else(|| PodSliceError::CalculationFailure.into()) + } +} + +/// Special type for using a slice of mutable `Pod`s in a zero-copy way +pub struct PodSliceMut<'data, T: Pod> { + length: &'data mut PodU32, + data: &'data mut [T], + max_length: usize, +} +impl<'data, T: Pod> PodSliceMut<'data, T> { + /// Unpack the mutable buffer into a mutable slice, with the option to + /// initialize the data + fn unpack_internal<'a>(data: &'a mut [u8], init: bool) -> Result + where + 'a: 'data, + { + if data.len() < LENGTH_SIZE { + return Err(PodSliceError::BufferTooSmall.into()); + } + let (length, data) = data.split_at_mut(LENGTH_SIZE); + let length = pod_from_bytes_mut::(length)?; + if init { + *length = 0.into(); + } + let max_length = max_len_for_type::(data.len())?; + let data = pod_slice_from_bytes_mut(data)?; + Ok(Self { + length, + data, + max_length, + }) + } + + /// Unpack the mutable buffer into a mutable slice + pub fn unpack<'a>(data: &'a mut [u8]) -> Result + where + 'a: 'data, + { + Self::unpack_internal(data, /* init */ false) + } + + /// Unpack the mutable buffer into a mutable slice, and initialize the + /// slice to 0-length + pub fn init<'a>(data: &'a mut [u8]) -> Result + where + 'a: 'data, + { + Self::unpack_internal(data, /* init */ true) + } + + /// Add another item to the slice + pub fn push(&mut self, t: T) -> Result<(), ProgramError> { + let length = u32::from(*self.length); + if length as usize == self.max_length { + Err(PodSliceError::BufferTooSmall.into()) + } else { + self.data[length as usize] = t; + *self.length = length.saturating_add(1).into(); + Ok(()) + } + } +} + +fn max_len_for_type(data_len: usize) -> Result { + let size: usize = std::mem::size_of::(); + let max_len = data_len + .checked_div(size) + .ok_or(PodSliceError::CalculationFailure)?; + // check that it isn't over or under allocated + if max_len.saturating_mul(size) != data_len { + if max_len == 0 { + // Size of T is greater than buffer size + Err(PodSliceError::BufferTooSmall.into()) + } else { + Err(PodSliceError::BufferTooLarge.into()) + } + } else { + Ok(max_len) + } +} + +#[cfg(test)] +mod tests { + use {super::*, crate::bytemuck::pod_slice_to_bytes, bytemuck::Zeroable}; + + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] + struct TestStruct { + test_field: u8, + test_pubkey: [u8; 32], + } + + #[test] + fn test_pod_slice() { + let test_field_bytes = [0]; + let test_pubkey_bytes = [1; 32]; + let len_bytes = [2, 0, 0, 0]; + + // Slice will contain 2 `TestStruct` + let mut data_bytes = [0; 66]; + data_bytes[0..1].copy_from_slice(&test_field_bytes); + data_bytes[1..33].copy_from_slice(&test_pubkey_bytes); + data_bytes[33..34].copy_from_slice(&test_field_bytes); + data_bytes[34..66].copy_from_slice(&test_pubkey_bytes); + + let mut pod_slice_bytes = [0; 70]; + pod_slice_bytes[0..4].copy_from_slice(&len_bytes); + pod_slice_bytes[4..70].copy_from_slice(&data_bytes); + + let pod_slice = PodSlice::::unpack(&pod_slice_bytes).unwrap(); + let pod_slice_data = pod_slice.data(); + + assert_eq!(*pod_slice.length, PodU32::from(2)); + assert_eq!(pod_slice_to_bytes(pod_slice.data()), data_bytes); + assert_eq!(pod_slice_data[0].test_field, test_field_bytes[0]); + assert_eq!(pod_slice_data[0].test_pubkey, test_pubkey_bytes); + assert_eq!(PodSlice::::size_of(1).unwrap(), 37); + } + + #[test] + fn test_pod_slice_buffer_too_large() { + // 1 `TestStruct` + length = 37 bytes + // we pass 38 to trigger BufferTooLarge + let pod_slice_bytes = [1; 38]; + let err = PodSlice::::unpack(&pod_slice_bytes) + .err() + .unwrap(); + assert_eq!( + err, + PodSliceError::BufferTooLarge.into(), + "Expected an `PodSliceError::BufferTooLarge` error" + ); + } + + #[test] + fn test_pod_slice_buffer_too_small() { + // 1 `TestStruct` + length = 37 bytes + // we pass 36 to trigger BufferTooSmall + let pod_slice_bytes = [1; 36]; + let err = PodSlice::::unpack(&pod_slice_bytes) + .err() + .unwrap(); + assert_eq!( + err, + PodSliceError::BufferTooSmall.into(), + "Expected an `PodSliceError::BufferTooSmall` error" + ); + } + + #[test] + fn test_pod_slice_mut() { + // slice can fit 2 `TestStruct` + let mut pod_slice_bytes = [0; 70]; + // set length to 1, so we have room to push 1 more item + let len_bytes = [1, 0, 0, 0]; + pod_slice_bytes[0..4].copy_from_slice(&len_bytes); + + let mut pod_slice = PodSliceMut::::unpack(&mut pod_slice_bytes).unwrap(); + + assert_eq!(*pod_slice.length, PodU32::from(1)); + pod_slice.push(TestStruct::default()).unwrap(); + assert_eq!(*pod_slice.length, PodU32::from(2)); + let err = pod_slice + .push(TestStruct::default()) + .expect_err("Expected an `PodSliceError::BufferTooSmall` error"); + assert_eq!(err, PodSliceError::BufferTooSmall.into()); + } +} diff --git a/libraries/program-error/Cargo.toml b/libraries/program-error/Cargo.toml index d6d1b87f8c0..6bcd1d968c3 100644 --- a/libraries/program-error/Cargo.toml +++ b/libraries/program-error/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-program-error" -version = "0.2.0" +version = "0.3.0" description = "Library for Solana Program error attributes and derive macro for creating them" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -10,17 +10,17 @@ edition = "2021" [dependencies] num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" -spl-program-error-derive = { version = "0.2.0", path = "./derive" } +solana-program = "1.17.2" +spl-program-error-derive = { version = "0.3.1", path = "./derive" } thiserror = "1.0" [dev-dependencies] lazy_static = "1.4" serial_test = "2.0" -solana-sdk = "1.16.1" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] \ No newline at end of file +targets = ["x86_64-unknown-linux-gnu"] diff --git a/libraries/program-error/derive/Cargo.toml b/libraries/program-error/derive/Cargo.toml index 9eba3c1b637..aa39a06735a 100644 --- a/libraries/program-error/derive/Cargo.toml +++ b/libraries/program-error/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-program-error-derive" -version = "0.2.0" +version = "0.3.1" description = "Proc-Macro Library for Solana Program error attributes and derive macro" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -13,4 +13,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" +sha2 = "0.10" syn = { version = "2.0", features = ["full"] } diff --git a/libraries/program-error/derive/src/lib.rs b/libraries/program-error/derive/src/lib.rs index af9e53dadad..2681e85be4e 100644 --- a/libraries/program-error/derive/src/lib.rs +++ b/libraries/program-error/derive/src/lib.rs @@ -7,46 +7,79 @@ // `quote` crate // // Culprit is `macro_impl.rs:66` -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] extern crate proc_macro; mod macro_impl; +mod parser; -use macro_impl::MacroType; -use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemEnum}; +use { + crate::parser::SplProgramErrorArgs, + macro_impl::MacroType, + proc_macro::TokenStream, + syn::{parse_macro_input, ItemEnum}, +}; -/// Derive macro to add `Into` traits +/// Derive macro to add `Into` +/// trait #[proc_macro_derive(IntoProgramError)] pub fn into_program_error(input: TokenStream) -> TokenStream { - MacroType::IntoProgramError - .generate_tokens(parse_macro_input!(input as ItemEnum)) + let ItemEnum { ident, .. } = parse_macro_input!(input as ItemEnum); + MacroType::IntoProgramError { ident } + .generate_tokens() .into() } /// Derive macro to add `solana_program::decode_error::DecodeError` trait #[proc_macro_derive(DecodeError)] pub fn decode_error(input: TokenStream) -> TokenStream { - MacroType::DecodeError - .generate_tokens(parse_macro_input!(input as ItemEnum)) - .into() + let ItemEnum { ident, .. } = parse_macro_input!(input as ItemEnum); + MacroType::DecodeError { ident }.generate_tokens().into() } /// Derive macro to add `solana_program::program_error::PrintProgramError` trait #[proc_macro_derive(PrintProgramError)] pub fn print_program_error(input: TokenStream) -> TokenStream { - MacroType::PrintProgramError - .generate_tokens(parse_macro_input!(input as ItemEnum)) + let ItemEnum { + ident, variants, .. + } = parse_macro_input!(input as ItemEnum); + MacroType::PrintProgramError { ident, variants } + .generate_tokens() .into() } /// Proc macro attribute to turn your enum into a Solana Program Error +/// +/// Adds: +/// - `Clone` +/// - `Debug` +/// - `Eq` +/// - `PartialEq` +/// - `thiserror::Error` +/// - `num_derive::FromPrimitive` +/// - `Into` +/// - `solana_program::decode_error::DecodeError` +/// - `solana_program::program_error::PrintProgramError` +/// +/// Optionally, you can add `hash_error_code_start: u32` argument to create +/// a unique `u32` _starting_ error codes from the names of the enum variants. +/// Notes: +/// - The _error_ variant will start at this value, and the rest will be +/// incremented by one +/// - The value provided is only for code readability, the actual error code +/// will be a hash of the input string and is checked against your input +/// +/// Syntax: `#[spl_program_error(hash_error_code_start = 1275525928)]` +/// Hash Input: `spl_program_error::` +/// Value: `u32::from_le_bytes([13..17])` #[proc_macro_attribute] -pub fn spl_program_error(_: TokenStream, input: TokenStream) -> TokenStream { - MacroType::SplProgramError - .generate_tokens(parse_macro_input!(input as ItemEnum)) +pub fn spl_program_error(attr: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(attr as SplProgramErrorArgs); + let item_enum = parse_macro_input!(input as ItemEnum); + MacroType::SplProgramError { args, item_enum } + .generate_tokens() .into() } diff --git a/libraries/program-error/derive/src/macro_impl.rs b/libraries/program-error/derive/src/macro_impl.rs index f017728432e..f323e2ba786 100644 --- a/libraries/program-error/derive/src/macro_impl.rs +++ b/libraries/program-error/derive/src/macro_impl.rs @@ -1,32 +1,53 @@ //! The actual token generator for the macro -use quote::quote; -use syn::{punctuated::Punctuated, token::Comma, Ident, ItemEnum, LitStr, Variant}; + +use { + crate::parser::SplProgramErrorArgs, + proc_macro2::Span, + quote::quote, + sha2::{Digest, Sha256}, + syn::{ + punctuated::Punctuated, token::Comma, Expr, ExprLit, Ident, ItemEnum, Lit, LitInt, LitStr, + Token, Variant, + }, +}; + +const SPL_ERROR_HASH_NAMESPACE: &str = "spl_program_error"; +const SPL_ERROR_HASH_MIN_VALUE: u32 = 7_000; /// The type of macro being called, thus directing which tokens to generate #[allow(clippy::enum_variant_names)] pub enum MacroType { - IntoProgramError, - DecodeError, - PrintProgramError, - SplProgramError, + IntoProgramError { + ident: Ident, + }, + DecodeError { + ident: Ident, + }, + PrintProgramError { + ident: Ident, + variants: Punctuated, + }, + SplProgramError { + args: SplProgramErrorArgs, + item_enum: ItemEnum, + }, } impl MacroType { /// Generates the corresponding tokens based on variant selection - pub fn generate_tokens(&self, item_enum: ItemEnum) -> proc_macro2::TokenStream { + pub fn generate_tokens(&mut self) -> proc_macro2::TokenStream { match self { - MacroType::IntoProgramError => into_program_error(&item_enum.ident), - MacroType::DecodeError => decode_error(&item_enum.ident), - MacroType::PrintProgramError => { - print_program_error(&item_enum.ident, &item_enum.variants) - } - MacroType::SplProgramError => spl_program_error(item_enum), + Self::IntoProgramError { ident } => into_program_error(ident), + Self::DecodeError { ident } => decode_error(ident), + Self::PrintProgramError { ident, variants } => print_program_error(ident, variants), + Self::SplProgramError { args, item_enum } => spl_program_error(args, item_enum), } } } -/// Builds the implementation of `Into` -/// More specifically, implements `From for solana_program::program_error::ProgramError` +/// Builds the implementation of +/// `Into` More specifically, +/// implements `From for solana_program::program_error::ProgramError` pub fn into_program_error(ident: &Ident) -> proc_macro2::TokenStream { quote! { impl From<#ident> for solana_program::program_error::ProgramError { @@ -48,7 +69,8 @@ pub fn decode_error(ident: &Ident) -> proc_macro2::TokenStream { } } -/// Builds the implementation of `solana_program::program_error::PrintProgramError` +/// Builds the implementation of +/// `solana_program::program_error::PrintProgramError` pub fn print_program_error( ident: &Ident, variants: &Punctuated, @@ -96,16 +118,25 @@ fn get_error_message(variant: &Variant) -> Option { /// The main function that produces the tokens required to turn your /// error enum into a Solana Program Error -pub fn spl_program_error(input: ItemEnum) -> proc_macro2::TokenStream { - let ident = &input.ident; - let variants = &input.variants; +pub fn spl_program_error( + args: &SplProgramErrorArgs, + item_enum: &mut ItemEnum, +) -> proc_macro2::TokenStream { + if let Some(error_code_start) = args.hash_error_code_start { + set_first_discriminant(item_enum, error_code_start); + } + + let ident = &item_enum.ident; + let variants = &item_enum.variants; let into_program_error = into_program_error(ident); let decode_error = decode_error(ident); let print_program_error = print_program_error(ident, variants); + quote! { + #[repr(u32)] #[derive(Clone, Debug, Eq, thiserror::Error, num_derive::FromPrimitive, PartialEq)] #[num_traits = "num_traits"] - #input + #item_enum #into_program_error @@ -114,3 +145,56 @@ pub fn spl_program_error(input: ItemEnum) -> proc_macro2::TokenStream { #print_program_error } } + +/// This function adds a discriminant to the first enum variant based on the +/// hash of the `SPL_ERROR_HASH_NAMESPACE` constant, the enum name and variant +/// name. +/// It will then check to make sure the provided `hash_error_code_start` is +/// equal to the hash-produced `u32`. +/// +/// See https://docs.rs/syn/latest/syn/struct.Variant.html +fn set_first_discriminant(item_enum: &mut ItemEnum, error_code_start: u32) { + let enum_ident = &item_enum.ident; + if item_enum.variants.is_empty() { + panic!("Enum must have at least one variant"); + } + let first_variant = &mut item_enum.variants[0]; + let discriminant = u32_from_hash(enum_ident); + if discriminant == error_code_start { + let eq = Token![=](Span::call_site()); + let expr = Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: Lit::Int(LitInt::new(&discriminant.to_string(), Span::call_site())), + }); + first_variant.discriminant = Some((eq, expr)); + } else { + panic!( + "Error code start value from hash must be {0}. Update your macro attribute to \ + `#[spl_program_error(hash_error_code_start = {0})]`.", + discriminant + ); + } +} + +/// Hashes the `SPL_ERROR_HASH_NAMESPACE` constant, the enum name and variant +/// name and returns four middle bytes (13 through 16) as a u32. +fn u32_from_hash(enum_ident: &Ident) -> u32 { + let hash_input = format!("{}:{}", SPL_ERROR_HASH_NAMESPACE, enum_ident); + + // We don't want our error code to start at any number below + // `SPL_ERROR_HASH_MIN_VALUE`! + let mut nonce: u32 = 0; + loop { + let mut hasher = Sha256::new_with_prefix(hash_input.as_bytes()); + hasher.update(nonce.to_le_bytes()); + let d = u32::from_le_bytes( + hasher.finalize()[13..17] + .try_into() + .expect("Unable to convert hash to u32"), + ); + if d >= SPL_ERROR_HASH_MIN_VALUE { + return d; + } + nonce += 1; + } +} diff --git a/libraries/program-error/derive/src/parser.rs b/libraries/program-error/derive/src/parser.rs new file mode 100644 index 00000000000..a09ffc7ba7c --- /dev/null +++ b/libraries/program-error/derive/src/parser.rs @@ -0,0 +1,64 @@ +//! Token parsing + +use { + proc_macro2::Ident, + syn::{ + parse::{Parse, ParseStream}, + token::Comma, + LitInt, Token, + }, +}; + +/// Possible arguments to the `#[spl_program_error]` attribute +pub struct SplProgramErrorArgs { + /// Whether to hash the error codes using `solana_program::hash` + /// or to use the default error code assigned by `num_traits`. + pub hash_error_code_start: Option, +} + +impl Parse for SplProgramErrorArgs { + fn parse(input: ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self { + hash_error_code_start: None, + }); + } + match SplProgramErrorArgParser::parse(input)? { + SplProgramErrorArgParser::HashErrorCodes { value, .. } => Ok(Self { + hash_error_code_start: Some(value.base10_parse::()?), + }), + } + } +} + +/// Parser for args to the `#[spl_program_error]` attribute +/// ie. `#[spl_program_error(hash_error_code_start = 1275525928)]` +enum SplProgramErrorArgParser { + HashErrorCodes { + _ident: Ident, + _equals_sign: Token![=], + value: LitInt, + _comma: Option, + }, +} + +impl Parse for SplProgramErrorArgParser { + fn parse(input: ParseStream) -> syn::Result { + let _ident = { + let ident = input.parse::()?; + if ident != "hash_error_code_start" { + return Err(input.error("Expected argument 'hash_error_code_start'")); + } + ident + }; + let _equals_sign = input.parse::()?; + let value = input.parse::()?; + let _comma: Option = input.parse().unwrap_or(None); + Ok(Self::HashErrorCodes { + _ident, + _equals_sign, + value, + _comma, + }) + } +} diff --git a/libraries/program-error/src/lib.rs b/libraries/program-error/src/lib.rs index 8c1fb7c0b7f..739995dd197 100644 --- a/libraries/program-error/src/lib.rs +++ b/libraries/program-error/src/lib.rs @@ -8,10 +8,10 @@ extern crate self as spl_program_error; // Make these available downstream for the macro to work without // additional imports -pub use num_derive; -pub use num_traits; -pub use solana_program; -pub use spl_program_error_derive::{ - spl_program_error, DecodeError, IntoProgramError, PrintProgramError, +pub use { + num_derive, num_traits, solana_program, + spl_program_error_derive::{ + spl_program_error, DecodeError, IntoProgramError, PrintProgramError, + }, + thiserror, }; -pub use thiserror; diff --git a/libraries/program-error/tests/decode.rs b/libraries/program-error/tests/decode.rs index 28aeac56b61..0c7c209bc14 100644 --- a/libraries/program-error/tests/decode.rs +++ b/libraries/program-error/tests/decode.rs @@ -1,5 +1,5 @@ //! Tests `#[derive(DecodeError)]` -//! + use spl_program_error::*; /// Example error diff --git a/libraries/program-error/tests/into.rs b/libraries/program-error/tests/into.rs index 97e8868a9be..0f32b8f40d3 100644 --- a/libraries/program-error/tests/into.rs +++ b/libraries/program-error/tests/into.rs @@ -1,5 +1,5 @@ //! Tests `#[derive(IntoProgramError)]` -//! + use spl_program_error::*; /// Example error diff --git a/libraries/program-error/tests/mod.rs b/libraries/program-error/tests/mod.rs index ae50368fd35..413587758e9 100644 --- a/libraries/program-error/tests/mod.rs +++ b/libraries/program-error/tests/mod.rs @@ -6,13 +6,15 @@ pub mod spl; #[cfg(test)] mod tests { - use super::*; - use serial_test::serial; - use solana_program::{ - decode_error::DecodeError, - program_error::{PrintProgramError, ProgramError}, + use { + super::*, + serial_test::serial, + solana_program::{ + decode_error::DecodeError, + program_error::{PrintProgramError, ProgramError}, + }, + std::sync::{Arc, RwLock}, }; - use std::sync::{Arc, RwLock}; // Used to capture output for `PrintProgramError` for testing lazy_static::lazy_static! { diff --git a/libraries/program-error/tests/print.rs b/libraries/program-error/tests/print.rs index ddb62cf60b6..8b68f66a581 100644 --- a/libraries/program-error/tests/print.rs +++ b/libraries/program-error/tests/print.rs @@ -1,5 +1,5 @@ //! Tests `#[derive(PrintProgramError)]` -//! + use spl_program_error::*; /// Example error diff --git a/libraries/program-error/tests/spl.rs b/libraries/program-error/tests/spl.rs index 8cd557b0f36..bbdb9af0aca 100644 --- a/libraries/program-error/tests/spl.rs +++ b/libraries/program-error/tests/spl.rs @@ -1,5 +1,5 @@ //! Tests `#[spl_program_error]` -//! + use spl_program_error::*; /// Example error @@ -18,3 +18,57 @@ pub enum ExampleError { fn test_macros_compile() { let _ = ExampleError::MintHasNoMintAuthority; } + +/// Example library error with namespace +#[spl_program_error(hash_error_code_start = 2_056_342_880)] +enum ExampleLibraryError { + /// This is a very informative error + #[error("This is a very informative error")] + VeryInformativeError, + /// This is a super important error + #[error("This is a super important error")] + SuperImportantError, + /// This is a mega serious error + #[error("This is a mega serious error")] + MegaSeriousError, + /// You are toast + #[error("You are toast")] + YouAreToast, +} + +/// Tests hashing of error codes into unique `u32` values +#[test] +fn test_library_error_codes() { + fn get_error_code_check(hash_input: &str) -> u32 { + let mut nonce: u32 = 0; + loop { + let hash = solana_program::hash::hashv(&[hash_input.as_bytes(), &nonce.to_le_bytes()]); + let mut bytes = [0u8; 4]; + bytes.copy_from_slice(&hash.to_bytes()[13..17]); + let error_code = u32::from_le_bytes(bytes); + if error_code >= 10_000 { + return error_code; + } + nonce += 1; + } + } + + let first_error_as_u32 = ExampleLibraryError::VeryInformativeError as u32; + + assert_eq!( + ExampleLibraryError::VeryInformativeError as u32, + get_error_code_check("spl_program_error:ExampleLibraryError"), + ); + assert_eq!( + ExampleLibraryError::SuperImportantError as u32, + first_error_as_u32 + 1, + ); + assert_eq!( + ExampleLibraryError::MegaSeriousError as u32, + first_error_as_u32 + 2, + ); + assert_eq!( + ExampleLibraryError::YouAreToast as u32, + first_error_as_u32 + 3, + ); +} diff --git a/libraries/tlv-account-resolution/Cargo.toml b/libraries/tlv-account-resolution/Cargo.toml index bbd83643ba1..2e2b0dc8570 100644 --- a/libraries/tlv-account-resolution/Cargo.toml +++ b/libraries/tlv-account-resolution/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-tlv-account-resolution" -version = "0.2.0" +version = "0.4.0" description = "Solana Program Library TLV Account Resolution Interface" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -11,15 +11,19 @@ edition = "2021" test-sbf = [] [dependencies] -bytemuck = { version = "1.13.1", features = ["derive"] } -solana-program = "1.16.1" +bytemuck = { version = "1.14.0", features = ["derive"] } +solana-program = "1.17.2" spl-discriminator = { version = "0.1", path = "../discriminator" } -spl-program-error = { version = "0.2.0", path = "../program-error" } -spl-type-length-value = { version = "0.2", path = "../type-length-value" } +spl-program-error = { version = "0.3", path = "../program-error" } +spl-type-length-value = { version = "0.3", path = "../type-length-value" } +spl-pod = { version = "0.1", path = "../pod" } [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +futures = "0.3.29" +futures-util = "0.3" +solana-client = "1.17.2" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" spl-discriminator = { version = "0.1", path = "../discriminator" } [lib] diff --git a/libraries/tlv-account-resolution/README.md b/libraries/tlv-account-resolution/README.md index d1fbc17a04a..7d7bbb27f4d 100644 --- a/libraries/tlv-account-resolution/README.md +++ b/libraries/tlv-account-resolution/README.md @@ -12,7 +12,11 @@ into a TLV entry in an account, you can do the following: use { solana_program::{account_info::AccountInfo, instruction::{AccountMeta, Instruction}, pubkey::Pubkey}, spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, - spl_tlv_account_resolution::state::ExtraAccountMetas, + spl_tlv_account_resolution::{ + account::ExtraAccountMeta, + seeds::Seed, + state::ExtraAccountMetaList + }, }; struct MyInstruction; @@ -21,35 +25,65 @@ impl SplDiscriminate for MyInstruction { const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([1; ArrayDiscriminator::LENGTH]); } -// Actually put it in the additional required account keys and signer / writable +// Prepare the additional required account keys and signer / writable let extra_metas = [ - AccountMeta::new(Pubkey::new_unique(), false), - AccountMeta::new(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), false), + AccountMeta::new(Pubkey::new_unique(), false).into(), + AccountMeta::new_readonly(Pubkey::new_unique(), true).into(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: b"some_string".to_vec(), + }, + Seed::InstructionData { + index: 1, + length: 1, // u8 + }, + Seed::AccountKey { index: 1 }, + ], + false, + true, + ).unwrap(), + ExtraAccountMeta::new_external_pda_with_seeds( + 0, + &[Seed::AccountKey { index: 2 }], + false, + false, + ).unwrap(), ]; -// Assume that this buffer is actually account data, already allocated to `account_size` -let account_size = ExtraAccountMetas::size_of(extra_metas.len()).unwrap(); +// Allocate a new buffer with the proper `account_size` +let account_size = ExtraAccountMetaList::size_of(extra_metas.len()).unwrap(); let mut buffer = vec![0; account_size]; // Initialize the structure for your instruction -ExtraAccountMetas::init_with_account_metas::(&mut buffer, &extra_metas).unwrap(); +ExtraAccountMetaList::init::(&mut buffer, &extra_metas).unwrap(); // Off-chain, you can add the additional accounts directly from the account data +// You need to provide the resolver a way to fetch account data off-chain +let client = RpcClient::new_mock("succeeds".to_string()); let program_id = Pubkey::new_unique(); -let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]); -ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer).unwrap(); +let mut instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]); +ExtraAccountMetaList::add_to_instruction::<_, _, MyInstruction>( + &mut instruction, + |address: &Pubkey| { + client + .get_account(address) + .map_ok(|acct| Some(acct.data)) + }, + &buffer, +) +.await +.unwrap(); // On-chain, you can add the additional accounts *and* account infos -let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[], vec![]); +let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]); // Include all of the well-known required account infos here first let mut cpi_account_infos = vec![]; // Provide all "remaining_account_infos" that are *not* part of any other known interface let remaining_account_infos = &[]; -ExtraAccountMetas::add_to_cpi_instruction::( +ExtraAccountMetaList::add_to_cpi_instruction::( &mut cpi_instruction, &mut cpi_account_infos, &buffer, @@ -57,7 +91,7 @@ ExtraAccountMetas::add_to_cpi_instruction::( ).unwrap(); ``` -For ease of use on-chain, `ExtraAccountMetas::init_with_account_infos` is also +For ease of use on-chain, `ExtraAccountMetaList::init` is also provided to initialize directly from a set of given accounts. ## Motivation @@ -83,7 +117,7 @@ uses static account data. For example, let's imagine there's a `Transferable` interface, along with a `transfer` instruction. Some programs that implement `transfer` may need more -accounts than just the ones defined in the interface. How does a an on-chain or +accounts than just the ones defined in the interface. How does an on-chain or off-chain client figure out the additional required accounts? The "static" approach requires programs to write the extra required accounts to @@ -99,18 +133,41 @@ instruction and give the correct account infos. This approach could also be called a "state interface". -## How it works +### Types of Required Accounts + +This library is capable of storing two types of configurations for additional +required accounts: + +- Accounts with a fixed address +- Accounts with a **dynamic program-derived address** derived from seeds that +may come from any combination of the following: + - Hard-coded values, such as string literals or integers + - A slice of the instruction data provided to the transfer-hook program + - The address of another account in the total list of accounts + - A program id from another account in the instruction + +When you store configurations for a dynamic Program-Derived Address within the +additional required accounts, the PDA itself is evaluated (or resolved) at the +time of instruction invocation using the instruction itself. This +occurs in the offchain and onchain helpers mentioned below, which leverage +the SPL TLV Account Resolution library to perform this resolution +automatically. + +## How it Works This library uses `spl-type-length-value` to read and write required instruction accounts from account data. Interface instructions must have an 8-byte discriminator, so that the exposed -`ExtraAccountMetas` type can use the instruction discriminator as a `ArrayDiscriminator`. +`ExtraAccountMetaList` type can use the instruction discriminator as an +`ArrayDiscriminator`, which allows that discriminator to serve as a unique TLV +discriminator for identifying entries that correspond to that particular +instruction. This can be confusing. Typically, a type implements `SplDiscriminate`, so that -the type can be written into TLV data. In this case, `ExtraAccountMetas` is +the type can be written into TLV data. In this case, `ExtraAccountMetaList` is generic over `SplDiscriminate`, meaning that a program can write many different instances of -`ExtraAccountMetas` into one account, using different `ArrayDiscriminator`s. +`ExtraAccountMetaList` into one account, using different `ArrayDiscriminator`s. Also, it's reusing an instruction discriminator as a TLV discriminator. For example, if the `transfer` instruction has a discriminator of `[1, 2, 3, 4, 5, 6, 7, 8]`, diff --git a/libraries/tlv-account-resolution/src/account.rs b/libraries/tlv-account-resolution/src/account.rs new file mode 100644 index 00000000000..8c9203321a9 --- /dev/null +++ b/libraries/tlv-account-resolution/src/account.rs @@ -0,0 +1,227 @@ +//! Struct for managing extra required account configs, ie. defining accounts +//! required for your interface program, which can be `AccountMeta`s - which +//! have fixed addresses - or PDAs - which have addresses derived from a +//! collection of seeds + +use { + crate::{error::AccountResolutionError, seeds::Seed}, + bytemuck::{Pod, Zeroable}, + solana_program::{ + account_info::AccountInfo, instruction::AccountMeta, program_error::ProgramError, + pubkey::Pubkey, + }, + spl_pod::primitives::PodBool, +}; + +/// Resolve a program-derived address (PDA) from the instruction data +/// and the accounts that have already been resolved +fn resolve_pda<'a, F>( + seeds: &[Seed], + instruction_data: &[u8], + program_id: &Pubkey, + get_account_key_data_fn: F, +) -> Result +where + F: Fn(usize) -> Option<(&'a Pubkey, Option<&'a [u8]>)>, +{ + let mut pda_seeds: Vec<&[u8]> = vec![]; + for config in seeds { + match config { + Seed::Uninitialized => (), + Seed::Literal { bytes } => pda_seeds.push(bytes), + Seed::InstructionData { index, length } => { + let arg_start = *index as usize; + let arg_end = arg_start + *length as usize; + if arg_end > instruction_data.len() { + return Err(AccountResolutionError::InstructionDataTooSmall.into()); + } + pda_seeds.push(&instruction_data[arg_start..arg_end]); + } + Seed::AccountKey { index } => { + let account_index = *index as usize; + let address = get_account_key_data_fn(account_index) + .ok_or::(AccountResolutionError::AccountNotFound.into())? + .0; + pda_seeds.push(address.as_ref()); + } + Seed::AccountData { + account_index, + data_index, + length, + } => { + let account_index = *account_index as usize; + let account_data = get_account_key_data_fn(account_index) + .ok_or::(AccountResolutionError::AccountNotFound.into())? + .1 + .ok_or::(AccountResolutionError::AccountDataNotFound.into())?; + let arg_start = *data_index as usize; + let arg_end = arg_start + *length as usize; + if account_data.len() < arg_end { + return Err(AccountResolutionError::AccountDataTooSmall.into()); + } + pda_seeds.push(&account_data[arg_start..arg_end]); + } + } + } + Ok(Pubkey::find_program_address(&pda_seeds, program_id).0) +} + +/// `Pod` type for defining a required account in a validation account. +/// +/// This can either be a standard `AccountMeta` or a PDA. +/// Can be used in TLV-encoded data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct ExtraAccountMeta { + /// Discriminator to tell whether this represents a standard + /// `AccountMeta` or a PDA + pub discriminator: u8, + /// This `address_config` field can either be the pubkey of the account + /// or the seeds used to derive the pubkey from provided inputs + pub address_config: [u8; 32], + /// Whether the account should sign + pub is_signer: PodBool, + /// Whether the account should be writable + pub is_writable: PodBool, +} +/// Helper used to to know when the top bit is set, to interpret the discriminator +/// as an index rather than as a type +const U8_TOP_BIT: u8 = 1 << 7; +impl ExtraAccountMeta { + /// Create a `ExtraAccountMeta` from a public key, + /// thus representing a standard `AccountMeta` + pub fn new_with_pubkey( + pubkey: &Pubkey, + is_signer: bool, + is_writable: bool, + ) -> Result { + Ok(Self { + discriminator: 0, + address_config: pubkey.to_bytes(), + is_signer: is_signer.into(), + is_writable: is_writable.into(), + }) + } + + /// Create a `ExtraAccountMeta` from a list of seed configurations, + /// thus representing a PDA + pub fn new_with_seeds( + seeds: &[Seed], + is_signer: bool, + is_writable: bool, + ) -> Result { + Ok(Self { + discriminator: 1, + address_config: Seed::pack_into_address_config(seeds)?, + is_signer: is_signer.into(), + is_writable: is_writable.into(), + }) + } + + /// Create a `ExtraAccountMeta` from a list of seed configurations, representing + /// a PDA for an external program + /// + /// This PDA belongs to a program elsewhere in the account list, rather + /// than the executing program. For a PDA on the executing program, use + /// `ExtraAccountMeta::new_with_seeds`. + pub fn new_external_pda_with_seeds( + program_index: u8, + seeds: &[Seed], + is_signer: bool, + is_writable: bool, + ) -> Result { + Ok(Self { + discriminator: program_index + .checked_add(U8_TOP_BIT) + .ok_or(AccountResolutionError::InvalidSeedConfig)?, + address_config: Seed::pack_into_address_config(seeds)?, + is_signer: is_signer.into(), + is_writable: is_writable.into(), + }) + } + + /// Resolve an `ExtraAccountMeta` into an `AccountMeta`, potentially + /// resolving a program-derived address (PDA) if necessary + pub fn resolve<'a, F>( + &self, + instruction_data: &[u8], + program_id: &Pubkey, + get_account_key_data_fn: F, + ) -> Result + where + F: Fn(usize) -> Option<(&'a Pubkey, Option<&'a [u8]>)>, + { + match self.discriminator { + 0 => AccountMeta::try_from(self), + x if x == 1 || x >= U8_TOP_BIT => { + let program_id = if x == 1 { + program_id + } else { + get_account_key_data_fn(x.saturating_sub(U8_TOP_BIT) as usize) + .ok_or::(AccountResolutionError::AccountNotFound.into())? + .0 + }; + let seeds = Seed::unpack_address_config(&self.address_config)?; + Ok(AccountMeta { + pubkey: resolve_pda( + &seeds, + instruction_data, + program_id, + get_account_key_data_fn, + )?, + is_signer: self.is_signer.into(), + is_writable: self.is_writable.into(), + }) + } + _ => Err(ProgramError::InvalidAccountData), + } + } +} + +impl From<&AccountMeta> for ExtraAccountMeta { + fn from(meta: &AccountMeta) -> Self { + Self { + discriminator: 0, + address_config: meta.pubkey.to_bytes(), + is_signer: meta.is_signer.into(), + is_writable: meta.is_writable.into(), + } + } +} +impl From for ExtraAccountMeta { + fn from(meta: AccountMeta) -> Self { + ExtraAccountMeta::from(&meta) + } +} +impl From<&AccountInfo<'_>> for ExtraAccountMeta { + fn from(account_info: &AccountInfo) -> Self { + Self { + discriminator: 0, + address_config: account_info.key.to_bytes(), + is_signer: account_info.is_signer.into(), + is_writable: account_info.is_writable.into(), + } + } +} +impl From> for ExtraAccountMeta { + fn from(account_info: AccountInfo) -> Self { + ExtraAccountMeta::from(&account_info) + } +} + +impl TryFrom<&ExtraAccountMeta> for AccountMeta { + type Error = ProgramError; + + fn try_from(pod: &ExtraAccountMeta) -> Result { + if pod.discriminator == 0 { + Ok(AccountMeta { + pubkey: Pubkey::try_from(pod.address_config) + .map_err(|_| ProgramError::from(AccountResolutionError::InvalidPubkey))?, + is_signer: pod.is_signer.into(), + is_writable: pod.is_writable.into(), + }) + } else { + Err(AccountResolutionError::AccountTypeNotAccountMeta.into()) + } + } +} diff --git a/libraries/tlv-account-resolution/src/error.rs b/libraries/tlv-account-resolution/src/error.rs index 287d2ba60e6..d86ee6ba00c 100644 --- a/libraries/tlv-account-resolution/src/error.rs +++ b/libraries/tlv-account-resolution/src/error.rs @@ -3,7 +3,7 @@ use spl_program_error::*; /// Errors that may be returned by the Account Resolution library. -#[spl_program_error] +#[spl_program_error(hash_error_code_start = 2_724_315_840)] pub enum AccountResolutionError { /// Incorrect account provided #[error("Incorrect account provided")] @@ -20,4 +20,44 @@ pub enum AccountResolutionError { /// Too many pubkeys provided #[error("Too many pubkeys provided")] TooManyPubkeys, + /// Failed to parse `Pubkey` from bytes + #[error("Failed to parse `Pubkey` from bytes")] + InvalidPubkey, + /// Attempted to deserialize an `AccountMeta` but the underlying type has + /// PDA configs rather than a fixed address + #[error( + "Attempted to deserialize an `AccountMeta` but the underlying type has PDA configs rather \ + than a fixed address" + )] + AccountTypeNotAccountMeta, + /// Provided list of seed configurations too large for a validation account + #[error("Provided list of seed configurations too large for a validation account")] + SeedConfigsTooLarge, + /// Not enough bytes available to pack seed configuration + #[error("Not enough bytes available to pack seed configuration")] + NotEnoughBytesForSeed, + /// The provided bytes are not valid for a seed configuration + #[error("The provided bytes are not valid for a seed configuration")] + InvalidBytesForSeed, + /// Tried to pack an invalid seed configuration + #[error("Tried to pack an invalid seed configuration")] + InvalidSeedConfig, + /// Instruction data too small for seed configuration + #[error("Instruction data too small for seed configuration")] + InstructionDataTooSmall, + /// Could not find account at specified index + #[error("Could not find account at specified index")] + AccountNotFound, + /// Error in checked math operation + #[error("Error in checked math operation")] + CalculationFailure, + /// Could not find account data at specified index + #[error("Could not find account data at specified index")] + AccountDataNotFound, + /// Account data too small for requested seed configuration + #[error("Account data too small for requested seed configuration")] + AccountDataTooSmall, + /// Failed to fetch account + #[error("Failed to fetch account")] + AccountFetchFailed, } diff --git a/libraries/tlv-account-resolution/src/lib.rs b/libraries/tlv-account-resolution/src/lib.rs index 87647996357..1f961ce8b6b 100644 --- a/libraries/tlv-account-resolution/src/lib.rs +++ b/libraries/tlv-account-resolution/src/lib.rs @@ -1,14 +1,17 @@ -//! Crate defining a state interface for offchain account resolution. If a program -//! writes the proper state information into one of their accounts, any offchain -//! and onchain client can fetch any additional required accounts for an instruction. +//! Crate defining a state interface for offchain account resolution. If a +//! program writes the proper state information into one of their accounts, any +//! offchain and onchain client can fetch any additional required accounts for +//! an instruction. -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] +pub mod account; pub mod error; -pub mod pod; +pub mod seeds; pub mod state; -// Export current sdk types for downstream users building with a different sdk version +// Export current sdk types for downstream users building with a different sdk +// version pub use solana_program; diff --git a/libraries/tlv-account-resolution/src/pod.rs b/libraries/tlv-account-resolution/src/pod.rs deleted file mode 100644 index d127e8a1572..00000000000 --- a/libraries/tlv-account-resolution/src/pod.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Pod types to be used with bytemuck for zero-copy serde - -use { - bytemuck::{Pod, Zeroable}, - solana_program::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey}, - spl_type_length_value::pod::PodBool, -}; - -/// The standard `AccountMeta` is not a `Pod`, define a replacement that is -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -pub struct PodAccountMeta { - /// The pubkey of the account - pub pubkey: Pubkey, - /// Whether the account should sign - pub is_signer: PodBool, - /// Whether the account should be writable - pub is_writable: PodBool, -} -impl PartialEq> for PodAccountMeta { - fn eq(&self, other: &AccountInfo) -> bool { - self.pubkey == *other.key - && self.is_signer == other.is_signer.into() - && self.is_writable == other.is_writable.into() - } -} - -impl From<&AccountInfo<'_>> for PodAccountMeta { - fn from(account_info: &AccountInfo) -> Self { - Self { - pubkey: *account_info.key, - is_signer: account_info.is_signer.into(), - is_writable: account_info.is_writable.into(), - } - } -} - -impl From<&AccountMeta> for PodAccountMeta { - fn from(meta: &AccountMeta) -> Self { - Self { - pubkey: meta.pubkey, - is_signer: meta.is_signer.into(), - is_writable: meta.is_writable.into(), - } - } -} - -impl From<&PodAccountMeta> for AccountMeta { - fn from(meta: &PodAccountMeta) -> Self { - Self { - pubkey: meta.pubkey, - is_signer: meta.is_signer.into(), - is_writable: meta.is_writable.into(), - } - } -} diff --git a/libraries/tlv-account-resolution/src/seeds.rs b/libraries/tlv-account-resolution/src/seeds.rs new file mode 100644 index 00000000000..716d9db5806 --- /dev/null +++ b/libraries/tlv-account-resolution/src/seeds.rs @@ -0,0 +1,516 @@ +//! Types for managing seed configurations in TLV Account Resolution +//! +//! As determined by the `address_config` field of `ExtraAccountMeta`, +//! seed configurations are limited to a maximum of 32 bytes. +//! This means that the maximum number of seed configurations that can be +//! packed into a single `ExtraAccountMeta` will depend directly on the size +//! of the seed configurations themselves. +//! +//! Sizes are as follows: +//! * `Seed::Literal`: 1 + 1 + N +//! * 1 - Discriminator +//! * 1 - Length of literal +//! * N - Literal bytes themselves +//! * `Seed::InstructionData`: 1 + 1 + 1 = 3 +//! * 1 - Discriminator +//! * 1 - Start index of instruction data +//! * 1 - Length of instruction data starting at index +//! * `Seed::AccountKey` - 1 + 1 = 2 +//! * 1 - Discriminator +//! * 1 - Index of account in accounts list +//! * `Seed::AccountData`: 1 + 1 + 1 + 1 = 4 +//! * 1 - Discriminator +//! * 1 - Index of account in accounts list +//! * 1 - Start index of account data +//! * 1 - Length of account data starting at index +//! +//! No matter which types of seeds you choose, the total size of all seed +//! configurations must be less than or equal to 32 bytes. + +use {crate::error::AccountResolutionError, solana_program::program_error::ProgramError}; + +/// Enum to describe a required seed for a Program-Derived Address +#[derive(Clone, Debug, PartialEq)] +pub enum Seed { + /// Uninitialized configuration byte space + Uninitialized, + /// A literal hard-coded argument + /// Packed as: + /// * 1 - Discriminator + /// * 1 - Length of literal + /// * N - Literal bytes themselves + Literal { + /// The literal value repesented as a vector of bytes. + /// + /// For example, if a literal value is a string literal, + /// such as "my-seed", this value would be + /// `"my-seed".as_bytes().to_vec()`. + bytes: Vec, + }, + /// An instruction-provided argument, to be resolved from the instruction + /// data + /// Packed as: + /// * 1 - Discriminator + /// * 1 - Start index of instruction data + /// * 1 - Length of instruction data starting at index + InstructionData { + /// The index where the bytes of an instruction argument begin + index: u8, + /// The length of the instruction argument (number of bytes) + /// + /// Note: Max seed length is 32 bytes, so `u8` is appropriate here + length: u8, + }, + /// The public key of an account from the entire accounts list. + /// Note: This includes an extra accounts required. + /// + /// Packed as: + /// * 1 - Discriminator + /// * 1 - Index of account in accounts list + AccountKey { + /// The index of the account in the entire accounts list + index: u8, + }, + /// An argument to be resolved from the inner data of some account + /// Packed as: + /// * 1 - Discriminator + /// * 1 - Index of account in accounts list + /// * 1 - Start index of account data + /// * 1 - Length of account data starting at index + AccountData { + /// The index of the account in the entire accounts list + account_index: u8, + /// The index where the bytes of an account data argument begin + data_index: u8, + /// The length of the argument (number of bytes) + /// + /// Note: Max seed length is 32 bytes, so `u8` is appropriate here + length: u8, + }, +} +impl Seed { + /// Get the size of a seed configuration + pub fn tlv_size(&self) -> u8 { + match &self { + // 1 byte for the discriminator + Self::Uninitialized => 0, + // 1 byte for the discriminator, 1 byte for the length of the bytes, then the raw bytes + Self::Literal { bytes } => 1 + 1 + bytes.len() as u8, + // 1 byte for the discriminator, 1 byte for the index, 1 byte for the length + Self::InstructionData { .. } => 1 + 1 + 1, + // 1 byte for the discriminator, 1 byte for the index + Self::AccountKey { .. } => 1 + 1, + // 1 byte for the discriminator, 1 byte for the account index, + // 1 byte for the data index 1 byte for the length + Self::AccountData { .. } => 1 + 1 + 1 + 1, + } + } + + /// Packs a seed configuration into a slice + pub fn pack(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + if dst.len() != self.tlv_size() as usize { + return Err(AccountResolutionError::NotEnoughBytesForSeed.into()); + } + if dst.len() > 32 { + return Err(AccountResolutionError::SeedConfigsTooLarge.into()); + } + match &self { + Self::Uninitialized => return Err(AccountResolutionError::InvalidSeedConfig.into()), + Self::Literal { bytes } => { + dst[0] = 1; + dst[1] = bytes.len() as u8; + dst[2..].copy_from_slice(bytes); + } + Self::InstructionData { index, length } => { + dst[0] = 2; + dst[1] = *index; + dst[2] = *length; + } + Self::AccountKey { index } => { + dst[0] = 3; + dst[1] = *index; + } + Self::AccountData { + account_index, + data_index, + length, + } => { + dst[0] = 4; + dst[1] = *account_index; + dst[2] = *data_index; + dst[3] = *length; + } + } + Ok(()) + } + + /// Packs a vector of seed configurations into a 32-byte array, + /// filling the rest with 0s. Errors if it overflows. + pub fn pack_into_address_config(seeds: &[Self]) -> Result<[u8; 32], ProgramError> { + let mut packed = [0u8; 32]; + let mut i: usize = 0; + for seed in seeds { + let seed_size = seed.tlv_size() as usize; + let slice_end = i + seed_size; + if slice_end > 32 { + return Err(AccountResolutionError::SeedConfigsTooLarge.into()); + } + seed.pack(&mut packed[i..slice_end])?; + i = slice_end; + } + Ok(packed) + } + + /// Unpacks a seed configuration from a slice + pub fn unpack(bytes: &[u8]) -> Result { + let (discrim, rest) = bytes + .split_first() + .ok_or::(ProgramError::InvalidAccountData)?; + match discrim { + 0 => Ok(Self::Uninitialized), + 1 => unpack_seed_literal(rest), + 2 => unpack_seed_instruction_arg(rest), + 3 => unpack_seed_account_key(rest), + 4 => unpack_seed_account_data(rest), + _ => Err(ProgramError::InvalidAccountData), + } + } + + /// Unpacks all seed configurations from a 32-byte array. + /// Stops when it hits uninitialized data (0s). + pub fn unpack_address_config(address_config: &[u8; 32]) -> Result, ProgramError> { + let mut seeds = vec![]; + let mut i = 0; + while i < 32 { + let seed = Self::unpack(&address_config[i..])?; + let seed_size = seed.tlv_size() as usize; + i += seed_size; + if seed == Self::Uninitialized { + break; + } + seeds.push(seed); + } + Ok(seeds) + } +} + +fn unpack_seed_literal(bytes: &[u8]) -> Result { + let (length, rest) = bytes + .split_first() + // Should be at least 1 byte + .ok_or::(AccountResolutionError::InvalidBytesForSeed.into())?; + let length = *length as usize; + if rest.len() < length { + // Should be at least `length` bytes + return Err(AccountResolutionError::InvalidBytesForSeed.into()); + } + Ok(Seed::Literal { + bytes: rest[..length].to_vec(), + }) +} + +fn unpack_seed_instruction_arg(bytes: &[u8]) -> Result { + if bytes.len() < 2 { + // Should be at least 2 bytes + return Err(AccountResolutionError::InvalidBytesForSeed.into()); + } + Ok(Seed::InstructionData { + index: bytes[0], + length: bytes[1], + }) +} + +fn unpack_seed_account_key(bytes: &[u8]) -> Result { + if bytes.is_empty() { + // Should be at least 1 byte + return Err(AccountResolutionError::InvalidBytesForSeed.into()); + } + Ok(Seed::AccountKey { index: bytes[0] }) +} + +fn unpack_seed_account_data(bytes: &[u8]) -> Result { + if bytes.len() < 3 { + // Should be at least 3 bytes + return Err(AccountResolutionError::InvalidBytesForSeed.into()); + } + Ok(Seed::AccountData { + account_index: bytes[0], + data_index: bytes[1], + length: bytes[2], + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pack() { + // Seed too large + let seed = Seed::Literal { bytes: vec![1; 33] }; + let mut packed = vec![0u8; seed.tlv_size() as usize]; + assert_eq!( + seed.pack(&mut packed).unwrap_err(), + AccountResolutionError::SeedConfigsTooLarge.into() + ); + assert_eq!( + Seed::pack_into_address_config(&[seed]).unwrap_err(), + AccountResolutionError::SeedConfigsTooLarge.into() + ); + + // Should fail if the length is wrong + let seed = Seed::Literal { bytes: vec![1; 12] }; + let mut packed = vec![0u8; seed.tlv_size() as usize - 1]; + assert_eq!( + seed.pack(&mut packed).unwrap_err(), + AccountResolutionError::NotEnoughBytesForSeed.into() + ); + + // Can't pack a `Seed::Uninitialized` + let seed = Seed::Uninitialized; + let mut packed = vec![0u8; seed.tlv_size() as usize]; + assert_eq!( + seed.pack(&mut packed).unwrap_err(), + AccountResolutionError::InvalidSeedConfig.into() + ); + } + + #[test] + fn test_pack_address_config() { + // Should fail if one seed is too large + let seed = Seed::Literal { bytes: vec![1; 36] }; + assert_eq!( + Seed::pack_into_address_config(&[seed]).unwrap_err(), + AccountResolutionError::SeedConfigsTooLarge.into() + ); + + // Should fail if the combination of all seeds is too large + let seed1 = Seed::Literal { bytes: vec![1; 30] }; // 30 bytes + let seed2 = Seed::InstructionData { + index: 0, + length: 4, + }; // 3 bytes + assert_eq!( + Seed::pack_into_address_config(&[seed1, seed2]).unwrap_err(), + AccountResolutionError::SeedConfigsTooLarge.into() + ); + } + + #[test] + fn test_unpack() { + // Can unpack zeroes + let zeroes = [0u8; 32]; + let seeds = Seed::unpack_address_config(&zeroes).unwrap(); + assert_eq!(seeds, vec![]); + + // Should fail for empty bytes + let bytes = []; + assert_eq!( + Seed::unpack(&bytes).unwrap_err(), + ProgramError::InvalidAccountData + ); + + // Should fail if bytes are malformed for literal seed + let bytes = [ + 1, // Discrim (Literal) + 4, // Length + 1, 1, 1, // Incorrect length + ]; + assert_eq!( + Seed::unpack(&bytes).unwrap_err(), + AccountResolutionError::InvalidBytesForSeed.into() + ); + + // Should fail if bytes are malformed for literal seed + let bytes = [ + 2, // Discrim (InstructionData) + 2, // Index (Length missing) + ]; + assert_eq!( + Seed::unpack(&bytes).unwrap_err(), + AccountResolutionError::InvalidBytesForSeed.into() + ); + + // Should fail if bytes are malformed for literal seed + let bytes = [ + 3, // Discrim (AccountKey, Index missing) + ]; + assert_eq!( + Seed::unpack(&bytes).unwrap_err(), + AccountResolutionError::InvalidBytesForSeed.into() + ); + } + + #[test] + fn test_unpack_address_config() { + // Should fail if bytes are malformed + let bytes = [ + 1, // Discrim (Literal) + 4, // Length + 1, 1, 1, 1, // 4 + 6, // Discrim (Invalid) + 2, // Index + 1, // Length + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!( + Seed::unpack_address_config(&bytes).unwrap_err(), + ProgramError::InvalidAccountData + ); + + // Should fail if 32nd byte is not zero, but it would be the + // start of a config + // + // Namely, if a seed config is unpacked and leaves 1 byte remaining, + // it has to be 0, since no valid seed config can be 1 byte long + let bytes = [ + 1, // Discrim (Literal) + 16, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 16 + 1, // Discrim (Literal) + 11, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 11 + 2, // Non-zero byte + ]; + assert_eq!( + Seed::unpack_address_config(&bytes).unwrap_err(), + AccountResolutionError::InvalidBytesForSeed.into(), + ); + + // Should pass if 31st byte is not zero, but it would be + // the start of a config + // + // Similar to above, however we now have 2 bytes to work with, + // which could be a valid seed config + let bytes = [ + 1, // Discrim (Literal) + 16, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 16 + 1, // Discrim (Literal) + 10, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 + 3, // Non-zero byte - Discrim (AccountKey) + 0, // Index + ]; + assert_eq!( + Seed::unpack_address_config(&bytes).unwrap(), + vec![ + Seed::Literal { + bytes: vec![1u8; 16] + }, + Seed::Literal { + bytes: vec![1u8; 10] + }, + Seed::AccountKey { index: 0 } + ], + ); + + // Should fail if 31st byte is not zero and a valid seed config + // discriminator, but the seed config requires more than 2 bytes + let bytes = [ + 1, // Discrim (Literal) + 16, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 16 + 1, // Discrim (Literal) + 10, // Length + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 + 2, // Non-zero byte - Discrim (InstructionData) + 0, // Index (Length missing) + ]; + assert_eq!( + Seed::unpack_address_config(&bytes).unwrap_err(), + AccountResolutionError::InvalidBytesForSeed.into(), + ); + } + + fn test_pack_unpack_seed(seed: Seed) { + let tlv_size = seed.tlv_size() as usize; + let mut packed = vec![0u8; tlv_size]; + seed.pack(&mut packed).unwrap(); + let unpacked = Seed::unpack(&packed).unwrap(); + assert_eq!(seed, unpacked); + } + + #[test] + fn test_pack_unpack() { + let mut mixed = vec![]; + + // Literals + + let bytes = b"hello"; + let seed = Seed::Literal { + bytes: bytes.to_vec(), + }; + test_pack_unpack_seed(seed); + + let bytes = 8u8.to_le_bytes(); + let seed = Seed::Literal { + bytes: bytes.to_vec(), + }; + test_pack_unpack_seed(seed.clone()); + mixed.push(seed); + + let bytes = 32u32.to_le_bytes(); + let seed = Seed::Literal { + bytes: bytes.to_vec(), + }; + test_pack_unpack_seed(seed.clone()); + mixed.push(seed); + + // Instruction args + + let seed = Seed::InstructionData { + index: 0, + length: 0, + }; + test_pack_unpack_seed(seed); + + let seed = Seed::InstructionData { + index: 6, + length: 9, + }; + test_pack_unpack_seed(seed.clone()); + mixed.push(seed); + + // Account keys + + let seed = Seed::AccountKey { index: 0 }; + test_pack_unpack_seed(seed); + + let seed = Seed::AccountKey { index: 9 }; + test_pack_unpack_seed(seed.clone()); + mixed.push(seed); + + // Account data + + let seed = Seed::AccountData { + account_index: 0, + data_index: 0, + length: 0, + }; + test_pack_unpack_seed(seed); + + let seed = Seed::AccountData { + account_index: 0, + data_index: 0, + length: 9, + }; + test_pack_unpack_seed(seed.clone()); + mixed.push(seed); + + // Arrays + + let packed_array = Seed::pack_into_address_config(&mixed).unwrap(); + let unpacked_array = Seed::unpack_address_config(&packed_array).unwrap(); + assert_eq!(mixed, unpacked_array); + + let mut shuffled_mixed = mixed.clone(); + shuffled_mixed.swap(0, 1); + shuffled_mixed.swap(1, 4); + shuffled_mixed.swap(3, 0); + + let packed_array = Seed::pack_into_address_config(&shuffled_mixed).unwrap(); + let unpacked_array = Seed::unpack_address_config(&packed_array).unwrap(); + assert_eq!(shuffled_mixed, unpacked_array); + } +} diff --git a/libraries/tlv-account-resolution/src/state.rs b/libraries/tlv-account-resolution/src/state.rs index c46f3cdb08f..5a687bfeddc 100644 --- a/libraries/tlv-account-resolution/src/state.rs +++ b/libraries/tlv-account-resolution/src/state.rs @@ -1,36 +1,84 @@ //! State transition types use { - crate::{error::AccountResolutionError, pod::PodAccountMeta}, + crate::{account::ExtraAccountMeta, error::AccountResolutionError}, solana_program::{ account_info::AccountInfo, instruction::{AccountMeta, Instruction}, program_error::ProgramError, + pubkey::Pubkey, }, spl_discriminator::SplDiscriminate, - spl_type_length_value::{ - pod::{PodSlice, PodSliceMut}, - state::{TlvState, TlvStateBorrowed, TlvStateMut}, - }, + spl_pod::slice::{PodSlice, PodSliceMut}, + spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut}, + std::future::Future, }; -/// Stateless helper for storing additional accounts required for an instruction. +/// Type representing the output of an account fetching function, for easy +/// chaining between APIs +pub type AccountDataResult = Result>, AccountFetchError>; +/// Generic error type that can come out of any client while fetching account data +pub type AccountFetchError = Box; + +/// Helper to convert an `AccountInfo` to an `AccountMeta` +fn account_info_to_meta(account_info: &AccountInfo) -> AccountMeta { + AccountMeta { + pubkey: *account_info.key, + is_signer: account_info.is_signer, + is_writable: account_info.is_writable, + } +} + +/// De-escalate an account meta if necessary +fn de_escalate_account_meta(account_meta: &mut AccountMeta, account_metas: &[AccountMeta]) { + // This is a little tricky to read, but the idea is to see if + // this account is marked as writable or signer anywhere in + // the instruction at the start. If so, DON'T escalate it to + // be a writer or signer in the CPI + let maybe_highest_privileges = account_metas + .iter() + .filter(|&x| x.pubkey == account_meta.pubkey) + .map(|x| (x.is_signer, x.is_writable)) + .reduce(|acc, x| (acc.0 || x.0, acc.1 || x.1)); + // If `Some`, then the account was found somewhere in the instruction + if let Some((is_signer, is_writable)) = maybe_highest_privileges { + if !is_signer && is_signer != account_meta.is_signer { + // Existing account is *NOT* a signer already, but the CPI + // wants it to be, so de-escalate to not be a signer + account_meta.is_signer = false; + } + if !is_writable && is_writable != account_meta.is_writable { + // Existing account is *NOT* writable already, but the CPI + // wants it to be, so de-escalate to not be writable + account_meta.is_writable = false; + } + } +} + +/// Stateless helper for storing additional accounts required for an +/// instruction. /// /// This struct works with any `SplDiscriminate`, and stores the extra accounts -/// needed for that specific instruction, using the given `ArrayDiscriminator` as the -/// type-length-value `ArrayDiscriminator`, and then storing all of the given -/// `AccountMeta`s as a zero-copy slice. +/// needed for that specific instruction, using the given `ArrayDiscriminator` +/// as the type-length-value `ArrayDiscriminator`, and then storing all of the +/// given `AccountMeta`s as a zero-copy slice. /// /// Sample usage: /// -/// ``` +/// ```rust /// use { +/// futures_util::TryFutureExt, +/// solana_client::nonblocking::rpc_client::RpcClient, /// solana_program::{ /// account_info::AccountInfo, instruction::{AccountMeta, Instruction}, /// pubkey::Pubkey /// }, /// spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, -/// spl_tlv_account_resolution::state::ExtraAccountMetas, +/// spl_tlv_account_resolution::{ +/// account::ExtraAccountMeta, +/// seeds::Seed, +/// state::{AccountDataResult, AccountFetchError, ExtraAccountMetaList} +/// }, /// }; /// /// struct MyInstruction; @@ -41,156 +89,221 @@ use { /// /// // actually put it in the additional required account keys and signer / writable /// let extra_metas = [ -/// AccountMeta::new(Pubkey::new_unique(), false), -/// AccountMeta::new(Pubkey::new_unique(), true), -/// AccountMeta::new_readonly(Pubkey::new_unique(), true), -/// AccountMeta::new_readonly(Pubkey::new_unique(), false), +/// AccountMeta::new(Pubkey::new_unique(), false).into(), +/// AccountMeta::new_readonly(Pubkey::new_unique(), false).into(), +/// ExtraAccountMeta::new_with_seeds( +/// &[ +/// Seed::Literal { +/// bytes: b"some_string".to_vec(), +/// }, +/// Seed::InstructionData { +/// index: 1, +/// length: 1, // u8 +/// }, +/// Seed::AccountKey { index: 1 }, +/// ], +/// false, +/// true, +/// ).unwrap(), +/// ExtraAccountMeta::new_external_pda_with_seeds( +/// 0, +/// &[Seed::AccountKey { index: 2 }], +/// false, +/// false, +/// ).unwrap(), /// ]; /// /// // assume that this buffer is actually account data, already allocated to `account_size` -/// let account_size = ExtraAccountMetas::size_of(extra_metas.len()).unwrap(); +/// let account_size = ExtraAccountMetaList::size_of(extra_metas.len()).unwrap(); /// let mut buffer = vec![0; account_size]; /// /// // Initialize the structure for your instruction -/// ExtraAccountMetas::init_with_account_metas::(&mut buffer, &extra_metas).unwrap(); +/// ExtraAccountMetaList::init::(&mut buffer, &extra_metas).unwrap(); /// /// // Off-chain, you can add the additional accounts directly from the account data +/// // You need to provide the resolver a way to fetch account data off-chain +/// struct MyClient { +/// client: RpcClient, +/// } +/// impl MyClient { +/// pub fn new() -> Self { +/// Self { +/// client: RpcClient::new_mock("succeeds".to_string()), +/// } +/// } +/// pub async fn get_account_data(&self, address: Pubkey) -> AccountDataResult { +/// self.client.get_account(&address) +/// .await +/// .map(|acct| Some(acct.data)) +/// .map_err(|e| Box::new(e) as AccountFetchError) +/// } +/// } +/// +/// let client = MyClient::new(); /// let program_id = Pubkey::new_unique(); -/// let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]); -/// ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer).unwrap(); +/// let mut instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]); +/// # futures::executor::block_on(async { +/// // Now use the resolver to add the additional accounts off-chain +/// ExtraAccountMetaList::add_to_instruction::( +/// &mut instruction, +/// |address: Pubkey| client.get_account_data(address), +/// &buffer, +/// ) +/// .await; +/// # }); /// /// // On-chain, you can add the additional accounts *and* account infos -/// let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[], vec![]); +/// let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]); /// let mut cpi_account_infos = vec![]; // assume the other required account infos are already included /// let remaining_account_infos: &[AccountInfo<'_>] = &[]; // these are the account infos provided to the instruction that are *not* part of any other known interface -/// ExtraAccountMetas::add_to_cpi_instruction::( +/// ExtraAccountMetaList::add_to_cpi_instruction::( /// &mut cpi_instruction, /// &mut cpi_account_infos, /// &buffer, /// &remaining_account_infos, /// ); /// ``` -pub struct ExtraAccountMetas; -impl ExtraAccountMetas { - /// Initialize pod slice data for the given instruction and any type - /// convertible to account metas - pub fn init<'a, T: SplDiscriminate, M>( +pub struct ExtraAccountMetaList; +impl ExtraAccountMetaList { + /// Initialize pod slice data for the given instruction and its required + /// list of `ExtraAccountMeta`s + pub fn init( data: &mut [u8], - convertible_account_metas: &'a [M], - ) -> Result<(), ProgramError> - where - PodAccountMeta: From<&'a M>, - { + extra_account_metas: &[ExtraAccountMeta], + ) -> Result<(), ProgramError> { let mut state = TlvStateMut::unpack(data).unwrap(); - let tlv_size = PodSlice::::size_of(convertible_account_metas.len())?; - let bytes = state.alloc::(tlv_size)?; - let mut extra_account_metas = PodSliceMut::init(bytes)?; - for account_metas in convertible_account_metas { - extra_account_metas.push(PodAccountMeta::from(account_metas))?; + let tlv_size = PodSlice::::size_of(extra_account_metas.len())?; + let (bytes, _) = state.alloc::(tlv_size, false)?; + let mut validation_data = PodSliceMut::init(bytes)?; + for meta in extra_account_metas { + validation_data.push(*meta)?; } Ok(()) } - /// Initialize a TLV entry for the given discriminator, populating the data - /// with the given account infos - pub fn init_with_account_infos( - data: &mut [u8], - account_infos: &[AccountInfo<'_>], - ) -> Result<(), ProgramError> { - Self::init::(data, account_infos) - } - - /// Get the underlying `PodSlice` from an unpacked TLV + /// Get the underlying `PodSlice` from an unpacked TLV /// /// Due to lifetime annoyances, this function can't just take in the bytes, /// since then we would be returning a reference to a locally created /// `TlvStateBorrowed`. I hope there's a better way to do this! pub fn unpack_with_tlv_state<'a, T: SplDiscriminate>( tlv_state: &'a TlvStateBorrowed, - ) -> Result, ProgramError> { - let bytes = tlv_state.get_bytes::()?; - PodSlice::::unpack(bytes) - } - - /// Initialize a TLV entry for the given discriminator, populating the data - /// with the given account metas - pub fn init_with_account_metas( - data: &mut [u8], - account_metas: &[AccountMeta], - ) -> Result<(), ProgramError> { - Self::init::(data, account_metas) + ) -> Result, ProgramError> { + let bytes = tlv_state.get_first_bytes::()?; + PodSlice::::unpack(bytes) } /// Get the byte size required to hold `num_items` items pub fn size_of(num_items: usize) -> Result { Ok(TlvStateBorrowed::get_base_len() - .saturating_add(PodSlice::::size_of(num_items)?)) + .saturating_add(PodSlice::::size_of(num_items)?)) } - fn de_escalate_account_meta( - account_meta: &mut AccountMeta, - account_metas: &[AccountMeta], - initial_length: usize, - ) { - // This is a little tricky to read, but the idea is to see if - // this account is marked as writable or signer anywhere in - // the instruction at the start. If so, DON'T escalate it to - // be a writer or signer in the CPI - let maybe_highest_privileges = account_metas + /// Checks provided account infos against validation data, using + /// instruction data and program ID to resolve any dynamic PDAs + /// if necessary. + /// + /// Note: this function will also verify all extra required accounts + /// have been provided in the correct order + pub fn check_account_infos( + account_infos: &[AccountInfo], + instruction_data: &[u8], + program_id: &Pubkey, + data: &[u8], + ) -> Result<(), ProgramError> { + let state = TlvStateBorrowed::unpack(data).unwrap(); + let extra_meta_list = ExtraAccountMetaList::unpack_with_tlv_state::(&state)?; + let extra_account_metas = extra_meta_list.data(); + + let initial_accounts_len = account_infos.len() - extra_account_metas.len(); + + // Convert to `AccountMeta` to check resolved metas + let provided_metas = account_infos .iter() - .take(initial_length) - .filter(|&x| x.pubkey == account_meta.pubkey) - .map(|x| (x.is_signer, x.is_writable)) - .reduce(|acc, x| (acc.0 || x.0, acc.1 || x.1)); - // If `Some`, then the account was found somewhere in the instruction - if let Some((is_signer, is_writable)) = maybe_highest_privileges { - if !is_signer && is_signer != account_meta.is_signer { - // Existing account is *NOT* a signer already, but the CPI - // wants it to be, so de-escalate to not be a signer - account_meta.is_signer = false; - } - if !is_writable && is_writable != account_meta.is_writable { - // Existing account is *NOT* writable already, but the CPI - // wants it to be, so de-escalate to not be writable - account_meta.is_writable = false; + .map(account_info_to_meta) + .collect::>(); + + for (i, config) in extra_account_metas.iter().enumerate() { + let meta = { + // Create a list of `Ref`s so we can reference account data in the + // resolution step + let account_key_data_refs = account_infos + .iter() + .map(|info| { + let key = *info.key; + let data = info.try_borrow_data()?; + Ok((key, data)) + }) + .collect::, ProgramError>>()?; + + config.resolve(instruction_data, program_id, |usize| { + account_key_data_refs + .get(usize) + .map(|(pubkey, opt_data)| (pubkey, Some(opt_data.as_ref()))) + })? + }; + + // Ensure the account is in the correct position + let expected_index = i + .checked_add(initial_accounts_len) + .ok_or::(AccountResolutionError::CalculationFailure.into())?; + if provided_metas.get(expected_index) != Some(&meta) { + return Err(AccountResolutionError::IncorrectAccount.into()); } } - } - /// Add the additional account metas to an existing instruction - pub fn add_to_vec( - account_metas: &mut Vec, - data: &[u8], - ) -> Result<(), ProgramError> { - let state = TlvStateBorrowed::unpack(data)?; - let bytes = state.get_bytes::()?; - let extra_account_metas = PodSlice::::unpack(bytes)?; - let initial_instruction_length = account_metas.len(); - for mut account_meta in extra_account_metas.data().iter().map(AccountMeta::from) { - Self::de_escalate_account_meta( - &mut account_meta, - account_metas, - initial_instruction_length, - ); - account_metas.push(account_meta); - } Ok(()) } /// Add the additional account metas to an existing instruction - pub fn add_to_instruction( + pub async fn add_to_instruction( instruction: &mut Instruction, + fetch_account_data_fn: F, data: &[u8], - ) -> Result<(), ProgramError> { - Self::add_to_vec::(&mut instruction.accounts, data) + ) -> Result<(), ProgramError> + where + F: Fn(Pubkey) -> Fut, + Fut: Future, + { + let state = TlvStateBorrowed::unpack(data)?; + let bytes = state.get_first_bytes::()?; + let extra_account_metas = PodSlice::::unpack(bytes)?; + + // Fetch account data for each of the instruction accounts + let mut account_key_datas = vec![]; + for meta in instruction.accounts.iter() { + let account_data = fetch_account_data_fn(meta.pubkey) + .await + .map_err::(|_| { + AccountResolutionError::AccountFetchFailed.into() + })?; + account_key_datas.push((meta.pubkey, account_data)); + } + + for extra_meta in extra_account_metas.data().iter() { + let mut meta = + extra_meta.resolve(&instruction.data, &instruction.program_id, |usize| { + account_key_datas + .get(usize) + .map(|(pubkey, opt_data)| (pubkey, opt_data.as_ref().map(|x| x.as_slice()))) + })?; + de_escalate_account_meta(&mut meta, &instruction.accounts); + + // Fetch account data for the new account + account_key_datas.push(( + meta.pubkey, + fetch_account_data_fn(meta.pubkey) + .await + .map_err::(|_| { + AccountResolutionError::AccountFetchFailed.into() + })?, + )); + instruction.accounts.push(meta); + } + Ok(()) } - /// Add the additional account metas and account infos for a CPI, while - /// de-escalating repeated accounts. - /// - /// If an added account already exists in the instruction with lower - /// privileges, match it to the existing account. This prevents a lower - /// program from gaining unexpected privileges. + /// Add the additional account metas and account infos for a CPI pub fn add_to_cpi_instruction<'a, T: SplDiscriminate>( cpi_instruction: &mut Instruction, cpi_account_infos: &mut Vec>, @@ -198,24 +311,42 @@ impl ExtraAccountMetas { account_infos: &[AccountInfo<'a>], ) -> Result<(), ProgramError> { let state = TlvStateBorrowed::unpack(data)?; - let bytes = state.get_bytes::()?; - let extra_account_metas = PodSlice::::unpack(bytes)?; + let bytes = state.get_first_bytes::()?; + let extra_account_metas = PodSlice::::unpack(bytes)?; - let initial_cpi_instruction_length = cpi_instruction.accounts.len(); + for extra_meta in extra_account_metas.data().iter() { + let mut meta = { + // Create a list of `Ref`s so we can reference account data in the + // resolution step + let account_key_data_refs = cpi_account_infos + .iter() + .map(|info| { + let key = *info.key; + let data = info.try_borrow_data()?; + Ok((key, data)) + }) + .collect::, ProgramError>>()?; + + extra_meta.resolve( + &cpi_instruction.data, + &cpi_instruction.program_id, + |usize| { + account_key_data_refs + .get(usize) + .map(|(pubkey, opt_data)| (pubkey, Some(opt_data.as_ref()))) + }, + )? + }; + de_escalate_account_meta(&mut meta, &cpi_instruction.accounts); - for mut account_meta in extra_account_metas.data().iter().map(AccountMeta::from) { let account_info = account_infos .iter() - .find(|&x| *x.key == account_meta.pubkey) + .find(|&x| *x.key == meta.pubkey) .ok_or(AccountResolutionError::IncorrectAccount)? .clone(); - Self::de_escalate_account_meta( - &mut account_meta, - &cpi_instruction.accounts, - initial_cpi_instruction_length, - ); + + cpi_instruction.accounts.push(meta); cpi_account_infos.push(account_info); - cpi_instruction.accounts.push(account_meta); } Ok(()) } @@ -225,8 +356,11 @@ impl ExtraAccountMetas { mod tests { use { super::*, + crate::seeds::Seed, solana_program::{clock::Epoch, instruction::AccountMeta, pubkey::Pubkey}, + solana_program_test::tokio, spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, + std::collections::HashMap, }; pub struct TestInstruction; @@ -241,88 +375,68 @@ mod tests { ArrayDiscriminator::new([2; ArrayDiscriminator::LENGTH]); } - #[test] - fn init_with_metas() { - let metas = [ - AccountMeta::new(Pubkey::new_unique(), false), - AccountMeta::new(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), false), - ]; - let account_size = ExtraAccountMetas::size_of(metas.len()).unwrap(); - let mut buffer = vec![0; account_size]; - - ExtraAccountMetas::init_with_account_metas::(&mut buffer, &metas).unwrap(); + pub struct MockRpc<'a> { + cache: HashMap>, + } + impl<'a> MockRpc<'a> { + pub fn setup(account_infos: &'a [AccountInfo<'a>]) -> Self { + let mut cache = HashMap::new(); + for info in account_infos { + cache.insert(*info.key, info); + } + Self { cache } + } - let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); - assert_eq!( - instruction - .accounts - .iter() - .map(PodAccountMeta::from) - .collect::>(), - metas.iter().map(PodAccountMeta::from).collect::>() - ); + pub async fn get_account_data(&self, pubkey: Pubkey) -> AccountDataResult { + Ok(self + .cache + .get(&pubkey) + .map(|account| account.try_borrow_data().unwrap().to_vec())) + } } - #[test] - fn init_multiple() { + #[tokio::test] + async fn init_with_metas() { let metas = [ - AccountMeta::new(Pubkey::new_unique(), false), - AccountMeta::new(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), false), + AccountMeta::new(Pubkey::new_unique(), false).into(), + AccountMeta::new(Pubkey::new_unique(), true).into(), + AccountMeta::new_readonly(Pubkey::new_unique(), true).into(), + AccountMeta::new_readonly(Pubkey::new_unique(), false).into(), ]; - let other_metas = [AccountMeta::new(Pubkey::new_unique(), false)]; - let account_size = ExtraAccountMetas::size_of(metas.len()).unwrap() - + ExtraAccountMetas::size_of(other_metas.len()).unwrap(); + let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); let mut buffer = vec![0; account_size]; - ExtraAccountMetas::init_with_account_metas::(&mut buffer, &metas).unwrap(); - ExtraAccountMetas::init_with_account_metas::( - &mut buffer, - &other_metas, + ExtraAccountMetaList::init::(&mut buffer, &metas).unwrap(); + + let mock_rpc = MockRpc::setup(&[]); + + let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, ) + .await .unwrap(); - let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); - assert_eq!( - instruction - .accounts - .iter() - .map(PodAccountMeta::from) - .collect::>(), - metas.iter().map(PodAccountMeta::from).collect::>() - ); - let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); - assert_eq!( - instruction - .accounts - .iter() - .map(PodAccountMeta::from) - .collect::>(), - other_metas - .iter() - .map(PodAccountMeta::from) - .collect::>() - ); + let check_metas = metas + .iter() + .map(|e| AccountMeta::try_from(e).unwrap()) + .collect::>(); + + assert_eq!(instruction.accounts, check_metas,); } - #[test] - fn init_mixed() { - // annoying to setup, but need to test this! + #[tokio::test] + async fn init_with_infos() { + let program_id = Pubkey::new_unique(); + let pubkey1 = Pubkey::new_unique(); let mut lamports1 = 0; let mut data1 = []; let pubkey2 = Pubkey::new_unique(); let mut lamports2 = 0; - let mut data2 = []; + let mut data2 = [4, 4, 4, 6, 6, 6, 8, 8]; let pubkey3 = Pubkey::new_unique(); let mut lamports3 = 0; let mut data3 = []; @@ -359,54 +473,293 @@ mod tests { Epoch::default(), ), ]; - let metas = [ - AccountMeta::new(Pubkey::new_unique(), false), - AccountMeta::new(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), true), - AccountMeta::new_readonly(Pubkey::new_unique(), false), - AccountMeta::new_readonly(Pubkey::new_unique(), false), - AccountMeta::new_readonly(Pubkey::new_unique(), false), + + let required_pda = ExtraAccountMeta::new_with_seeds( + &[ + Seed::AccountKey { index: 0 }, + Seed::AccountData { + account_index: 1, + data_index: 2, + length: 4, + }, + ], + false, + true, + ) + .unwrap(); + + // Convert to `ExtraAccountMeta` + let required_extra_accounts = [ + ExtraAccountMeta::from(&account_infos[0]), + ExtraAccountMeta::from(&account_infos[1]), + ExtraAccountMeta::from(&account_infos[2]), + required_pda, ]; - let account_size = ExtraAccountMetas::size_of(account_infos.len()).unwrap() - + ExtraAccountMetas::size_of(metas.len()).unwrap(); + + let account_size = ExtraAccountMetaList::size_of(required_extra_accounts.len()).unwrap(); let mut buffer = vec![0; account_size]; - ExtraAccountMetas::init_with_account_infos::(&mut buffer, &account_infos) - .unwrap(); - ExtraAccountMetas::init_with_account_metas::(&mut buffer, &metas) + ExtraAccountMetaList::init::(&mut buffer, &required_extra_accounts) .unwrap(); - let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); + let mock_rpc = MockRpc::setup(&account_infos); + + let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + let (check_required_pda, _) = Pubkey::find_program_address( + &[ + account_infos[0].key.as_ref(), // Account key + &account_infos[1].try_borrow_data().unwrap()[2..6], // Account data + ], + &program_id, + ); + + // Convert to `AccountMeta` to check instruction + let check_metas = [ + account_info_to_meta(&account_infos[0]), + account_info_to_meta(&account_infos[1]), + account_info_to_meta(&account_infos[2]), + AccountMeta::new(check_required_pda, false), + ]; + + assert_eq!(instruction.accounts, check_metas,); + assert_eq!( - instruction - .accounts - .iter() - .map(PodAccountMeta::from) - .collect::>(), - account_infos - .iter() - .map(PodAccountMeta::from) - .collect::>() + instruction.accounts.get(3).unwrap().pubkey, + check_required_pda ); + } + + #[tokio::test] + async fn init_with_extra_account_metas() { + let program_id = Pubkey::new_unique(); + + let extra_meta3_literal_str = "seed_prefix"; + + let ix_account1 = AccountMeta::new(Pubkey::new_unique(), false); + let ix_account2 = AccountMeta::new(Pubkey::new_unique(), true); + + let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false); + let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true); + let extra_meta3 = ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: extra_meta3_literal_str.as_bytes().to_vec(), + }, + Seed::InstructionData { + index: 1, + length: 1, // u8 + }, + Seed::AccountKey { index: 0 }, + Seed::AccountKey { index: 2 }, + ], + false, + true, + ) + .unwrap(); + + let metas = [ + ExtraAccountMeta::from(&extra_meta1), + ExtraAccountMeta::from(&extra_meta2), + extra_meta3, + ]; + + let ix_data = vec![1, 2, 3, 4]; + let ix_accounts = vec![ix_account1.clone(), ix_account2.clone()]; + let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts); + + let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); + let mut buffer = vec![0; account_size]; + + ExtraAccountMetaList::init::(&mut buffer, &metas).unwrap(); + + let mock_rpc = MockRpc::setup(&[]); + + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + let check_extra_meta3_u8_arg = ix_data[1]; + let check_extra_meta3_pubkey = Pubkey::find_program_address( + &[ + extra_meta3_literal_str.as_bytes(), + &[check_extra_meta3_u8_arg], + ix_account1.pubkey.as_ref(), + extra_meta1.pubkey.as_ref(), + ], + &program_id, + ) + .0; + let check_metas = [ + ix_account1, + ix_account2, + extra_meta1, + extra_meta2, + AccountMeta::new(check_extra_meta3_pubkey, false), + ]; - let mut instruction = Instruction::new_with_bytes(Pubkey::new_unique(), &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); assert_eq!( - instruction - .accounts - .iter() - .map(PodAccountMeta::from) - .collect::>(), - metas.iter().map(PodAccountMeta::from).collect::>() + instruction.accounts.get(4).unwrap().pubkey, + check_extra_meta3_pubkey, ); + assert_eq!(instruction.accounts, check_metas,); } - #[test] - fn cpi_instruction() { - // annoying to setup, but need to test this! + #[tokio::test] + async fn init_multiple() { + let extra_meta5_literal_str = "seed_prefix"; + let extra_meta5_literal_u32 = 4u32; + let other_meta2_literal_str = "other_seed_prefix"; + + let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false); + let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true); + let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), true); + let extra_meta4 = AccountMeta::new_readonly(Pubkey::new_unique(), false); + let extra_meta5 = ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: extra_meta5_literal_str.as_bytes().to_vec(), + }, + Seed::Literal { + bytes: extra_meta5_literal_u32.to_le_bytes().to_vec(), + }, + Seed::InstructionData { + index: 5, + length: 1, // u8 + }, + Seed::AccountKey { index: 2 }, + ], + false, + true, + ) + .unwrap(); + + let other_meta1 = AccountMeta::new(Pubkey::new_unique(), false); + let other_meta2 = ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: other_meta2_literal_str.as_bytes().to_vec(), + }, + Seed::InstructionData { + index: 1, + length: 4, // u32 + }, + Seed::AccountKey { index: 0 }, + ], + false, + true, + ) + .unwrap(); + + let metas = [ + ExtraAccountMeta::from(&extra_meta1), + ExtraAccountMeta::from(&extra_meta2), + ExtraAccountMeta::from(&extra_meta3), + ExtraAccountMeta::from(&extra_meta4), + extra_meta5, + ]; + let other_metas = [ExtraAccountMeta::from(&other_meta1), other_meta2]; + + let account_size = ExtraAccountMetaList::size_of(metas.len()).unwrap() + + ExtraAccountMetaList::size_of(other_metas.len()).unwrap(); + let mut buffer = vec![0; account_size]; + + ExtraAccountMetaList::init::(&mut buffer, &metas).unwrap(); + ExtraAccountMetaList::init::(&mut buffer, &other_metas).unwrap(); + + let mock_rpc = MockRpc::setup(&[]); + + let program_id = Pubkey::new_unique(); + let ix_data = vec![0, 0, 0, 0, 0, 7, 0, 0]; + let ix_accounts = vec![]; + let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + let check_extra_meta5_u8_arg = ix_data[5]; + let check_extra_meta5_pubkey = Pubkey::find_program_address( + &[ + extra_meta5_literal_str.as_bytes(), + extra_meta5_literal_u32.to_le_bytes().as_ref(), + &[check_extra_meta5_u8_arg], + extra_meta3.pubkey.as_ref(), + ], + &program_id, + ) + .0; + let check_metas = [ + extra_meta1, + extra_meta2, + extra_meta3, + extra_meta4, + AccountMeta::new(check_extra_meta5_pubkey, false), + ]; + + assert_eq!( + instruction.accounts.get(4).unwrap().pubkey, + check_extra_meta5_pubkey, + ); + assert_eq!(instruction.accounts, check_metas,); + + let program_id = Pubkey::new_unique(); + let ix_account1 = AccountMeta::new(Pubkey::new_unique(), false); + let ix_account2 = AccountMeta::new(Pubkey::new_unique(), true); + let ix_accounts = vec![ix_account1.clone(), ix_account2.clone()]; + let ix_data = vec![0, 26, 0, 0, 0, 0, 0]; + let mut instruction = Instruction::new_with_bytes(program_id, &ix_data, ix_accounts); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + let check_other_meta2_u32_arg = u32::from_le_bytes(ix_data[1..5].try_into().unwrap()); + let check_other_meta2_pubkey = Pubkey::find_program_address( + &[ + other_meta2_literal_str.as_bytes(), + check_other_meta2_u32_arg.to_le_bytes().as_ref(), + ix_account1.pubkey.as_ref(), + ], + &program_id, + ) + .0; + let check_other_metas = [ + ix_account1, + ix_account2, + other_meta1, + AccountMeta::new(check_other_meta2_pubkey, false), + ]; + + assert_eq!( + instruction.accounts.get(3).unwrap().pubkey, + check_other_meta2_pubkey, + ); + assert_eq!(instruction.accounts, check_other_metas,); + } + + #[tokio::test] + async fn init_mixed() { + let extra_meta5_literal_str = "seed_prefix"; + let extra_meta6_literal_u64 = 28u64; + let pubkey1 = Pubkey::new_unique(); let mut lamports1 = 0; let mut data1 = []; @@ -449,52 +802,470 @@ mod tests { Epoch::default(), ), ]; - let account_size = ExtraAccountMetas::size_of(account_infos.len()).unwrap(); + + let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false); + let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true); + let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), true); + let extra_meta4 = AccountMeta::new_readonly(Pubkey::new_unique(), false); + let extra_meta5 = ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: extra_meta5_literal_str.as_bytes().to_vec(), + }, + Seed::InstructionData { + index: 1, + length: 8, // [u8; 8] + }, + Seed::InstructionData { + index: 9, + length: 32, // Pubkey + }, + Seed::AccountKey { index: 2 }, + ], + false, + true, + ) + .unwrap(); + let extra_meta6 = ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: extra_meta6_literal_u64.to_le_bytes().to_vec(), + }, + Seed::AccountKey { index: 1 }, + Seed::AccountKey { index: 4 }, + ], + false, + true, + ) + .unwrap(); + + let test_ix_required_extra_accounts = account_infos + .iter() + .map(ExtraAccountMeta::from) + .collect::>(); + let test_other_ix_required_extra_accounts = [ + ExtraAccountMeta::from(&extra_meta1), + ExtraAccountMeta::from(&extra_meta2), + ExtraAccountMeta::from(&extra_meta3), + ExtraAccountMeta::from(&extra_meta4), + extra_meta5, + extra_meta6, + ]; + + let account_size = ExtraAccountMetaList::size_of(test_ix_required_extra_accounts.len()) + .unwrap() + + ExtraAccountMetaList::size_of(test_other_ix_required_extra_accounts.len()).unwrap(); let mut buffer = vec![0; account_size]; - ExtraAccountMetas::init_with_account_infos::(&mut buffer, &account_infos) - .unwrap(); + ExtraAccountMetaList::init::( + &mut buffer, + &test_ix_required_extra_accounts, + ) + .unwrap(); + ExtraAccountMetaList::init::( + &mut buffer, + &test_other_ix_required_extra_accounts, + ) + .unwrap(); + + let mock_rpc = MockRpc::setup(&account_infos); - // make an instruction to check later let program_id = Pubkey::new_unique(); let mut instruction = Instruction::new_with_bytes(program_id, &[], vec![]); - ExtraAccountMetas::add_to_instruction::(&mut instruction, &buffer) - .unwrap(); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); - // mess around with the account infos to make it harder - let mut messed_account_infos = account_infos.to_vec(); - let pubkey4 = Pubkey::new_unique(); - let mut lamports4 = 0; - let mut data4 = []; - messed_account_infos.push(AccountInfo::new( - &pubkey4, - false, - true, - &mut lamports4, - &mut data4, - &owner, - false, - Epoch::default(), - )); - let pubkey5 = Pubkey::new_unique(); - let mut lamports5 = 0; - let mut data5 = []; - messed_account_infos.push(AccountInfo::new( - &pubkey5, - false, - true, - &mut lamports5, - &mut data5, - &owner, - false, - Epoch::default(), - )); + let test_ix_check_metas = account_infos + .iter() + .map(account_info_to_meta) + .collect::>(); + assert_eq!(instruction.accounts, test_ix_check_metas,); + + let program_id = Pubkey::new_unique(); + let instruction_u8array_arg = [1, 2, 3, 4, 5, 6, 7, 8]; + let instruction_pubkey_arg = Pubkey::new_unique(); + let mut instruction_data = vec![0]; + instruction_data.extend_from_slice(&instruction_u8array_arg); + instruction_data.extend_from_slice(instruction_pubkey_arg.as_ref()); + let mut instruction = Instruction::new_with_bytes(program_id, &instruction_data, vec![]); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + let check_extra_meta5_pubkey = Pubkey::find_program_address( + &[ + extra_meta5_literal_str.as_bytes(), + &instruction_u8array_arg, + instruction_pubkey_arg.as_ref(), + extra_meta3.pubkey.as_ref(), + ], + &program_id, + ) + .0; + + let check_extra_meta6_pubkey = Pubkey::find_program_address( + &[ + extra_meta6_literal_u64.to_le_bytes().as_ref(), + extra_meta2.pubkey.as_ref(), + check_extra_meta5_pubkey.as_ref(), // The first PDA should be at index 4 + ], + &program_id, + ) + .0; + + let test_other_ix_check_metas = vec![ + extra_meta1, + extra_meta2, + extra_meta3, + extra_meta4, + AccountMeta::new(check_extra_meta5_pubkey, false), + AccountMeta::new(check_extra_meta6_pubkey, false), + ]; + + assert_eq!( + instruction.accounts.get(4).unwrap().pubkey, + check_extra_meta5_pubkey, + ); + assert_eq!( + instruction.accounts.get(5).unwrap().pubkey, + check_extra_meta6_pubkey, + ); + assert_eq!(instruction.accounts, test_other_ix_check_metas,); + } + + #[tokio::test] + async fn cpi_instruction() { + // Say we have a program that CPIs to another program. + // + // Say that _other_ program will need extra account infos. + + // This will be our program + let program_id = Pubkey::new_unique(); + let owner = Pubkey::new_unique(); + + // Some seeds used by the program for PDAs + let required_pda1_literal_string = "required_pda1"; + let required_pda2_literal_u32 = 4u32; + + // Define instruction data + // - 0: u8 + // - 1-8: [u8; 8] + // - 9-16: u64 + let instruction_u8array_arg = [1, 2, 3, 4, 5, 6, 7, 8]; + let instruction_u64_arg = 208u64; + let mut instruction_data = vec![0]; + instruction_data.extend_from_slice(&instruction_u8array_arg); + instruction_data.extend_from_slice(instruction_u64_arg.to_le_bytes().as_ref()); + + // Define known instruction accounts + let ix_accounts = vec![ + AccountMeta::new(Pubkey::new_unique(), false), + AccountMeta::new(Pubkey::new_unique(), false), + ]; + + // Define extra account metas required by the program we will CPI to + let extra_meta1 = AccountMeta::new(Pubkey::new_unique(), false); + let extra_meta2 = AccountMeta::new(Pubkey::new_unique(), true); + let extra_meta3 = AccountMeta::new_readonly(Pubkey::new_unique(), false); + let required_accounts = [ + ExtraAccountMeta::from(&extra_meta1), + ExtraAccountMeta::from(&extra_meta2), + ExtraAccountMeta::from(&extra_meta3), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: required_pda1_literal_string.as_bytes().to_vec(), + }, + Seed::InstructionData { + index: 1, + length: 8, // [u8; 8] + }, + Seed::AccountKey { index: 1 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: required_pda2_literal_u32.to_le_bytes().to_vec(), + }, + Seed::InstructionData { + index: 9, + length: 8, // u64 + }, + Seed::AccountKey { index: 5 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::InstructionData { + index: 0, + length: 1, // u8 + }, + Seed::AccountData { + account_index: 2, + data_index: 0, + length: 8, + }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::AccountData { + account_index: 5, + data_index: 4, + length: 4, + }, // This one is a PDA! + ], + false, + true, + ) + .unwrap(), + ]; + + // Now here we're going to build the list of account infos + // We'll need to include: + // - The instruction account infos for the program to CPI to + // - The extra account infos for the program to CPI to + // - Some other arbitrary account infos our program may use + + // First we need to manually derive each PDA + let check_required_pda1_pubkey = Pubkey::find_program_address( + &[ + required_pda1_literal_string.as_bytes(), + &instruction_u8array_arg, + ix_accounts.get(1).unwrap().pubkey.as_ref(), // The second account + ], + &program_id, + ) + .0; + let check_required_pda2_pubkey = Pubkey::find_program_address( + &[ + required_pda2_literal_u32.to_le_bytes().as_ref(), + instruction_u64_arg.to_le_bytes().as_ref(), + check_required_pda1_pubkey.as_ref(), // The first PDA should be at index 5 + ], + &program_id, + ) + .0; + let check_required_pda3_pubkey = Pubkey::find_program_address( + &[ + &[0], // Instruction "discriminator" (u8) + &[8; 8], // The first 8 bytes of the data for account at index 2 (extra account 1) + ], + &program_id, + ) + .0; + let check_required_pda4_pubkey = Pubkey::find_program_address( + &[ + &[7; 4], /* 4 bytes starting at index 4 of the data for account at index 5 (extra + * pda 1) */ + ], + &program_id, + ) + .0; + + // The instruction account infos for the program to CPI to + let pubkey_ix_1 = ix_accounts.get(0).unwrap().pubkey; + let mut lamports_ix_1 = 0; + let mut data_ix_1 = []; + let pubkey_ix_2 = ix_accounts.get(1).unwrap().pubkey; + let mut lamports_ix_2 = 0; + let mut data_ix_2 = []; + + // The extra account infos for the program to CPI to + let mut lamports1 = 0; + let mut data1 = [8; 12]; + let mut lamports2 = 0; + let mut data2 = []; + let mut lamports3 = 0; + let mut data3 = []; + let mut lamports_pda1 = 0; + let mut data_pda1 = [7; 12]; + let mut lamports_pda2 = 0; + let mut data_pda2 = []; + let mut lamports_pda3 = 0; + let mut data_pda3 = []; + let mut lamports_pda4 = 0; + let mut data_pda4 = []; + + // Some other arbitrary account infos our program may use + let pubkey_arb_1 = Pubkey::new_unique(); + let mut lamports_arb_1 = 0; + let mut data_arb_1 = []; + let pubkey_arb_2 = Pubkey::new_unique(); + let mut lamports_arb_2 = 0; + let mut data_arb_2 = []; + + let all_account_infos = [ + AccountInfo::new( + &pubkey_ix_1, + ix_accounts.get(0).unwrap().is_signer, + ix_accounts.get(0).unwrap().is_writable, + &mut lamports_ix_1, + &mut data_ix_1, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &pubkey_ix_2, + ix_accounts.get(1).unwrap().is_signer, + ix_accounts.get(1).unwrap().is_writable, + &mut lamports_ix_2, + &mut data_ix_2, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &extra_meta1.pubkey, + required_accounts.get(0).unwrap().is_signer.into(), + required_accounts.get(0).unwrap().is_writable.into(), + &mut lamports1, + &mut data1, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &extra_meta2.pubkey, + required_accounts.get(1).unwrap().is_signer.into(), + required_accounts.get(1).unwrap().is_writable.into(), + &mut lamports2, + &mut data2, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &extra_meta3.pubkey, + required_accounts.get(2).unwrap().is_signer.into(), + required_accounts.get(2).unwrap().is_writable.into(), + &mut lamports3, + &mut data3, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &check_required_pda1_pubkey, + required_accounts.get(3).unwrap().is_signer.into(), + required_accounts.get(3).unwrap().is_writable.into(), + &mut lamports_pda1, + &mut data_pda1, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &check_required_pda2_pubkey, + required_accounts.get(4).unwrap().is_signer.into(), + required_accounts.get(4).unwrap().is_writable.into(), + &mut lamports_pda2, + &mut data_pda2, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &check_required_pda3_pubkey, + required_accounts.get(5).unwrap().is_signer.into(), + required_accounts.get(5).unwrap().is_writable.into(), + &mut lamports_pda3, + &mut data_pda3, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &check_required_pda4_pubkey, + required_accounts.get(6).unwrap().is_signer.into(), + required_accounts.get(6).unwrap().is_writable.into(), + &mut lamports_pda4, + &mut data_pda4, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &pubkey_arb_1, + false, + true, + &mut lamports_arb_1, + &mut data_arb_1, + &owner, + false, + Epoch::default(), + ), + AccountInfo::new( + &pubkey_arb_2, + false, + true, + &mut lamports_arb_2, + &mut data_arb_2, + &owner, + false, + Epoch::default(), + ), + ]; + + // Let's use a mock RPC and set up a test instruction to check the CPI + // instruction against later + let rpc_account_infos = all_account_infos.clone(); + let mock_rpc = MockRpc::setup(&rpc_account_infos); + + let account_size = ExtraAccountMetaList::size_of(required_accounts.len()).unwrap(); + let mut buffer = vec![0; account_size]; + ExtraAccountMetaList::init::(&mut buffer, &required_accounts).unwrap(); + + let mut instruction = + Instruction::new_with_bytes(program_id, &instruction_data, ix_accounts.clone()); + ExtraAccountMetaList::add_to_instruction::( + &mut instruction, + |pubkey| mock_rpc.get_account_data(pubkey), + &buffer, + ) + .await + .unwrap(); + + // Perform the account resolution for the CPI instruction + + // Create the instruction itself + let mut cpi_instruction = + Instruction::new_with_bytes(program_id, &instruction_data, ix_accounts); + + // Start with the known account infos + let mut cpi_account_infos = + vec![all_account_infos[0].clone(), all_account_infos[1].clone()]; + + // Mess up the ordering of the account infos to make it harder! + let mut messed_account_infos = all_account_infos.clone(); messed_account_infos.swap(0, 4); messed_account_infos.swap(1, 2); + messed_account_infos.swap(3, 4); + messed_account_infos.swap(5, 6); + messed_account_infos.swap(8, 7); - let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[], vec![]); - let mut cpi_account_infos = vec![]; - ExtraAccountMetas::add_to_cpi_instruction::( + // Resolve the rest! + ExtraAccountMetaList::add_to_cpi_instruction::( &mut cpi_instruction, &mut cpi_account_infos, &buffer, @@ -502,12 +1273,164 @@ mod tests { ) .unwrap(); + // Our CPI instruction should match the check instruction. assert_eq!(cpi_instruction, instruction); - assert_eq!(cpi_account_infos.len(), account_infos.len()); - for (a, b) in std::iter::zip(cpi_account_infos, account_infos) { + + // CPI account infos should have the instruction account infos + // and the extra required account infos from the validation account, + // and they should be in the correct order. + // Note: The two additional arbitrary account infos for the currently + // executing program won't be present in the CPI instruction's account + // infos, so we will omit them (hence the `..9`). + let check_account_infos = &all_account_infos[..9]; + assert_eq!(cpi_account_infos.len(), check_account_infos.len()); + for (a, b) in std::iter::zip(cpi_account_infos, check_account_infos) { assert_eq!(a.key, b.key); assert_eq!(a.is_signer, b.is_signer); assert_eq!(a.is_writable, b.is_writable); } } + + #[test] + fn check_account_infos_test() { + let program_id = Pubkey::new_unique(); + let owner = Pubkey::new_unique(); + + // Create a list of required account metas + let pubkey1 = Pubkey::new_unique(); + let pubkey2 = Pubkey::new_unique(); + let required_accounts = [ + ExtraAccountMeta::new_with_pubkey(&pubkey1, false, true).unwrap(), + ExtraAccountMeta::new_with_pubkey(&pubkey2, false, false).unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: b"lit_seed".to_vec(), + }, + Seed::InstructionData { + index: 0, + length: 4, + }, + Seed::AccountKey { index: 0 }, + ], + false, + true, + ) + .unwrap(), + ]; + + // Create the validation data + let account_size = ExtraAccountMetaList::size_of(required_accounts.len()).unwrap(); + let mut buffer = vec![0; account_size]; + ExtraAccountMetaList::init::(&mut buffer, &required_accounts).unwrap(); + + // Create the instruction data + let instruction_data = vec![0, 1, 2, 3, 4, 5, 6, 7]; + + // Set up a list of the required accounts as account infos, + // with two instruction accounts + let pubkey_ix_1 = Pubkey::new_unique(); + let mut lamports_ix_1 = 0; + let mut data_ix_1 = []; + let pubkey_ix_2 = Pubkey::new_unique(); + let mut lamports_ix_2 = 0; + let mut data_ix_2 = []; + let mut lamports1 = 0; + let mut data1 = []; + let mut lamports2 = 0; + let mut data2 = []; + let mut lamports3 = 0; + let mut data3 = []; + let pda = Pubkey::find_program_address( + &[b"lit_seed", &instruction_data[..4], pubkey_ix_1.as_ref()], + &program_id, + ) + .0; + let account_infos = [ + // Instruction account 1 + AccountInfo::new( + &pubkey_ix_1, + false, + true, + &mut lamports_ix_1, + &mut data_ix_1, + &owner, + false, + Epoch::default(), + ), + // Instruction account 2 + AccountInfo::new( + &pubkey_ix_2, + false, + true, + &mut lamports_ix_2, + &mut data_ix_2, + &owner, + false, + Epoch::default(), + ), + // Required account 1 + AccountInfo::new( + &pubkey1, + false, + true, + &mut lamports1, + &mut data1, + &owner, + false, + Epoch::default(), + ), + // Required account 2 + AccountInfo::new( + &pubkey2, + false, + false, + &mut lamports2, + &mut data2, + &owner, + false, + Epoch::default(), + ), + // Required account 3 (PDA) + AccountInfo::new( + &pda, + false, + true, + &mut lamports3, + &mut data3, + &owner, + false, + Epoch::default(), + ), + ]; + + // Create another list of account infos to intentionally mess up + let mut messed_account_infos = account_infos.clone().to_vec(); + messed_account_infos.swap(0, 2); + messed_account_infos.swap(1, 4); + messed_account_infos.swap(3, 2); + + // Account info check should fail for the messed list + assert_eq!( + ExtraAccountMetaList::check_account_infos::( + &messed_account_infos, + &instruction_data, + &program_id, + &buffer, + ) + .unwrap_err(), + AccountResolutionError::IncorrectAccount.into(), + ); + + // Account info check should pass for the correct list + assert_eq!( + ExtraAccountMetaList::check_account_infos::( + &account_infos, + &instruction_data, + &program_id, + &buffer, + ), + Ok(()), + ); + } } diff --git a/libraries/type-length-value-derive-test/Cargo.toml b/libraries/type-length-value-derive-test/Cargo.toml new file mode 100644 index 00000000000..85c3a29df7b --- /dev/null +++ b/libraries/type-length-value-derive-test/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "spl-type-length-value-derive-test" +version = "0.1.0" +description = "Testing Derive Macro Library for SPL Type Length Value traits" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[dev-dependencies] +borsh = "0.10" +solana-program = "1.16" +spl-discriminator = { version = "0.1.0", path = "../discriminator" } +spl-type-length-value = { version = "0.3.0", path = "../type-length-value", features = ["derive"] } diff --git a/libraries/type-length-value-derive-test/src/lib.rs b/libraries/type-length-value-derive-test/src/lib.rs new file mode 100644 index 00000000000..032d1341a4f --- /dev/null +++ b/libraries/type-length-value-derive-test/src/lib.rs @@ -0,0 +1,59 @@ +//! Test crate to avoid making `borsh` a direct dependency of +//! `spl-type-length-value`. You can't use a derive macro from within the same +//! crate that the macro is defined, so we need this extra crate for just +//! testing the macro itself. + +#[cfg(test)] +pub mod test { + use { + borsh::{BorshDeserialize, BorshSerialize}, + solana_program::borsh0_10::{get_instance_packed_len, try_from_slice_unchecked}, + spl_discriminator::SplDiscriminate, + spl_type_length_value::{variable_len_pack::VariableLenPack, SplBorshVariableLenPack}, + }; + + #[derive( + Clone, + Debug, + Default, + PartialEq, + BorshDeserialize, + BorshSerialize, + SplDiscriminate, + SplBorshVariableLenPack, + )] + #[discriminator_hash_input("vehicle::my_vehicle")] + pub struct Vehicle { + vin: [u8; 8], + plate: [u8; 7], + } + + #[test] + fn test_derive() { + let vehicle = Vehicle { + vin: [0; 8], + plate: [0; 7], + }; + + assert_eq!( + get_instance_packed_len::(&vehicle).unwrap(), + vehicle.get_packed_len().unwrap() + ); + + let dst1 = &mut [0u8; 15]; + borsh::to_writer(&mut dst1[..], &vehicle).unwrap(); + + let dst2 = &mut [0u8; 15]; + vehicle.pack_into_slice(&mut dst2[..]).unwrap(); + + assert_eq!(dst1, dst2,); + + let mut buffer = [0u8; 15]; + buffer.copy_from_slice(&dst1[..]); + + assert_eq!( + try_from_slice_unchecked::(&buffer).unwrap(), + Vehicle::unpack_from_slice(&buffer).unwrap() + ); + } +} diff --git a/libraries/type-length-value/Cargo.toml b/libraries/type-length-value/Cargo.toml index ac25aaa0a72..e681838a42a 100644 --- a/libraries/type-length-value/Cargo.toml +++ b/libraries/type-length-value/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-type-length-value" -version = "0.2.0" +version = "0.3.0" description = "Solana Program Library Type-Length-Value Management" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -9,14 +9,15 @@ edition = "2021" exclude = ["js/**"] [features] -borsh = ["dep:borsh"] +derive = ["dep:spl-type-length-value-derive"] [dependencies] -borsh = { version = "0.10", optional = true } -bytemuck = { version = "1.13.1", features = ["derive"] } -solana-program = "1.16.1" -spl-discriminator = { version = "0.1.0", path = "../discriminator" } -spl-program-error = { version = "0.2.0", path = "../program-error" } +bytemuck = { version = "1.14.0", features = ["derive"] } +solana-program = "1.17.2" +spl-discriminator = { version = "0.1", path = "../discriminator" } +spl-program-error = { version = "0.3", path = "../program-error" } +spl-type-length-value-derive = { version = "0.1", path = "./derive", optional = true } +spl-pod = { version = "0.1", path = "../pod" } [lib] crate-type = ["cdylib", "lib"] diff --git a/libraries/type-length-value/README.md b/libraries/type-length-value/README.md index b5b2c4168f4..e2c769dc4f6 100644 --- a/libraries/type-length-value/README.md +++ b/libraries/type-length-value/README.md @@ -21,7 +21,7 @@ use { struct MyPodValue { data: [u8; 32], } -impl SpleDiscriminates for MyPodValue { +impl SplDiscriminate for MyPodValue { // Give it a unique discriminator, can also be generated using a hash function const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([1; ArrayDiscriminator::LENGTH]); } @@ -56,22 +56,39 @@ let mut buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // Init and write default value -let value = state.init_value::().unwrap(); +// Note: you'll need to provide a boolean whether or not to allow repeating +// values with the same TLV discriminator. +// If set to false, this function will error when an existing entry is detected. +let value = state.init_value::(false).unwrap(); // Update it in-place value.data[0] = 1; // Init and write another default value -let other_value = state.init_value::().unwrap(); -assert_eq!(other_value.data, 10); +// This time, we're going to allow repeating values. +let other_value1 = state.init_value::(true).unwrap(); +assert_eq!(other_value1.data, 10); // Update it in-place -other_value.data = 2; +other_value1.data = 2; -// Later on, to work with it again -let value = state.get_value_mut::().unwrap(); +// Let's do it again, since we can now have repeating values! +let other_value2 = state.init_value::(true).unwrap(); +assert_eq!(other_value2.data, 10); +// Update it in-place +other_value1.data = 4; + +// Later on, to work with it again, since we did _not_ allow repeating entries, +// we can just get the first value we encounter. +let value = state.get_first_value_mut::().unwrap(); // Or fetch it from an immutable buffer let state = TlvStateBorrowed::unpack(&buffer).unwrap(); -let value = state.get_value::().unwrap(); +let value1 = state.get_first_value::().unwrap(); + +// Since we used repeating entries for `MyOtherPodValue`, we can grab either one by +// its entry number +let value1 = state.get_value_with_repetition::(1).unwrap(); +let value2 = state.get_value_with_repetition::(2).unwrap(); + ``` ## Motivation @@ -109,23 +126,41 @@ If not, it reads the next 4-byte length. If the discriminator matches, it return the next `length` bytes. If not, it jumps ahead `length` bytes and reads the next 8-byte discriminator. -## Borsh integration +## Serialization of variable-length types The initial example works using the `bytemuck` crate for zero-copy serialization -and deserialization. It's possible to use Borsh by activating the `borsh` feature. +and deserialization. It's possible to use Borsh by implementing the `VariableLenPack` +trait on your type. ```rust use { borsh::{BorshDeserialize, BorshSerialize}, - spl_type_length_value::state::{TlvState, TlvStateMut}, + solana_program::borsh::{get_instance_packed_len, try_from_slice_unchecked}, + spl_type_length_value::{ + state::{TlvState, TlvStateMut}, + variable_len_pack::VariableLenPack + }, }; #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)] -struct MyBorsh { +struct MyVariableLenType { data: String, // variable length type } -impl SplDiscriminate for MyBorsh { +impl SplDiscriminate for MyVariableLenType { const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([5; ArrayDiscriminator::LENGTH]); } +impl VariableLenPack for MyVariableLenType { + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + borsh::to_writer(&mut dst[..], self).map_err(Into::into) + } + + fn unpack_from_slice(src: &[u8]) -> Result { + try_from_slice_unchecked(src).map_err(Into::into) + } + + fn get_packed_len(&self) -> Result { + get_instance_packed_len(self).map_err(Into::into) + } +} let initial_data = "This is a pretty cool test!"; // Allocate exactly the right size for the string, can go bigger if desired let tlv_size = 4 + initial_data.len(); @@ -137,11 +172,12 @@ let mut buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // No need to hold onto the bytes since we'll serialize back into the right place -let _ = state.allocate::(tlv_size).unwrap(); -let my_borsh = MyBorsh { +// For this example, let's _not_ allow repeating entries. +let _ = state.alloc::(tlv_size, false).unwrap(); +let my_variable_len = MyVariableLenType { data: initial_data.to_string() }; -state.borsh_serialize(&my_borsh).unwrap(); -let deser = state.borsh_deserialize::().unwrap(); -assert_eq!(deser, my_borsh); +state.pack_variable_len_value(&my_variable_len).unwrap(); +let deser = state.get_first_variable_len_value::().unwrap(); +assert_eq!(deser, my_variable_len); ``` diff --git a/libraries/type-length-value/derive/Cargo.toml b/libraries/type-length-value/derive/Cargo.toml new file mode 100644 index 00000000000..d0461352612 --- /dev/null +++ b/libraries/type-length-value/derive/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "spl-type-length-value-derive" +version = "0.1.0" +description = "Derive Macro Library for SPL Type Length Value traits" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } \ No newline at end of file diff --git a/libraries/type-length-value/derive/src/builder.rs b/libraries/type-length-value/derive/src/builder.rs new file mode 100644 index 00000000000..59254d41ee7 --- /dev/null +++ b/libraries/type-length-value/derive/src/builder.rs @@ -0,0 +1,91 @@ +//! The actual token generator for the macro +use { + proc_macro2::{Span, TokenStream}, + quote::{quote, ToTokens}, + syn::{parse::Parse, Generics, Ident, Item, ItemEnum, ItemStruct, WhereClause}, +}; + +pub struct SplBorshVariableLenPackBuilder { + /// The struct/enum identifier + pub ident: Ident, + /// The item's generic arguments (if any) + pub generics: Generics, + /// The item's where clause for generics (if any) + pub where_clause: Option, +} + +impl TryFrom for SplBorshVariableLenPackBuilder { + type Error = syn::Error; + + fn try_from(item_enum: ItemEnum) -> Result { + let ident = item_enum.ident; + let where_clause = item_enum.generics.where_clause.clone(); + let generics = item_enum.generics; + Ok(Self { + ident, + generics, + where_clause, + }) + } +} + +impl TryFrom for SplBorshVariableLenPackBuilder { + type Error = syn::Error; + + fn try_from(item_struct: ItemStruct) -> Result { + let ident = item_struct.ident; + let where_clause = item_struct.generics.where_clause.clone(); + let generics = item_struct.generics; + Ok(Self { + ident, + generics, + where_clause, + }) + } +} + +impl Parse for SplBorshVariableLenPackBuilder { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let item = Item::parse(input)?; + match item { + Item::Enum(item_enum) => item_enum.try_into(), + Item::Struct(item_struct) => item_struct.try_into(), + _ => { + return Err(syn::Error::new( + Span::call_site(), + "Only enums and structs are supported", + )) + } + } + .map_err(|e| syn::Error::new(input.span(), format!("Failed to parse item: {}", e))) + } +} + +impl ToTokens for SplBorshVariableLenPackBuilder { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + tokens.extend::(self.into()); + } +} + +impl From<&SplBorshVariableLenPackBuilder> for TokenStream { + fn from(builder: &SplBorshVariableLenPackBuilder) -> Self { + let ident = &builder.ident; + let generics = &builder.generics; + let where_clause = &builder.where_clause; + quote! { + impl #generics spl_type_length_value::variable_len_pack::VariableLenPack for #ident #generics #where_clause { + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), solana_program::program_error::ProgramError> { + borsh::to_writer(&mut dst[..], self).map_err(Into::into) + } + + fn unpack_from_slice(src: &[u8]) -> Result { + solana_program::borsh0_10::try_from_slice_unchecked(src).map_err(Into::into) + } + + fn get_packed_len(&self) -> Result { + solana_program::borsh0_10::get_instance_packed_len(self).map_err(Into::into) + } + } + } + } +} diff --git a/libraries/type-length-value/derive/src/lib.rs b/libraries/type-length-value/derive/src/lib.rs new file mode 100644 index 00000000000..b3f0f12a297 --- /dev/null +++ b/libraries/type-length-value/derive/src/lib.rs @@ -0,0 +1,22 @@ +//! Crate defining a derive macro for a basic borsh implementation of +//! the trait `VariableLenPack`. + +#![deny(missing_docs)] +#![cfg_attr(not(test), forbid(unsafe_code))] + +extern crate proc_macro; + +mod builder; + +use { + builder::SplBorshVariableLenPackBuilder, proc_macro::TokenStream, quote::ToTokens, + syn::parse_macro_input, +}; + +/// Derive macro to add `VariableLenPack` trait for borsh-implemented types +#[proc_macro_derive(SplBorshVariableLenPack)] +pub fn spl_borsh_variable_len_pack(input: TokenStream) -> TokenStream { + parse_macro_input!(input as SplBorshVariableLenPackBuilder) + .to_token_stream() + .into() +} diff --git a/libraries/type-length-value/js/.eslintignore b/libraries/type-length-value/js/.eslintignore new file mode 100644 index 00000000000..6da325effab --- /dev/null +++ b/libraries/type-length-value/js/.eslintignore @@ -0,0 +1,5 @@ +docs +lib +test-ledger + +package-lock.json diff --git a/libraries/type-length-value/js/.eslintrc b/libraries/type-length-value/js/.eslintrc new file mode 100644 index 00000000000..5aef10a4729 --- /dev/null +++ b/libraries/type-length-value/js/.eslintrc @@ -0,0 +1,34 @@ +{ + "root": true, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "plugin:require-extensions/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint", + "prettier", + "require-extensions" + ], + "rules": { + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/consistent-type-imports": "error" + }, + "overrides": [ + { + "files": [ + "examples/**/*", + "test/**/*" + ], + "rules": { + "require-extensions/require-extensions": "off", + "require-extensions/require-index": "off" + } + } + ] +} diff --git a/libraries/type-length-value/js/.gitignore b/libraries/type-length-value/js/.gitignore new file mode 100644 index 00000000000..21f33db819c --- /dev/null +++ b/libraries/type-length-value/js/.gitignore @@ -0,0 +1,13 @@ +.idea +.vscode +.DS_Store + +node_modules + +pnpm-lock.yaml +yarn.lock + +docs +lib +test-ledger +*.tsbuildinfo diff --git a/libraries/type-length-value/js/.mocharc.json b/libraries/type-length-value/js/.mocharc.json new file mode 100644 index 00000000000..451c14c3016 --- /dev/null +++ b/libraries/type-length-value/js/.mocharc.json @@ -0,0 +1,5 @@ +{ + "extension": ["ts"], + "node-option": ["experimental-specifier-resolution=node", "loader=ts-node/esm"], + "timeout": 5000 +} diff --git a/libraries/type-length-value/js/.prettierignore b/libraries/type-length-value/js/.prettierignore new file mode 100644 index 00000000000..6da325effab --- /dev/null +++ b/libraries/type-length-value/js/.prettierignore @@ -0,0 +1,5 @@ +docs +lib +test-ledger + +package-lock.json diff --git a/libraries/type-length-value/js/.prettierrc b/libraries/type-length-value/js/.prettierrc new file mode 100644 index 00000000000..b9ce4c1923a --- /dev/null +++ b/libraries/type-length-value/js/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": true +} \ No newline at end of file diff --git a/libraries/type-length-value/js/LICENSE b/libraries/type-length-value/js/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/libraries/type-length-value/js/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libraries/type-length-value/js/README.md b/libraries/type-length-value/js/README.md new file mode 100644 index 00000000000..69db493a984 --- /dev/null +++ b/libraries/type-length-value/js/README.md @@ -0,0 +1,18 @@ +# Type-Length-Value-js + +Library with utilities for working with Type-Length-Value structures in js. + +## Example usage + +```ts +import { TlvState, SplDiscriminator } from '@solana/spl-type-length-value'; + +const tlv = new TlvState(tlvData, discriminatorSize, lengthSize); +const discriminator = splDiscriminate("", discriminatorSize); + +const firstValue = tlv.firstBytes(discriminator); + +const allValues = tlv.bytesRepeating(discriminator); + +const firstThreeValues = tlv.bytesRepeating(discriminator, 3); +``` diff --git a/libraries/type-length-value/js/package-lock.json b/libraries/type-length-value/js/package-lock.json new file mode 100644 index 00000000000..421f470ec05 --- /dev/null +++ b/libraries/type-length-value/js/package-lock.json @@ -0,0 +1,5234 @@ +{ + "name": "@solana/spl-type-length-value", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@solana/spl-type-length-value", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "buffer": "^6.0.3" + }, + "devDependencies": { + "@types/chai": "^4.3.9", + "@types/mocha": "^10.0.2", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-require-extensions": "^0.1.1", + "gh-pages": "^6.0.0", + "mocha": "^10.1.0", + "prettier": "^3.0.0", + "shx": "^0.3.4", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", + "dev": true, + "peer": true + }, + "node_modules/@types/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", + "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/type-utils": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", + "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", + "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer": { + "version": "6.0.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "dev": true, + "license": "WTFPL", + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-require-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-require-extensions/-/eslint-plugin-require-extensions-0.1.3.tgz", + "integrity": "sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gh-pages": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "dev": true, + "dependencies": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/interpret": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shx": { + "version": "0.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "dev": true + }, + "@types/chai": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "dev": true + }, + "@types/mocha": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", + "dev": true + }, + "@types/node": { + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", + "dev": true, + "peer": true + }, + "@types/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", + "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/type-utils": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/parser": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", + "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", + "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" + } + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "dev": true + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "dev": true + }, + "base64-js": { + "version": "1.5.1" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, + "brace-expansion": { + "version": "1.1.11", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "dev": true + }, + "buffer": { + "version": "6.0.3", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "dev": true + }, + "chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "chokidar": { + "version": "3.5.3", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "dev": true + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "dev": true + }, + "eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + } + }, + "eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + } + }, + "eslint-plugin-require-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-require-extensions/-/eslint-plugin-require-extensions-0.1.3.tgz", + "integrity": "sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "dev": true + }, + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "dev": true + }, + "fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "filename-reserved-regex": { + "version": "2.0.0", + "dev": true + }, + "filenamify": { + "version": "4.3.0", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "gh-pages": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "dev": true, + "requires": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "dev": true + } + } + }, + "glob": { + "version": "7.2.0", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "he": { + "version": "1.2.0", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "dev": true + }, + "ieee754": { + "version": "1.2.1" + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "dev": true + }, + "interpret": { + "version": "1.4.0", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.10.0", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, + "isexe": { + "version": "2.0.0", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "dev": true + }, + "once": { + "version": "1.4.0", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "require-directory": { + "version": "2.1.1", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "dev": true + }, + "shelljs": { + "version": "0.8.5", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shx": { + "version": "0.3.4", + "dev": true, + "requires": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + } + }, + "signal-exit": { + "version": "3.0.7", + "dev": true + }, + "slash": { + "version": "3.0.0", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "dev": true + }, + "strip-outer": { + "version": "1.0.1", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "text-table": { + "version": "0.2.0", + "dev": true + }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "trim-repeated": { + "version": "1.0.0", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + } + } + }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, + "ts-node": { + "version": "10.9.1", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "dev": true + } + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true + }, + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yn": { + "version": "3.1.1", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "dev": true + } + } +} diff --git a/libraries/type-length-value/js/package.json b/libraries/type-length-value/js/package.json new file mode 100644 index 00000000000..b7fdf17c97c --- /dev/null +++ b/libraries/type-length-value/js/package.json @@ -0,0 +1,64 @@ +{ + "name": "@solana/spl-type-length-value", + "description": "SPL Type Length Value Library", + "version": "0.1.0", + "author": "Solana Labs Maintainers ", + "repository": "https://github.com/solana-labs/solana-program-library", + "license": "Apache-2.0", + "type": "module", + "sideEffects": false, + "engines": { + "node": ">=16" + }, + "files": [ + "lib", + "src", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "main": "./lib/cjs/index.js", + "module": "./lib/esm/index.js", + "types": "./lib/types/index.d.ts", + "exports": { + "types": "./lib/types/index.d.ts", + "require": "./lib/cjs/index.js", + "import": "./lib/esm/index.js" + }, + "scripts": { + "nuke": "shx rm -rf node_modules package-lock.json || true", + "reinstall": "npm run nuke && npm install", + "clean": "shx rm -rf lib **/*.tsbuildinfo || true", + "build": "tsc --build --verbose tsconfig.all.json", + "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "watch": "tsc --build --verbose --watch tsconfig.all.json", + "release": "npm run clean && npm run build", + "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", + "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", + "lint:fix": "npm run fmt && eslint --fix .", + "test": "mocha test" + }, + "dependencies": { + "buffer": "^6.0.3" + }, + "devDependencies": { + "@types/chai": "^4.3.9", + "@types/mocha": "^10.0.2", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-require-extensions": "^0.1.1", + "gh-pages": "^6.0.0", + "mocha": "^10.1.0", + "prettier": "^3.0.0", + "shx": "^0.3.4", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + } +} diff --git a/libraries/type-length-value/js/src/errors.ts b/libraries/type-length-value/js/src/errors.ts new file mode 100644 index 00000000000..1b128cb8b92 --- /dev/null +++ b/libraries/type-length-value/js/src/errors.ts @@ -0,0 +1,11 @@ +/** Base class for errors */ +export abstract class TlvError extends Error { + constructor(message?: string) { + super(message); + } +} + +/** Thrown if the byte length of an tlv buffer doesn't match the expected size */ +export class TlvInvalidAccountDataError extends TlvError { + name = 'TlvInvalidAccountDataError'; +} diff --git a/libraries/type-length-value/js/src/index.ts b/libraries/type-length-value/js/src/index.ts new file mode 100644 index 00000000000..68afecb9af3 --- /dev/null +++ b/libraries/type-length-value/js/src/index.ts @@ -0,0 +1,3 @@ +export * from './splDiscriminate.js'; +export * from './tlvState.js'; +export * from './errors.js'; diff --git a/libraries/type-length-value/js/src/splDiscriminate.ts b/libraries/type-length-value/js/src/splDiscriminate.ts new file mode 100644 index 00000000000..8590503504a --- /dev/null +++ b/libraries/type-length-value/js/src/splDiscriminate.ts @@ -0,0 +1,6 @@ +import { createHash } from 'crypto'; + +export const splDiscriminate = (discriminator: string, length = 8): Buffer => { + const digest = createHash('sha256').update(discriminator).digest(); + return digest.subarray(0, length); +}; diff --git a/libraries/type-length-value/js/src/tlvState.ts b/libraries/type-length-value/js/src/tlvState.ts new file mode 100644 index 00000000000..816a242843d --- /dev/null +++ b/libraries/type-length-value/js/src/tlvState.ts @@ -0,0 +1,109 @@ +import { TlvInvalidAccountDataError } from './errors.js'; + +export type LengthSize = 1 | 2 | 4 | 8; + +export type Discriminator = Uint8Array; + +export class TlvState { + private readonly tlvData: Buffer; + private readonly discriminatorSize: number; + private readonly lengthSize: LengthSize; + + public constructor(buffer: Buffer, discriminatorSize = 2, lengthSize: LengthSize = 2, offset: number = 0) { + this.tlvData = buffer.subarray(offset); + this.discriminatorSize = discriminatorSize; + this.lengthSize = lengthSize; + } + + /** + * Get the raw tlv data + * + * @return the raw tlv data + */ + public get data(): Buffer { + return this.tlvData; + } + + private readEntryLength(size: LengthSize, offset: number, constructor: (x: number | bigint) => T): T { + switch (size) { + case 1: + return constructor(this.tlvData.readUInt8(offset)); + case 2: + return constructor(this.tlvData.readUInt16LE(offset)); + case 4: + return constructor(this.tlvData.readUInt32LE(offset)); + case 8: + return constructor(this.tlvData.readBigUInt64LE(offset)); + } + } + + /** + * Get a single entry from the tlv data. This function returns the first entry with the given type. + * + * @param type the type of the entry to get + * + * @return the entry from the tlv data or null + */ + public firstBytes(discriminator: Discriminator): Buffer | null { + const entries = this.bytesRepeating(discriminator, 1); + return entries.length > 0 ? entries[0] : null; + } + + /** + * Get a multiple entries from the tlv data. This function returns `count` or less entries with the given type. + * + * @param type the type of the entry to get + * @param count the number of entries to get (0 for all entries) + * + * @return the entry from the tlv data or null + */ + public bytesRepeating(discriminator: Discriminator, count = 0): Buffer[] { + const entries: Buffer[] = []; + let offset = 0; + while (offset < this.tlvData.length) { + if (offset + this.discriminatorSize + this.lengthSize > this.tlvData.length) { + throw new TlvInvalidAccountDataError(); + } + const type = this.tlvData.subarray(offset, offset + this.discriminatorSize); + offset += this.discriminatorSize; + const entryLength = this.readEntryLength(this.lengthSize, offset, Number); + offset += this.lengthSize; + if (offset + entryLength > this.tlvData.length) { + throw new TlvInvalidAccountDataError(); + } + if (type.equals(discriminator)) { + entries.push(this.tlvData.subarray(offset, offset + entryLength)); + } + if (count > 0 && entries.length >= count) { + break; + } + offset += entryLength; + } + return entries; + } + + /** + * Get all the discriminators from the tlv data. This function will return a type multiple times if it occurs multiple times in the tlv data. + * + * @return a list of the discriminators. + */ + public discriminators(): Buffer[] { + const discriminators: Buffer[] = []; + let offset = 0; + while (offset < this.tlvData.length) { + if (offset + this.discriminatorSize + this.lengthSize > this.tlvData.length) { + throw new TlvInvalidAccountDataError(); + } + const type = this.tlvData.subarray(offset, offset + this.discriminatorSize); + discriminators.push(type); + offset += this.discriminatorSize; + const entryLength = this.readEntryLength(this.lengthSize, offset, Number); + offset += this.lengthSize; + if (offset + entryLength > this.tlvData.length) { + throw new TlvInvalidAccountDataError(); + } + offset += entryLength; + } + return discriminators; + } +} diff --git a/libraries/type-length-value/js/test/splDiscriminate.test.ts b/libraries/type-length-value/js/test/splDiscriminate.test.ts new file mode 100644 index 00000000000..0dd8616ddff --- /dev/null +++ b/libraries/type-length-value/js/test/splDiscriminate.test.ts @@ -0,0 +1,37 @@ +import { expect } from 'chai'; +import { splDiscriminate } from '../src/splDiscriminate'; +import { createHash } from 'crypto'; + +describe('splDiscrimintor', () => { + const testVectors = [ + 'hello', + 'this-is-a-test', + 'test-namespace:this-is-a-test', + 'test-namespace:this-is-a-test:with-a-longer-name', + ]; + + const testExpectedBytes = testVectors.map((x) => { + return createHash('sha256').update(x).digest(); + }); + + const testSplDiscriminator = (length: number) => { + for (let i = 0; i < testVectors.length; i++) { + const discriminator = splDiscriminate(testVectors[i], length); + const expectedBytes = testExpectedBytes[i].subarray(0, length); + expect(discriminator).to.have.length(length); + expect(discriminator).to.deep.equal(expectedBytes); + } + }; + + it('should produce the expected bytes', () => { + testSplDiscriminator(8); + testSplDiscriminator(4); + testSplDiscriminator(2); + }); + + it('should produce the same bytes as rust library', () => { + const expectedBytes = Buffer.from([105, 37, 101, 197, 75, 251, 102, 26]); + const discriminator = splDiscriminate('spl-transfer-hook-interface:execute'); + expect(discriminator).to.deep.equal(expectedBytes); + }); +}); diff --git a/libraries/type-length-value/js/test/tlvData.test.ts b/libraries/type-length-value/js/test/tlvData.test.ts new file mode 100644 index 00000000000..2ac06aff7e9 --- /dev/null +++ b/libraries/type-length-value/js/test/tlvData.test.ts @@ -0,0 +1,147 @@ +import type { LengthSize } from '../src/tlvState'; +import { TlvState } from '../src/tlvState'; +import { expect } from 'chai'; + +describe('tlvData', () => { + // typeLength 1, lengthSize 2 + const tlvData1 = Buffer.concat([ + Buffer.from([0]), + Buffer.from([0, 0]), + Buffer.from([]), + Buffer.from([1]), + Buffer.from([1, 0]), + Buffer.from([1]), + Buffer.from([2]), + Buffer.from([2, 0]), + Buffer.from([1, 2]), + Buffer.from([0]), + Buffer.from([3, 0]), + Buffer.from([1, 2, 3]), + ]); + + // typeLength 2, lengthSize 1 + const tlvData2 = Buffer.concat([ + Buffer.from([0, 0]), + Buffer.from([0]), + Buffer.from([]), + Buffer.from([1, 0]), + Buffer.from([1]), + Buffer.from([1]), + Buffer.from([2, 0]), + Buffer.from([2]), + Buffer.from([1, 2]), + Buffer.from([0, 0]), + Buffer.from([3]), + Buffer.from([1, 2, 3]), + ]); + + // typeLength 4, lengthSize 8 + const tlvData3 = Buffer.concat([ + Buffer.from([0, 0, 0, 0]), + Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([]), + Buffer.from([1, 0, 0, 0]), + Buffer.from([1, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([1]), + Buffer.from([2, 0, 0, 0]), + Buffer.from([2, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([1, 2]), + Buffer.from([0, 0, 0, 0]), + Buffer.from([3, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([1, 2, 3]), + ]); + + // typeLength 8, lengthSize 4 + const tlvData4 = Buffer.concat([ + Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([0, 0, 0, 0]), + Buffer.from([]), + Buffer.from([1, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([1, 0, 0, 0]), + Buffer.from([1]), + Buffer.from([2, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([2, 0, 0, 0]), + Buffer.from([1, 2]), + Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), + Buffer.from([3, 0, 0, 0]), + Buffer.from([1, 2, 3]), + ]); + + const testRawData = (tlvData: Buffer, discriminatorSize: number, lengthSize: LengthSize) => { + const tlv = new TlvState(tlvData, discriminatorSize, lengthSize); + expect(tlv.data).to.be.deep.equal(tlvData); + const tlvWithOffset = new TlvState(tlvData, discriminatorSize, lengthSize, discriminatorSize + lengthSize); + expect(tlvWithOffset.data).to.be.deep.equal(tlvData.subarray(discriminatorSize + lengthSize)); + }; + + it('should get the raw tlv data', () => { + testRawData(tlvData1, 1, 2); + testRawData(tlvData2, 2, 1); + testRawData(tlvData3, 4, 8); + testRawData(tlvData4, 8, 4); + }); + + const testIndividualEntries = (tlvData: Buffer, discriminatorSize: number, lengthSize: LengthSize) => { + const tlv = new TlvState(tlvData, discriminatorSize, lengthSize); + + const type = Buffer.alloc(discriminatorSize); + type[0] = 0; + expect(tlv.firstBytes(type)).to.be.deep.equal(Buffer.from([])); + type[0] = 1; + expect(tlv.firstBytes(type)).to.be.deep.equal(Buffer.from([1])); + type[0] = 2; + expect(tlv.firstBytes(type)).to.be.deep.equal(Buffer.from([1, 2])); + type[0] = 3; + expect(tlv.firstBytes(type)).to.be.null; + }; + + it('should get the entries individually', () => { + testIndividualEntries(tlvData1, 1, 2); + testIndividualEntries(tlvData2, 2, 1); + testIndividualEntries(tlvData3, 4, 8); + testIndividualEntries(tlvData4, 8, 4); + }); + + const testRepeatingEntries = (tlvData: Buffer, discriminatorSize: number, lengthSize: LengthSize) => { + const tlv = new TlvState(tlvData, discriminatorSize, lengthSize); + + const bufferDiscriminator = tlv.bytesRepeating(Buffer.alloc(discriminatorSize)); + expect(bufferDiscriminator).to.have.length(2); + expect(bufferDiscriminator[0]).to.be.deep.equal(Buffer.from([])); + expect(bufferDiscriminator[1]).to.be.deep.equal(Buffer.from([1, 2, 3])); + + const bufferDiscriminatorWithCount = tlv.bytesRepeating(Buffer.alloc(discriminatorSize), 1); + expect(bufferDiscriminatorWithCount).to.have.length(1); + expect(bufferDiscriminatorWithCount[0]).to.be.deep.equal(Buffer.from([])); + }; + + it('should get the repeating entries', () => { + testRepeatingEntries(tlvData1, 1, 2); + testRepeatingEntries(tlvData2, 2, 1); + testRepeatingEntries(tlvData3, 4, 8); + testRepeatingEntries(tlvData4, 8, 4); + }); + + const testDiscriminators = (tlvData: Buffer, discriminatorSize: number, lengthSize: LengthSize) => { + const tlv = new TlvState(tlvData, discriminatorSize, lengthSize); + const discriminators = tlv.discriminators(); + expect(discriminators).to.have.length(4); + + const type = Buffer.alloc(discriminatorSize); + type[0] = 0; + expect(discriminators[0]).to.be.deep.equal(type); + type[0] = 1; + expect(discriminators[1]).to.be.deep.equal(type); + type[0] = 2; + expect(discriminators[2]).to.be.deep.equal(type); + type[0] = 0; + expect(discriminators[3]).to.be.deep.equal(type); + }; + + it('should get the discriminators', () => { + testDiscriminators(tlvData1, 1, 2); + testDiscriminators(tlvData2, 2, 1); + testDiscriminators(tlvData3, 4, 8); + testDiscriminators(tlvData4, 8, 4); + }); +}); diff --git a/libraries/type-length-value/js/tsconfig.all.json b/libraries/type-length-value/js/tsconfig.all.json new file mode 100644 index 00000000000..985513259e2 --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.all.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.root.json", + "references": [ + { + "path": "./tsconfig.cjs.json" + }, + { + "path": "./tsconfig.esm.json" + } + ] +} diff --git a/libraries/type-length-value/js/tsconfig.base.json b/libraries/type-length-value/js/tsconfig.base.json new file mode 100644 index 00000000000..90620c4e485 --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.base.json @@ -0,0 +1,14 @@ +{ + "include": [], + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "esModuleInterop": true, + "isolatedModules": true, + "noEmitOnError": true, + "resolveJsonModule": true, + "strict": true, + "stripInternal": true + } +} diff --git a/libraries/type-length-value/js/tsconfig.cjs.json b/libraries/type-length-value/js/tsconfig.cjs.json new file mode 100644 index 00000000000..2db9b71569e --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.cjs.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/cjs", + "target": "ES2016", + "module": "CommonJS", + "sourceMap": true + } +} diff --git a/libraries/type-length-value/js/tsconfig.esm.json b/libraries/type-length-value/js/tsconfig.esm.json new file mode 100644 index 00000000000..25e7e25e751 --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.esm.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/esm", + "declarationDir": "lib/types", + "target": "ES2020", + "module": "ES2020", + "sourceMap": true, + "declaration": true, + "declarationMap": true + } +} diff --git a/libraries/type-length-value/js/tsconfig.json b/libraries/type-length-value/js/tsconfig.json new file mode 100644 index 00000000000..2f9b239bfca --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.all.json", + "include": ["src", "test"], + "compilerOptions": { + "noEmit": true, + "skipLibCheck": true + } +} diff --git a/libraries/type-length-value/js/tsconfig.root.json b/libraries/type-length-value/js/tsconfig.root.json new file mode 100644 index 00000000000..fadf294ab43 --- /dev/null +++ b/libraries/type-length-value/js/tsconfig.root.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "composite": true + } +} diff --git a/libraries/type-length-value/src/error.rs b/libraries/type-length-value/src/error.rs index a24d63de90b..48e5d9efa34 100644 --- a/libraries/type-length-value/src/error.rs +++ b/libraries/type-length-value/src/error.rs @@ -3,7 +3,7 @@ use spl_program_error::*; /// Errors that may be returned by the Token program. -#[spl_program_error] +#[spl_program_error(hash_error_code_start = 1_202_666_432)] pub enum TlvError { /// Type not found in TLV data #[error("Type not found in TLV data")] @@ -11,13 +11,4 @@ pub enum TlvError { /// Type already exists in TLV data #[error("Type already exists in TLV data")] TypeAlreadyExists, - /// Error in checked math operation - #[error("Error in checked math operation")] - CalculationFailure, - /// Provided byte buffer too small for expected type - #[error("Provided byte buffer too small for expected type")] - BufferTooSmall, - /// Provided byte buffer too large for expected type - #[error("Provided byte buffer too large for expected type")] - BufferTooLarge, } diff --git a/libraries/type-length-value/src/length.rs b/libraries/type-length-value/src/length.rs index 6eb07e4a6da..6c2a8fc3788 100644 --- a/libraries/type-length-value/src/length.rs +++ b/libraries/type-length-value/src/length.rs @@ -1,8 +1,8 @@ //! Module for the length portion of a Type-Length-Value structure use { - crate::pod::PodU32, bytemuck::{Pod, Zeroable}, solana_program::program_error::ProgramError, + spl_pod::primitives::PodU32, }; /// Length in TLV structure diff --git a/libraries/type-length-value/src/lib.rs b/libraries/type-length-value/src/lib.rs index 2e7934baca4..5cc1869cab9 100644 --- a/libraries/type-length-value/src/lib.rs +++ b/libraries/type-length-value/src/lib.rs @@ -1,14 +1,18 @@ //! Crate defining an interface for managing type-length-value entries in a slab //! of bytes, to be used with Solana accounts. -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] pub mod error; pub mod length; -pub mod pod; pub mod state; +pub mod variable_len_pack; -// Export current sdk types for downstream users building with a different sdk version +// Export current sdk types for downstream users building with a different sdk +// version pub use solana_program; +// Expose derive macro on feature flag +#[cfg(feature = "derive")] +pub use spl_type_length_value_derive::SplBorshVariableLenPack; diff --git a/libraries/type-length-value/src/pod.rs b/libraries/type-length-value/src/pod.rs deleted file mode 100644 index acdde6b77af..00000000000 --- a/libraries/type-length-value/src/pod.rs +++ /dev/null @@ -1,179 +0,0 @@ -//! Pod types to be used with bytemuck for zero-copy serde - -use { - crate::error::TlvError, - bytemuck::{Pod, Zeroable}, - solana_program::program_error::ProgramError, -}; - -/// Convert a slice into a `Pod` (zero copy) -pub fn pod_from_bytes(bytes: &[u8]) -> Result<&T, ProgramError> { - bytemuck::try_from_bytes(bytes).map_err(|_| ProgramError::InvalidArgument) -} -/// Convert a slice into a mutable `Pod` (zero copy) -pub fn pod_from_bytes_mut(bytes: &mut [u8]) -> Result<&mut T, ProgramError> { - bytemuck::try_from_bytes_mut(bytes).map_err(|_| ProgramError::InvalidArgument) -} -/// Convert a slice into a mutable `Pod` slice (zero copy) -pub fn pod_slice_from_bytes(bytes: &[u8]) -> Result<&[T], ProgramError> { - bytemuck::try_cast_slice(bytes).map_err(|_| ProgramError::InvalidArgument) -} -/// Convert a slice into a mutable `Pod` slice (zero copy) -pub fn pod_slice_from_bytes_mut(bytes: &mut [u8]) -> Result<&mut [T], ProgramError> { - bytemuck::try_cast_slice_mut(bytes).map_err(|_| ProgramError::InvalidArgument) -} - -/// Simple macro for implementing conversion functions between Pod* ints and standard ints. -/// -/// The standard int types can cause alignment issues when placed in a `Pod`, -/// so these replacements are usable in all `Pod`s. -macro_rules! impl_int_conversion { - ($P:ty, $I:ty) => { - impl From<$I> for $P { - fn from(n: $I) -> Self { - Self(n.to_le_bytes()) - } - } - impl From<$P> for $I { - fn from(pod: $P) -> Self { - Self::from_le_bytes(pod.0) - } - } - }; -} - -/// `u32` type that can be used in `Pod`s -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU32([u8; 4]); -impl_int_conversion!(PodU32, u32); - -/// The standard `bool` is not a `Pod`, define a replacement that is -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodBool(u8); -impl From for PodBool { - fn from(b: bool) -> Self { - Self(if b { 1 } else { 0 }) - } -} -impl From<&PodBool> for bool { - fn from(b: &PodBool) -> Self { - b.0 != 0 - } -} -impl From for bool { - fn from(b: PodBool) -> Self { - b.0 != 0 - } -} - -const LENGTH_SIZE: usize = std::mem::size_of::(); -/// Special type for using a slice of `Pod`s in a zero-copy way -pub struct PodSlice<'data, T: Pod> { - length: &'data PodU32, - data: &'data [T], -} -impl<'data, T: Pod> PodSlice<'data, T> { - /// Unpack the buffer into a slice - pub fn unpack<'a>(data: &'a [u8]) -> Result - where - 'a: 'data, - { - if data.len() < LENGTH_SIZE { - return Err(TlvError::BufferTooSmall.into()); - } - let (length, data) = data.split_at(LENGTH_SIZE); - let length = pod_from_bytes::(length)?; - let _max_length = max_len_for_type::(data.len())?; - let data = pod_slice_from_bytes(data)?; - Ok(Self { length, data }) - } - - /// Get the slice data - pub fn data(&self) -> &[T] { - let length = u32::from(*self.length) as usize; - &self.data[..length] - } - - /// Get the amount of bytes used by `num_items` - pub fn size_of(num_items: usize) -> Result { - std::mem::size_of::() - .checked_mul(num_items) - .and_then(|len| len.checked_add(LENGTH_SIZE)) - .ok_or_else(|| TlvError::CalculationFailure.into()) - } -} - -/// Special type for using a slice of mutable `Pod`s in a zero-copy way -pub struct PodSliceMut<'data, T: Pod> { - length: &'data mut PodU32, - data: &'data mut [T], - max_length: usize, -} -impl<'data, T: Pod> PodSliceMut<'data, T> { - /// Unpack the mutable buffer into a mutable slice, with the option to - /// initialize the data - fn unpack_internal<'a>(data: &'a mut [u8], init: bool) -> Result - where - 'a: 'data, - { - if data.len() < LENGTH_SIZE { - return Err(TlvError::BufferTooSmall.into()); - } - let (length, data) = data.split_at_mut(LENGTH_SIZE); - let length = pod_from_bytes_mut::(length)?; - if init { - *length = 0.into(); - } - let max_length = max_len_for_type::(data.len())?; - let data = pod_slice_from_bytes_mut(data)?; - Ok(Self { - length, - data, - max_length, - }) - } - - /// Unpack the mutable buffer into a mutable slice - pub fn unpack<'a>(data: &'a mut [u8]) -> Result - where - 'a: 'data, - { - Self::unpack_internal(data, /* init */ false) - } - - /// Unpack the mutable buffer into a mutable slice, and initialize the - /// slice to 0-length - pub fn init<'a>(data: &'a mut [u8]) -> Result - where - 'a: 'data, - { - Self::unpack_internal(data, /* init */ true) - } - - /// Add another item to the slice - pub fn push(&mut self, t: T) -> Result<(), ProgramError> { - let length = u32::from(*self.length); - if length as usize == self.max_length { - Err(TlvError::BufferTooSmall.into()) - } else { - self.data[length as usize] = t; - *self.length = length.saturating_add(1).into(); - Ok(()) - } - } -} - -fn max_len_for_type(data_len: usize) -> Result { - let size: usize = std::mem::size_of::(); - let max_len = data_len - .checked_div(size) - .ok_or(TlvError::CalculationFailure)?; - // check that it isn't overallocated - if max_len.saturating_mul(size) != data_len { - Err(TlvError::BufferTooLarge.into()) - } else { - Ok(max_len) - } -} diff --git a/libraries/type-length-value/src/state.rs b/libraries/type-length-value/src/state.rs index eb4c7ed2aae..8321ce76f74 100644 --- a/libraries/type-length-value/src/state.rs +++ b/libraries/type-length-value/src/state.rs @@ -1,25 +1,23 @@ //! Type-length-value structure definition and manipulation use { - crate::{ - error::TlvError, - length::Length, - pod::{pod_from_bytes, pod_from_bytes_mut}, - }, + crate::{error::TlvError, length::Length, variable_len_pack::VariableLenPack}, bytemuck::Pod, - solana_program::program_error::ProgramError, + solana_program::{account_info::AccountInfo, program_error::ProgramError}, spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, + spl_pod::bytemuck::{pod_from_bytes, pod_from_bytes_mut}, std::{cmp::Ordering, mem::size_of}, }; /// Get the current TlvIndices from the current spot -const fn get_indices_unchecked(type_start: usize) -> TlvIndices { +const fn get_indices_unchecked(type_start: usize, value_repetition_number: usize) -> TlvIndices { let length_start = type_start.saturating_add(size_of::()); let value_start = length_start.saturating_add(size_of::()); TlvIndices { type_start, length_start, value_start, + value_repetition_number, } } @@ -30,15 +28,19 @@ struct TlvIndices { pub type_start: usize, pub length_start: usize, pub value_start: usize, + pub value_repetition_number: usize, } + fn get_indices( tlv_data: &[u8], value_discriminator: ArrayDiscriminator, init: bool, + repetition_number: Option, ) -> Result { + let mut current_repetition_number = 0; let mut start_index = 0; while start_index < tlv_data.len() { - let tlv_indices = get_indices_unchecked(start_index); + let tlv_indices = get_indices_unchecked(start_index, current_repetition_number); if tlv_data.len() < tlv_indices.value_start { return Err(ProgramError::InvalidAccountData); } @@ -46,8 +48,12 @@ fn get_indices( &tlv_data[tlv_indices.type_start..tlv_indices.length_start], )?; if discriminator == value_discriminator { - // found an instance of the extension that we're initializing, return! - return Ok(tlv_indices); + if let Some(desired_repetition_number) = repetition_number { + if current_repetition_number == desired_repetition_number { + return Ok(tlv_indices); + } + } + current_repetition_number += 1; // got to an empty spot, init here, or error if we're searching, since // nothing is written after an Uninitialized spot } else if discriminator == ArrayDiscriminator::UNINITIALIZED { @@ -56,15 +62,13 @@ fn get_indices( } else { return Err(TlvError::TypeNotFound.into()); } - } else { - let length = pod_from_bytes::( - &tlv_data[tlv_indices.length_start..tlv_indices.value_start], - )?; - let value_end_index = tlv_indices - .value_start - .saturating_add(usize::try_from(*length)?); - start_index = value_end_index; } + let length = + pod_from_bytes::(&tlv_data[tlv_indices.length_start..tlv_indices.value_start])?; + let value_end_index = tlv_indices + .value_start + .saturating_add(usize::try_from(*length)?); + start_index = value_end_index; } Err(ProgramError::InvalidAccountData) } @@ -77,7 +81,9 @@ fn get_discriminators_and_end_index( let mut discriminators = vec![]; let mut start_index = 0; while start_index < tlv_data.len() { - let tlv_indices = get_indices_unchecked(start_index); + // This function is not concerned with repetitions, so we can just + // arbitrarily pass `0` here + let tlv_indices = get_indices_unchecked(start_index, 0); if tlv_data.len() < tlv_indices.length_start { // we got to the end, but there might be some uninitialized data after let remainder = &tlv_data[tlv_indices.type_start..]; @@ -115,13 +121,23 @@ fn get_discriminators_and_end_index( Ok((discriminators, start_index)) } -fn get_bytes(tlv_data: &[u8]) -> Result<&[u8], ProgramError> { +fn get_bytes( + tlv_data: &[u8], + repetition_number: usize, +) -> Result<&[u8], ProgramError> { let TlvIndices { type_start: _, length_start, value_start, - } = get_indices(tlv_data, V::SPL_DISCRIMINATOR, false)?; - // get_indices has checked that tlv_data is long enough to include these indices + value_repetition_number: _, + } = get_indices( + tlv_data, + V::SPL_DISCRIMINATOR, + false, + Some(repetition_number), + )?; + // get_indices has checked that tlv_data is long enough to include these + // indices let length = pod_from_bytes::(&tlv_data[length_start..value_start])?; let value_end = value_start.saturating_add(usize::try_from(*length)?); if tlv_data.len() < value_end { @@ -145,8 +161,8 @@ fn get_bytes(tlv_data: &[u8]) -> Result<&[u8], ProgramError> /// For example, if we have two distinct types, one which is an 8-byte array /// of value `[0, 1, 0, 0, 0, 0, 0, 0]` and discriminator /// `[1, 1, 1, 1, 1, 1, 1, 1]`, and another which is just a single `u8` of value -/// `4` with the discriminator `[2, 2, 2, 2, 2, 2, 2, 2]`, we can deserialize this -/// buffer as follows: +/// `4` with the discriminator `[2, 2, 2, 2, 2, 2, 2, 2]`, we can deserialize +/// this buffer as follows: /// /// ``` /// use { @@ -179,9 +195,9 @@ fn get_bytes(tlv_data: &[u8]) -> Result<&[u8], ProgramError> /// 4, // second type's value /// ]; /// let state = TlvStateBorrowed::unpack(&buffer).unwrap(); -/// let value = state.get_value::().unwrap(); +/// let value = state.get_first_value::().unwrap(); /// assert_eq!(value.data, [0, 1, 0, 0, 0, 0, 0, 0]); -/// let value = state.get_value::().unwrap(); +/// let value = state.get_first_value::().unwrap(); /// assert_eq!(value.data, 4); /// ``` /// @@ -190,24 +206,51 @@ pub trait TlvState { /// Get the full buffer containing all TLV data fn get_data(&self) -> &[u8]; - /// Unpack a portion of the TLV data as the desired Pod type - fn get_value(&self) -> Result<&V, ProgramError> { - let data = get_bytes::(self.get_data())?; + /// Unpack a portion of the TLV data as the desired Pod type for the entry + /// number specified + fn get_value_with_repetition( + &self, + repetition_number: usize, + ) -> Result<&V, ProgramError> { + let data = get_bytes::(self.get_data(), repetition_number)?; pod_from_bytes::(data) } - /// Unpacks a portion of the TLV data as the desired Borsh type - #[cfg(feature = "borsh")] - fn borsh_deserialize( + /// Unpack a portion of the TLV data as the desired Pod type for the first + /// entry found + fn get_first_value(&self) -> Result<&V, ProgramError> { + self.get_value_with_repetition::(0) + } + + /// Unpacks a portion of the TLV data as the desired variable-length type + /// for the entry number specified + fn get_variable_len_value_with_repetition( &self, + repetition_number: usize, ) -> Result { - let data = get_bytes::(self.get_data())?; - solana_program::borsh::try_from_slice_unchecked::(data).map_err(Into::into) + let data = get_bytes::(self.get_data(), repetition_number)?; + V::unpack_from_slice(data) } - /// Unpack a portion of the TLV data as bytes - fn get_bytes(&self) -> Result<&[u8], ProgramError> { - get_bytes::(self.get_data()) + /// Unpacks a portion of the TLV data as the desired variable-length type + /// for the first entry found + fn get_first_variable_len_value( + &self, + ) -> Result { + self.get_variable_len_value_with_repetition::(0) + } + + /// Unpack a portion of the TLV data as bytes for the entry number specified + fn get_bytes_with_repetition( + &self, + repetition_number: usize, + ) -> Result<&[u8], ProgramError> { + get_bytes::(self.get_data(), repetition_number) + } + + /// Unpack a portion of the TLV data as bytes for the first entry found + fn get_first_bytes(&self) -> Result<&[u8], ProgramError> { + self.get_bytes_with_repetition::(0) } /// Iterates through the TLV entries, returning only the types @@ -242,7 +285,8 @@ impl TlvState for TlvStateOwned { } } -/// Encapsulates immutable base state data (mint or account) with possible extensions +/// Encapsulates immutable base state data (mint or account) with possible +/// extensions #[derive(Debug, PartialEq)] pub struct TlvStateBorrowed<'data> { /// Slice of data containing all TLV data, deserialized on demand @@ -263,7 +307,8 @@ impl<'a> TlvState for TlvStateBorrowed<'a> { } } -/// Encapsulates mutable base state data (mint or account) with possible extensions +/// Encapsulates mutable base state data (mint or account) with possible +/// extensions #[derive(Debug, PartialEq)] pub struct TlvStateMut<'data> { /// Slice of data containing all TLV data, deserialized on demand @@ -278,19 +323,41 @@ impl<'data> TlvStateMut<'data> { Ok(Self { data }) } - /// Unpack a portion of the TLV data as the desired type that allows modifying the type - pub fn get_value_mut(&mut self) -> Result<&mut V, ProgramError> { - let data = self.get_bytes_mut::()?; + /// Unpack a portion of the TLV data as the desired type that allows + /// modifying the type for the entry number specified + pub fn get_value_with_repetition_mut( + &mut self, + repetition_number: usize, + ) -> Result<&mut V, ProgramError> { + let data = self.get_bytes_with_repetition_mut::(repetition_number)?; pod_from_bytes_mut::(data) } - /// Unpack a portion of the TLV data as mutable bytes - pub fn get_bytes_mut(&mut self) -> Result<&mut [u8], ProgramError> { + /// Unpack a portion of the TLV data as the desired type that allows + /// modifying the type for the first entry found + pub fn get_first_value_mut( + &mut self, + ) -> Result<&mut V, ProgramError> { + self.get_value_with_repetition_mut::(0) + } + + /// Unpack a portion of the TLV data as mutable bytes for the entry number + /// specified + pub fn get_bytes_with_repetition_mut( + &mut self, + repetition_number: usize, + ) -> Result<&mut [u8], ProgramError> { let TlvIndices { type_start: _, length_start, value_start, - } = get_indices(self.data, V::SPL_DISCRIMINATOR, false)?; + value_repetition_number: _, + } = get_indices( + self.data, + V::SPL_DISCRIMINATOR, + false, + Some(repetition_number), + )?; let length = pod_from_bytes::(&self.data[length_start..value_start])?; let value_end = value_start.saturating_add(usize::try_from(*length)?); @@ -300,36 +367,67 @@ impl<'data> TlvStateMut<'data> { Ok(&mut self.data[value_start..value_end]) } + /// Unpack a portion of the TLV data as mutable bytes for the first entry + /// found + pub fn get_first_bytes_mut(&mut self) -> Result<&mut [u8], ProgramError> { + self.get_bytes_with_repetition_mut::(0) + } + /// Packs the default TLV data into the first open slot in the data buffer. - /// If extension is already found in the buffer, it returns an error. + /// Handles repetition based on the boolean arg provided: + /// * `true`: If extension is already found in the buffer, + /// it returns an error. + /// * `false`: Will add a new entry to the next open slot. pub fn init_value( &mut self, - ) -> Result<&mut V, ProgramError> { + allow_repetition: bool, + ) -> Result<(&mut V, usize), ProgramError> { let length = size_of::(); - let buffer = self.alloc::(length)?; + let (buffer, repetition_number) = self.alloc::(length, allow_repetition)?; let extension_ref = pod_from_bytes_mut::(buffer)?; *extension_ref = V::default(); - Ok(extension_ref) + Ok((extension_ref, repetition_number)) } - /// Packs a borsh-serializable value into its appropriate data segment. Assumes - /// that space has already been allocated for the given type - #[cfg(feature = "borsh")] - pub fn borsh_serialize( + /// Packs a variable-length value into its appropriate data segment, where + /// repeating discriminators _are_ allowed + pub fn pack_variable_len_value_with_repetition( &mut self, value: &V, + repetition_number: usize, ) -> Result<(), ProgramError> { - let data = self.get_bytes_mut::()?; - borsh::to_writer(&mut data[..], value).map_err(Into::into) + let data = self.get_bytes_with_repetition_mut::(repetition_number)?; + // NOTE: Do *not* use `pack`, since the length check will cause + // reallocations to smaller sizes to fail + value.pack_into_slice(data) + } + + /// Packs a variable-length value into its appropriate data segment, where + /// no repeating discriminators are allowed + pub fn pack_first_variable_len_value( + &mut self, + value: &V, + ) -> Result<(), ProgramError> { + self.pack_variable_len_value_with_repetition::(value, 0) } /// Allocate the given number of bytes for the given SplDiscriminate - pub fn alloc(&mut self, length: usize) -> Result<&mut [u8], ProgramError> { + pub fn alloc( + &mut self, + length: usize, + allow_repetition: bool, + ) -> Result<(&mut [u8], usize), ProgramError> { let TlvIndices { type_start, length_start, value_start, - } = get_indices(self.data, V::SPL_DISCRIMINATOR, true)?; + value_repetition_number, + } = get_indices( + self.data, + V::SPL_DISCRIMINATOR, + true, + if allow_repetition { None } else { Some(0) }, + )?; let discriminator = ArrayDiscriminator::try_from(&self.data[type_start..length_start])?; if discriminator == ArrayDiscriminator::UNINITIALIZED { @@ -345,25 +443,47 @@ impl<'data> TlvStateMut<'data> { if self.data.len() < value_end { return Err(ProgramError::InvalidAccountData); } - Ok(&mut self.data[value_start..value_end]) + Ok(( + &mut self.data[value_start..value_end], + value_repetition_number, + )) } else { Err(TlvError::TypeAlreadyExists.into()) } } - /// Reallocate the given number of bytes for the given SplDiscriminate. If the new - /// length is smaller, it will compact the rest of the buffer and zero out - /// the difference at the end. If it's larger, it will move the rest of - /// the buffer data and zero out the new data. - pub fn realloc( + /// Allocates and serializes a new TLV entry from a `VariableLenPack` type + pub fn alloc_and_pack_variable_len_entry( + &mut self, + value: &V, + allow_repetition: bool, + ) -> Result { + let length = value.get_packed_len()?; + let (data, repetition_number) = self.alloc::(length, allow_repetition)?; + value.pack_into_slice(data)?; + Ok(repetition_number) + } + + /// Reallocate the given number of bytes for the given SplDiscriminate. If + /// the new length is smaller, it will compact the rest of the buffer + /// and zero out the difference at the end. If it's larger, it will move + /// the rest of the buffer data and zero out the new data. + pub fn realloc_with_repetition( &mut self, length: usize, + repetition_number: usize, ) -> Result<&mut [u8], ProgramError> { let TlvIndices { type_start: _, length_start, value_start, - } = get_indices(self.data, V::SPL_DISCRIMINATOR, false)?; + value_repetition_number: _, + } = get_indices( + self.data, + V::SPL_DISCRIMINATOR, + false, + Some(repetition_number), + )?; let (_, end_index) = get_discriminators_and_end_index(self.data)?; let data_len = self.data.len(); @@ -401,19 +521,29 @@ impl<'data> TlvStateMut<'data> { Ok(&mut self.data[value_start..new_value_end]) } + + /// Reallocate the given number of bytes for the given SplDiscriminate, + /// where no repeating discriminators are allowed + pub fn realloc_first( + &mut self, + length: usize, + ) -> Result<&mut [u8], ProgramError> { + self.realloc_with_repetition::(length, 0) + } } + impl<'a> TlvState for TlvStateMut<'a> { fn get_data(&self) -> &[u8] { self.data } } -/// Packs a borsh-serializable value into an existing TLV space, reallocating +/// Packs a variable-length value into an existing TLV space, reallocating /// the account and TLV as needed to accommodate for any change in space -#[cfg(feature = "borsh")] -pub fn realloc_and_borsh_serialize( - account_info: &solana_program::account_info::AccountInfo, +pub fn realloc_and_pack_variable_len_with_repetition( + account_info: &AccountInfo, value: &V, + repetition_number: usize, ) -> Result<(), ProgramError> { let previous_length = { let data = account_info.try_borrow_data()?; @@ -421,10 +551,11 @@ pub fn realloc_and_borsh_serialize( type_start: _, length_start, value_start, - } = get_indices(&data, V::SPL_DISCRIMINATOR, false)?; + value_repetition_number: _, + } = get_indices(&data, V::SPL_DISCRIMINATOR, false, Some(repetition_number))?; usize::try_from(*pod_from_bytes::(&data[length_start..value_start])?)? }; - let new_length = solana_program::borsh::get_instance_packed_len(&value)?; + let new_length = value.get_packed_len()?; let previous_account_size = account_info.try_data_len()?; if previous_length < new_length { // size increased, so realloc the account, then the TLV entry, then write data @@ -434,19 +565,19 @@ pub fn realloc_and_borsh_serialize( account_info.realloc(previous_account_size.saturating_add(additional_bytes), true)?; let mut buffer = account_info.try_borrow_mut_data()?; let mut state = TlvStateMut::unpack(&mut buffer)?; - state.realloc::(new_length)?; - state.borsh_serialize(value)?; + state.realloc_with_repetition::(new_length, repetition_number)?; + state.pack_variable_len_value_with_repetition(value, repetition_number)?; } else { // do it backwards otherwise, write the state, realloc TLV, then the account let mut buffer = account_info.try_borrow_mut_data()?; let mut state = TlvStateMut::unpack(&mut buffer)?; - state.borsh_serialize(value)?; + state.pack_variable_len_value_with_repetition(value, repetition_number)?; let removed_bytes = previous_length .checked_sub(new_length) .ok_or(ProgramError::AccountDataTooSmall)?; if removed_bytes > 0 { // we decreased the size, so need to realloc the TLV, then the account - state.realloc::(new_length)?; + state.realloc_with_repetition::(new_length, repetition_number)?; // this is probably fine, but be safe and avoid invalidating references drop(buffer); account_info.realloc(previous_account_size.saturating_sub(removed_bytes), false)?; @@ -455,10 +586,18 @@ pub fn realloc_and_borsh_serialize( Ok(()) } +/// Packs a variable-length value into an existing TLV space, where no repeating +/// discriminators are allowed +pub fn realloc_and_pack_first_variable_len( + account_info: &AccountInfo, + value: &V, +) -> Result<(), ProgramError> { + realloc_and_pack_variable_len_with_repetition::(account_info, value, 0) +} + /// Get the base size required for TLV data const fn get_base_len() -> usize { - let indices = get_indices_unchecked(0); - indices.value_start + get_indices_unchecked(0, 0).value_start } fn check_data(tlv_data: &[u8]) -> Result<(), ProgramError> { @@ -469,8 +608,10 @@ fn check_data(tlv_data: &[u8]) -> Result<(), ProgramError> { #[cfg(test)] mod test { - use super::*; - use bytemuck::{Pod, Zeroable}; + use { + super::*, + bytemuck::{Pod, Zeroable}, + }; const TEST_BUFFER: &[u8] = &[ 1, 1, 1, 1, 1, 1, 1, 1, // discriminator @@ -538,19 +679,19 @@ mod test { #[test] fn unpack_opaque_buffer() { let state = TlvStateBorrowed::unpack(TEST_BUFFER).unwrap(); - let value = state.get_value::().unwrap(); + let value = state.get_first_value::().unwrap(); assert_eq!(value.data, [1; 32]); assert_eq!( - state.get_value::(), + state.get_first_value::(), Err(ProgramError::InvalidAccountData) ); let mut test_buffer = TEST_BUFFER.to_vec(); let state = TlvStateMut::unpack(&mut test_buffer).unwrap(); - let value = state.get_value::().unwrap(); + let value = state.get_first_value::().unwrap(); assert_eq!(value.data, [1; 32]); let state = TlvStateOwned::unpack(test_buffer).unwrap(); - let value = state.get_value::().unwrap(); + let value = state.get_first_value::().unwrap(); assert_eq!(value.data, [1; 32]); } @@ -576,7 +717,7 @@ mod test { buffer[0] += 1; let state = TlvStateMut::unpack(&mut buffer).unwrap(); assert_eq!( - state.get_value::(), + state.get_first_value::(), Err(ProgramError::InvalidAccountData) ); @@ -593,7 +734,7 @@ mod test { buffer[ArrayDiscriminator::LENGTH] -= 1; let state = TlvStateMut::unpack(&mut buffer).unwrap(); assert_eq!( - state.get_value::(), + state.get_first_value::(), Err(ProgramError::InvalidArgument) ); @@ -633,18 +774,18 @@ mod test { let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // success init and write value - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; let data = [100; 32]; value.data = data; assert_eq!( &state.get_discriminators().unwrap(), &[TestValue::SPL_DISCRIMINATOR], ); - assert_eq!(&state.get_value::().unwrap().data, &data,); + assert_eq!(&state.get_first_value::().unwrap().data, &data,); // fail init extension when already initialized assert_eq!( - state.init_value::().unwrap_err(), + state.init_value::(false).unwrap_err(), TlvError::TypeAlreadyExists.into(), ); @@ -660,7 +801,7 @@ mod test { // check unpacking let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); - let mut unpacked = state.get_value_mut::().unwrap(); + let unpacked = state.get_first_value_mut::().unwrap(); assert_eq!(*unpacked, TestValue { data }); // update extension @@ -669,7 +810,7 @@ mod test { // check updates are propagated let state = TlvStateBorrowed::unpack(&buffer).unwrap(); - let unpacked = state.get_value::().unwrap(); + let unpacked = state.get_first_value::().unwrap(); assert_eq!(*unpacked, TestValue { data: new_data }); // check raw buffer @@ -684,7 +825,7 @@ mod test { let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // init one more value - let new_value = state.init_value::().unwrap(); + let new_value = state.init_value::(false).unwrap().0; let small_data = [102; 3]; new_value.data = small_data; @@ -713,7 +854,7 @@ mod test { // fail to init one more extension that does not fit let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); assert_eq!( - state.init_value::(), + state.init_value::(false), Err(ProgramError::InvalidAccountData), ); } @@ -730,9 +871,9 @@ mod test { let small_data = [98; 3]; // write values - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; value.data = data; - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; value.data = small_data; assert_eq!( @@ -747,9 +888,9 @@ mod test { let mut other_buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut other_buffer).unwrap(); - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; value.data = small_data; - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; value.data = data; assert_eq!( @@ -767,12 +908,12 @@ mod test { // BUT values are the same assert_eq!( - state.get_value::().unwrap(), - other_state.get_value::().unwrap() + state.get_first_value::().unwrap(), + other_state.get_first_value::().unwrap() ); assert_eq!( - state.get_value::().unwrap(), - other_state.get_value::().unwrap() + state.get_first_value::().unwrap(), + other_state.get_first_value::().unwrap() ); } @@ -781,7 +922,7 @@ mod test { let account_size = get_base_len() + size_of::(); let mut buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); - let value = state.init_value::().unwrap(); + let value = state.init_value::(false).unwrap().0; assert_eq!(value.data, TEST_NON_ZERO_DEFAULT_DATA); } @@ -790,14 +931,14 @@ mod test { let account_size = get_base_len() + size_of::(); let mut buffer = vec![0; account_size - 1]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); - let err = state.init_value::().unwrap_err(); + let err = state.init_value::(false).unwrap_err(); assert_eq!(err, ProgramError::InvalidAccountData); // hack the buffer to look like it was initialized, still fails let discriminator_ref = &mut state.data[0..ArrayDiscriminator::LENGTH]; discriminator_ref.copy_from_slice(TestValue::SPL_DISCRIMINATOR.as_ref()); state.data[ArrayDiscriminator::LENGTH] = 32; - let err = state.get_value::().unwrap_err(); + let err = state.get_first_value::().unwrap_err(); assert_eq!(err, ProgramError::InvalidAccountData); assert_eq!( state.get_discriminators().unwrap_err(), @@ -812,29 +953,29 @@ mod test { let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); assert_eq!( - state.get_value::().unwrap_err(), + state.get_first_value::().unwrap_err(), TlvError::TypeNotFound.into(), ); - state.init_value::().unwrap(); - state.get_value::().unwrap(); + state.init_value::(false).unwrap(); + state.get_first_value::().unwrap(); // re-init fails assert_eq!( - state.init_value::().unwrap_err(), + state.init_value::(false).unwrap_err(), TlvError::TypeAlreadyExists.into(), ); } #[test] - fn alloc() { + fn alloc_first() { let tlv_size = 1; let account_size = get_base_len() + tlv_size; let mut buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // not enough room - let data = state.alloc::(tlv_size).unwrap(); + let data = state.alloc::(tlv_size, false).unwrap().0; assert_eq!( pod_from_bytes_mut::(data).unwrap_err(), ProgramError::InvalidArgument, @@ -842,13 +983,34 @@ mod test { // can't double alloc assert_eq!( - state.alloc::(tlv_size).unwrap_err(), + state.alloc::(tlv_size, false).unwrap_err(), TlvError::TypeAlreadyExists.into(), ); } #[test] - fn realloc() { + fn alloc_with_repetition() { + let tlv_size = 1; + let account_size = (get_base_len() + tlv_size) * 2; + let mut buffer = vec![0; account_size]; + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + + let (data, repetition_number) = state.alloc::(tlv_size, true).unwrap(); + assert_eq!(repetition_number, 0); + + // not enough room + assert_eq!( + pod_from_bytes_mut::(data).unwrap_err(), + ProgramError::InvalidArgument, + ); + + // Can alloc again! + let (_data, repetition_number) = state.alloc::(tlv_size, true).unwrap(); + assert_eq!(repetition_number, 1); + } + + #[test] + fn realloc_first() { const TLV_SIZE: usize = 10; const EXTRA_SPACE: usize = 5; const SMALL_SIZE: usize = 2; @@ -861,19 +1023,21 @@ mod test { let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // alloc both types - let _ = state.alloc::(TLV_SIZE).unwrap(); - let _ = state.init_value::().unwrap(); + let _ = state.alloc::(TLV_SIZE, false).unwrap(); + let _ = state.init_value::(false).unwrap(); // realloc first entry to larger, all 0 - let data = state.realloc::(TLV_SIZE + EXTRA_SPACE).unwrap(); + let data = state + .realloc_first::(TLV_SIZE + EXTRA_SPACE) + .unwrap(); assert_eq!(data, [0; TLV_SIZE + EXTRA_SPACE]); - let value = state.get_value::().unwrap(); + let value = state.get_first_value::().unwrap(); assert_eq!(*value, TestNonZeroDefault::default()); // realloc to smaller, still all 0 - let data = state.realloc::(SMALL_SIZE).unwrap(); + let data = state.realloc_first::(SMALL_SIZE).unwrap(); assert_eq!(data, [0; SMALL_SIZE]); - let value = state.get_value::().unwrap(); + let value = state.get_first_value::().unwrap(); assert_eq!(*value, TestNonZeroDefault::default()); let (_, end_index) = get_discriminators_and_end_index(&buffer).unwrap(); assert_eq!( @@ -886,62 +1050,315 @@ mod test { // realloc too much, fails assert_eq!( state - .realloc::(TLV_SIZE + EXTRA_SPACE + 1) + .realloc_first::(TLV_SIZE + EXTRA_SPACE + 1) .unwrap_err(), ProgramError::InvalidAccountData, ); } -} -#[cfg(all(test, feature = "borsh"))] -mod borsh_test { - use super::*; - #[derive(Clone, Debug, PartialEq, borsh::BorshDeserialize, borsh::BorshSerialize)] - struct TestBorsh { - data: String, // test with a variable length type - inner: TestInnerBorsh, + + #[test] + fn realloc_with_repeating_entries() { + const TLV_SIZE: usize = 10; + const EXTRA_SPACE: usize = 5; + const SMALL_SIZE: usize = 2; + const ACCOUNT_SIZE: usize = get_base_len() + + TLV_SIZE + + EXTRA_SPACE + + get_base_len() + + TLV_SIZE + + get_base_len() + + size_of::(); + let mut buffer = vec![0; ACCOUNT_SIZE]; + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + + // alloc both types, two for the first type and one for the second + let _ = state.alloc::(TLV_SIZE, true).unwrap(); + let _ = state.alloc::(TLV_SIZE, true).unwrap(); + let _ = state.init_value::(true).unwrap(); + + // realloc first entry to larger, all 0 + let data = state + .realloc_with_repetition::(TLV_SIZE + EXTRA_SPACE, 0) + .unwrap(); + assert_eq!(data, [0; TLV_SIZE + EXTRA_SPACE]); + let value = state.get_bytes_with_repetition::(0).unwrap(); + assert_eq!(*value, [0; TLV_SIZE + EXTRA_SPACE]); + let value = state.get_bytes_with_repetition::(1).unwrap(); + assert_eq!(*value, [0; TLV_SIZE]); + let value = state.get_first_value::().unwrap(); + assert_eq!(*value, TestNonZeroDefault::default()); + + // realloc to smaller, still all 0 + let data = state + .realloc_with_repetition::(SMALL_SIZE, 0) + .unwrap(); + assert_eq!(data, [0; SMALL_SIZE]); + let value = state.get_bytes_with_repetition::(0).unwrap(); + assert_eq!(*value, [0; SMALL_SIZE]); + let value = state.get_bytes_with_repetition::(1).unwrap(); + assert_eq!(*value, [0; TLV_SIZE]); + let value = state.get_first_value::().unwrap(); + assert_eq!(*value, TestNonZeroDefault::default()); + let (_, end_index) = get_discriminators_and_end_index(&buffer).unwrap(); + assert_eq!( + &buffer[end_index..ACCOUNT_SIZE], + [0; TLV_SIZE + EXTRA_SPACE - SMALL_SIZE] + ); + + // unpack again since we dropped the last `state` + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + // realloc too much, fails + assert_eq!( + state + .realloc_with_repetition::(TLV_SIZE + EXTRA_SPACE + 1, 0) + .unwrap_err(), + ProgramError::InvalidAccountData, + ); } - #[derive(Clone, Debug, PartialEq, borsh::BorshDeserialize, borsh::BorshSerialize)] - struct TestInnerBorsh { - data: String, + + #[derive(Clone, Debug, PartialEq)] + struct TestVariableLen { + data: String, // test with a variable length type } - impl SplDiscriminate for TestBorsh { + impl SplDiscriminate for TestVariableLen { const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([5; ArrayDiscriminator::LENGTH]); } + impl VariableLenPack for TestVariableLen { + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + let bytes = self.data.as_bytes(); + let end = 8 + bytes.len(); + if dst.len() < end { + Err(ProgramError::InvalidAccountData) + } else { + dst[..8].copy_from_slice(&self.data.len().to_le_bytes()); + dst[8..end].copy_from_slice(bytes); + Ok(()) + } + } + fn unpack_from_slice(src: &[u8]) -> Result { + let length = u64::from_le_bytes(src[..8].try_into().unwrap()) as usize; + if src[8..8 + length].len() != length { + return Err(ProgramError::InvalidAccountData); + } + let data = std::str::from_utf8(&src[8..8 + length]) + .unwrap() + .to_string(); + Ok(Self { data }) + } + fn get_packed_len(&self) -> Result { + Ok(size_of::().saturating_add(self.data.len())) + } + } + #[test] - fn borsh_value() { + fn first_variable_len_value() { let initial_data = "This is a pretty cool test!"; - let initial_inner_data = "And it gets even cooler!"; // exactly the right size - let tlv_size = 4 + initial_data.len() + 4 + initial_inner_data.len(); + let tlv_size = 8 + initial_data.len(); let account_size = get_base_len() + tlv_size; let mut buffer = vec![0; account_size]; let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // don't actually need to hold onto the data! - let _ = state.alloc::(tlv_size).unwrap(); - let test_borsh = TestBorsh { + let _ = state.alloc::(tlv_size, false).unwrap(); + let test_variable_len = TestVariableLen { data: initial_data.to_string(), - inner: TestInnerBorsh { - data: initial_inner_data.to_string(), - }, }; - state.borsh_serialize(&test_borsh).unwrap(); - let deser = state.borsh_deserialize::().unwrap(); - assert_eq!(deser, test_borsh); + state + .pack_first_variable_len_value(&test_variable_len) + .unwrap(); + let deser = state + .get_first_variable_len_value::() + .unwrap(); + assert_eq!(deser, test_variable_len); // writing too much data fails let too_much_data = "This is a pretty cool test!?"; assert_eq!( state - .borsh_serialize(&TestBorsh { + .pack_first_variable_len_value(&TestVariableLen { data: too_much_data.to_string(), - inner: TestInnerBorsh { - data: initial_inner_data.to_string(), - } }) .unwrap_err(), - ProgramError::BorshIoError("failed to write whole buffer".to_string()), + ProgramError::InvalidAccountData + ); + } + + #[test] + fn variable_len_value_with_repetition() { + let variable_len_1 = TestVariableLen { + data: "Let's see if we can pack multiple variable length values".to_string(), + }; + let tlv_size_1 = 8 + variable_len_1.data.len(); + + let variable_len_2 = TestVariableLen { + data: "I think we can".to_string(), + }; + let tlv_size_2 = 8 + variable_len_2.data.len(); + + let variable_len_3 = TestVariableLen { + data: "In fact, I know we can!".to_string(), + }; + let tlv_size_3 = 8 + variable_len_3.data.len(); + + let variable_len_4 = TestVariableLen { + data: "How cool is this?".to_string(), + }; + let tlv_size_4 = 8 + variable_len_4.data.len(); + + let account_size = get_base_len() + + tlv_size_1 + + get_base_len() + + tlv_size_2 + + get_base_len() + + tlv_size_3 + + get_base_len() + + tlv_size_4; + let mut buffer = vec![0; account_size]; + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + + let (_, repetition_number) = state.alloc::(tlv_size_1, true).unwrap(); + state + .pack_variable_len_value_with_repetition(&variable_len_1, repetition_number) + .unwrap(); + assert_eq!(repetition_number, 0); + assert_eq!( + state + .get_first_variable_len_value::() + .unwrap(), + variable_len_1, + ); + + let (_, repetition_number) = state.alloc::(tlv_size_2, true).unwrap(); + state + .pack_variable_len_value_with_repetition(&variable_len_2, repetition_number) + .unwrap(); + assert_eq!(repetition_number, 1); + assert_eq!( + state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(), + variable_len_2, + ); + + let (_, repetition_number) = state.alloc::(tlv_size_3, true).unwrap(); + state + .pack_variable_len_value_with_repetition(&variable_len_3, repetition_number) + .unwrap(); + assert_eq!(repetition_number, 2); + assert_eq!( + state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(), + variable_len_3, ); + + let (_, repetition_number) = state.alloc::(tlv_size_4, true).unwrap(); + state + .pack_variable_len_value_with_repetition(&variable_len_4, repetition_number) + .unwrap(); + assert_eq!(repetition_number, 3); + assert_eq!( + state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(), + variable_len_4, + ); + } + + #[test] + fn add_entry_mix_and_match() { + let mut buffer = vec![]; + + // Add an entry for a fixed length value + let fixed_data = TestValue { data: [1; 32] }; + let tlv_size = get_base_len() + size_of::(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let (value, repetition_number) = state.init_value::(true).unwrap(); + value.data = fixed_data.data; + assert_eq!(repetition_number, 0); + assert_eq!(*value, fixed_data); + } + + // Add an entry for a variable length value + let variable_data = TestVariableLen { + data: "This is my first variable length entry!".to_string(), + }; + let tlv_size = get_base_len() + 8 + variable_data.data.len(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let repetition_number = state + .alloc_and_pack_variable_len_entry(&variable_data, true) + .unwrap(); + let value = state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(); + assert_eq!(repetition_number, 0); + assert_eq!(value, variable_data); + } + + // Add another entry for a variable length value + let variable_data = TestVariableLen { + data: "This is actually my second variable length entry!".to_string(), + }; + let tlv_size = get_base_len() + 8 + variable_data.data.len(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let repetition_number = state + .alloc_and_pack_variable_len_entry(&variable_data, true) + .unwrap(); + let value = state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(); + assert_eq!(repetition_number, 1); + assert_eq!(value, variable_data); + } + + // Add another entry for a fixed length value + let fixed_data = TestValue { data: [2; 32] }; + let tlv_size = get_base_len() + size_of::(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let (value, repetition_number) = state.init_value::(true).unwrap(); + value.data = fixed_data.data; + assert_eq!(repetition_number, 1); + assert_eq!(*value, fixed_data); + } + + // Add another entry for a fixed length value + let fixed_data = TestValue { data: [3; 32] }; + let tlv_size = get_base_len() + size_of::(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let (value, repetition_number) = state.init_value::(true).unwrap(); + value.data = fixed_data.data; + assert_eq!(repetition_number, 2); + assert_eq!(*value, fixed_data); + } + + // Add another entry for a variable length value + let variable_data = TestVariableLen { + data: "Wow! My third variable length entry!".to_string(), + }; + let tlv_size = get_base_len() + 8 + variable_data.data.len(); + buffer.extend(vec![0; tlv_size]); + { + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + let repetition_number = state + .alloc_and_pack_variable_len_entry(&variable_data, true) + .unwrap(); + let value = state + .get_variable_len_value_with_repetition::(repetition_number) + .unwrap(); + assert_eq!(repetition_number, 2); + assert_eq!(value, variable_data); + } } } diff --git a/libraries/type-length-value/src/variable_len_pack.rs b/libraries/type-length-value/src/variable_len_pack.rs new file mode 100644 index 00000000000..1840298e5ac --- /dev/null +++ b/libraries/type-length-value/src/variable_len_pack.rs @@ -0,0 +1,27 @@ +//! The [`VariableLenPack`] serialization trait. + +use solana_program::program_error::ProgramError; + +/// Trait that mimics a lot of the functionality of +/// `solana_program::program_pack::Pack` but specifically works for +/// variable-size types. +pub trait VariableLenPack { + /// Writes the serialized form of the instance into the given slice + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), ProgramError>; + + /// Deserializes the type from the given slice + fn unpack_from_slice(src: &[u8]) -> Result + where + Self: Sized; + + /// Gets the packed length for a given instance of the type + fn get_packed_len(&self) -> Result; + + /// Safely write the contents to the type into the given slice + fn pack(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + if dst.len() != self.get_packed_len()? { + return Err(ProgramError::InvalidAccountData); + } + self.pack_into_slice(dst) + } +} diff --git a/managed-token/program/Cargo.toml b/managed-token/program/Cargo.toml index 8f92681799d..78b1cc06029 100644 --- a/managed-token/program/Cargo.toml +++ b/managed-token/program/Cargo.toml @@ -23,12 +23,12 @@ test = [] [dependencies] borsh = "0.10" -shank = "^0.0.5" -solana-program = "1.16.1" +shank = "^0.2.1" +solana-program = "1.17.2" spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = [ "no-entrypoint", ] } spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -thiserror = "^1.0.40" +thiserror = "^1.0.50" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" diff --git a/managed-token/sdk/idl/spl_managed_token.json b/managed-token/sdk/idl/spl_managed_token.json index 6f16c8bed65..cbb7f158ed6 100644 --- a/managed-token/sdk/idl/spl_managed_token.json +++ b/managed-token/sdk/idl/spl_managed_token.json @@ -24,18 +24,22 @@ "name": "systemProgram", "isMut": false, "isSigner": false, - "desc": "System program" + "docs": [ + "System program" + ] }, { "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [ { - "name": "instructionArgs", + "name": "decimals", "type": "u8" } ], @@ -81,19 +85,25 @@ "name": "systemProgram", "isMut": false, "isSigner": false, - "desc": "System program" + "docs": [ + "System program" + ] }, { "name": "associatedTokenProgram", "isMut": false, "isSigner": false, - "desc": "Associated Token program" + "docs": [ + "Associated Token program" + ] }, { "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [], @@ -139,12 +149,14 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [ { - "name": "instructionArgs", + "name": "amount", "type": "u64" } ], @@ -180,12 +192,14 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [ { - "name": "instructionArgs", + "name": "amount", "type": "u64" } ], @@ -226,12 +240,14 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [ { - "name": "instructionArgs", + "name": "amount", "type": "u64" } ], @@ -277,7 +293,9 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [], @@ -323,12 +341,14 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [ { - "name": "instructionArgs", + "name": "amount", "type": "u64" } ], @@ -369,7 +389,9 @@ "name": "tokenProgram", "isMut": false, "isSigner": false, - "desc": "Token program" + "docs": [ + "Token program" + ] } ], "args": [], @@ -381,8 +403,6 @@ ], "metadata": { "origin": "shank", - "address": "mTok58Lg4YfcmwqyrDHpf7ogp599WRhzb6PxjaBqAxS", - "binaryVersion": "0.0.5", - "libVersion": "^0.0.5" + "address": "mTok58Lg4YfcmwqyrDHpf7ogp599WRhzb6PxjaBqAxS" } } \ No newline at end of file diff --git a/memo/js/package-lock.json b/memo/js/package-lock.json index e9c1d1ae2ea..4f9b517e14c 100644 --- a/memo/js/package-lock.json +++ b/memo/js/package-lock.json @@ -17,23 +17,23 @@ "@types/jest": "^28.1.7", "@types/node": "^20.1.2", "@types/node-fetch": "^2.6.2", - "@types/prettier": "^2.7.0", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "chai": "^4.3.6", "eslint": "^8.20.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-require-extensions": "^0.1.1", - "gh-pages": "^5.0.0", + "gh-pages": "^6.0.0", "jest": "^28.1.3", - "prettier": "^2.7.1", + "prettier": "^3.0.0", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.0", "ts-jest": "^28.0.8", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" }, "engines": { @@ -66,17 +66,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.18.13", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", @@ -126,13 +198,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -181,34 +254,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -267,30 +340,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -320,13 +393,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -405,9 +478,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -579,45 +652,45 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -635,13 +708,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -692,18 +765,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -724,9 +797,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -748,12 +821,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -775,9 +848,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1205,41 +1278,38 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1276,6 +1346,26 @@ "node": ">= 8" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.2.12", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -1334,24 +1424,24 @@ } }, "node_modules/@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -1422,9 +1512,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "node_modules/@types/connect": { @@ -1480,37 +1570,44 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==", - "dev": true + "version": "20.8.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==", "dev": true, "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" } }, "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "deprecated": "This is a stub types definition. prettier provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "prettier": "*" + } }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1544,32 +1641,33 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1578,25 +1676,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1605,16 +1704,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1622,25 +1721,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1649,12 +1748,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1662,21 +1761,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1689,48 +1788,53 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -1922,20 +2026,6 @@ "form-data": "^4.0.0" } }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/babel-jest": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", @@ -2061,6 +2151,15 @@ } ] }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/bigint-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", @@ -2106,6 +2205,18 @@ "text-encoding-utf-8": "^1.0.2" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2229,6 +2340,21 @@ "node": ">=6.14.2" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2264,18 +2390,18 @@ ] }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" }, "engines": { "node": ">=4" @@ -2307,10 +2433,13 @@ } }, "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, "engines": { "node": "*" } @@ -2474,26 +2603,182 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/delay": { @@ -2659,27 +2944,28 @@ } }, "node_modules/eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2689,7 +2975,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -2701,7 +2986,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -2715,9 +2999,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2727,21 +3011,29 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } @@ -2760,23 +3052,14 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2784,15 +3067,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2800,19 +3079,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -2851,15 +3121,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -2872,7 +3133,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -2881,15 +3142,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2990,9 +3242,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3165,9 +3417,9 @@ } }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -3185,17 +3437,17 @@ "dev": true }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -3243,9 +3495,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -3273,17 +3525,17 @@ } }, "node_modules/gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", "dev": true, "dependencies": { "async": "^3.2.4", - "commander": "^2.18.0", + "commander": "^11.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", + "fs-extra": "^11.1.1", "globby": "^6.1.0" }, "bin": { @@ -3306,6 +3558,15 @@ "node": ">=0.10.0" } }, + "node_modules/gh-pages/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/gh-pages/node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -3381,9 +3642,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3421,12 +3682,6 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3498,9 +3753,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -3593,6 +3848,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3623,6 +3893,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3653,6 +3941,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4236,6 +4551,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/jest-snapshot/node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, "node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", @@ -4416,10 +4737,13 @@ "dev": true }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -4529,9 +4853,9 @@ "dev": true }, "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "dev": true, "dependencies": { "get-func-name": "^2.0.0" @@ -4703,16 +5027,10 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" @@ -4807,6 +5125,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5075,15 +5411,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -5213,9 +5549,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", "dev": true }, "node_modules/require-directory": { @@ -5349,6 +5685,21 @@ } } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5402,9 +5753,9 @@ ] }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5562,9 +5913,9 @@ } }, "node_modules/start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "dependencies": { "arg": "^5.0.2", @@ -5582,7 +5933,7 @@ "start-test": "src/bin/start.js" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/start-server-and-test/node_modules/arg": { @@ -5742,6 +6093,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -5790,6 +6157,18 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5844,6 +6223,18 @@ "node": ">=0.8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "28.0.8", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", @@ -5931,30 +6322,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, "node_modules/type-check": { @@ -5991,24 +6361,24 @@ } }, "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.3.tgz", + "integrity": "sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -6021,9 +6391,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -6036,9 +6406,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6048,13 +6418,28 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/update-browserslist-db": { @@ -6345,12 +6730,71 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -6391,13 +6835,14 @@ } }, "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -6435,28 +6880,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -6500,24 +6945,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -6538,13 +6983,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -6607,9 +7052,9 @@ } }, "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -6730,39 +7175,39 @@ } }, "@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6776,13 +7221,13 @@ } }, "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -6823,15 +7268,15 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -6846,9 +7291,9 @@ } }, "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true }, "@hapi/hoek": { @@ -6867,12 +7312,12 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" } @@ -6884,9 +7329,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -7222,28 +7667,28 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dev": true, "requires": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" } }, "@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", "dev": true }, "@nodelib/fs.scandir": { @@ -7272,6 +7717,20 @@ "fastq": "^1.6.0" } }, + "@pkgr/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.2.12", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.5.0" + } + }, "@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -7327,24 +7786,24 @@ } }, "@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "dev": true, "requires": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -7415,9 +7874,9 @@ } }, "@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "@types/connect": { @@ -7473,37 +7932,43 @@ } }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==", - "dev": true + "version": "20.8.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==", "dev": true, "requires": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" } }, "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "dev": true, + "requires": { + "prettier": "*" + } }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "@types/stack-utils": { @@ -7537,104 +8002,111 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" } }, "@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -7775,19 +8247,6 @@ "requires": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } } }, "babel-jest": { @@ -7880,6 +8339,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, "bigint-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", @@ -7921,6 +8386,15 @@ "text-encoding-utf-8": "^1.0.2" } }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8004,6 +8478,15 @@ "node-gyp-build": "^4.3.0" } }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8023,18 +8506,18 @@ "dev": true }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" } }, "chalk": { @@ -8054,10 +8537,13 @@ "dev": true }, "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } }, "check-more-types": { "version": "2.24.0", @@ -8212,6 +8698,101 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "dependencies": { + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -8333,27 +8914,28 @@ "dev": true }, "eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8363,7 +8945,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -8375,42 +8956,24 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "requires": {} }, "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" } }, "eslint-plugin-require-extensions": { @@ -8421,25 +8984,25 @@ "requires": {} }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { "acorn": "^8.9.0", @@ -8460,14 +9023,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -8477,20 +9032,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -8575,9 +9122,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -8708,9 +9255,9 @@ "dev": true }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -8725,14 +9272,14 @@ "dev": true }, "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, "fs.realpath": { @@ -8767,9 +9314,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-package-type": { @@ -8785,17 +9332,17 @@ "dev": true }, "gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", "dev": true, "requires": { "async": "^3.2.4", - "commander": "^2.18.0", + "commander": "^11.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", + "fs-extra": "^11.1.1", "globby": "^6.1.0" }, "dependencies": { @@ -8808,6 +9355,12 @@ "array-uniq": "^1.0.1" } }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, "find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -8864,9 +9417,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8892,12 +9445,6 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -8946,9 +9493,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -9014,6 +9561,12 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -9035,6 +9588,15 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9053,6 +9615,23 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -9503,6 +10082,14 @@ "natural-compare": "^1.4.0", "pretty-format": "^28.1.3", "semver": "^7.3.5" + }, + "dependencies": { + "@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + } } }, "jest-util": { @@ -9650,12 +10237,13 @@ "dev": true }, "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" } }, "jsonparse": { @@ -9736,9 +10324,9 @@ "dev": true }, "loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "dev": true, "requires": { "get-func-name": "^2.0.0" @@ -9873,16 +10461,10 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -9946,6 +10528,18 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -10138,9 +10732,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "prettier-linter-helpers": { @@ -10225,9 +10819,9 @@ } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", "dev": true }, "require-directory": { @@ -10314,6 +10908,15 @@ } } }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10339,9 +10942,9 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10462,9 +11065,9 @@ } }, "start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "requires": { "arg": "^5.0.2", @@ -10598,6 +11201,16 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -10637,6 +11250,12 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -10681,6 +11300,13 @@ } } }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, "ts-jest": { "version": "28.0.8", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", @@ -10719,28 +11345,11 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10763,14 +11372,14 @@ "dev": true }, "typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.3.tgz", + "integrity": "sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw==", "dev": true, "requires": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "dependencies": { @@ -10784,9 +11393,9 @@ } }, "minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -10795,15 +11404,27 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, "update-browserslist-db": { diff --git a/memo/js/package.json b/memo/js/package.json index a1beff64205..85ec719ae85 100644 --- a/memo/js/package.json +++ b/memo/js/package.json @@ -40,7 +40,7 @@ "lint:fix": "npm run fmt && eslint --fix .", "test": "npm run test:unit && npm run test:e2e", "test:unit": "jest test/unit", - "test:e2e": "start-server-and-test 'solana-test-validator -r -q' http://localhost:8899/health 'jest test/e2e'", + "test:e2e": "start-server-and-test 'solana-test-validator -r -q' http://127.0.0.1:8899/health 'jest test/e2e'", "deploy": "npm run deploy:docs", "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", "deploy:docs": "npm run docs && gh-pages --dest memo/js --dist docs --dotfiles" @@ -57,23 +57,23 @@ "@types/jest": "^28.1.7", "@types/node": "^20.1.2", "@types/node-fetch": "^2.6.2", - "@types/prettier": "^2.7.0", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "chai": "^4.3.6", "eslint": "^8.20.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-require-extensions": "^0.1.1", - "gh-pages": "^5.0.0", + "gh-pages": "^6.0.0", "jest": "^28.1.3", - "prettier": "^2.7.1", + "prettier": "^3.0.0", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.0", "ts-jest": "^28.0.8", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" } } diff --git a/memo/js/test/e2e/transaction.test.ts b/memo/js/test/e2e/transaction.test.ts index 32538b13e96..0d7cd62871c 100644 --- a/memo/js/test/e2e/transaction.test.ts +++ b/memo/js/test/e2e/transaction.test.ts @@ -2,7 +2,7 @@ import { createMemoInstruction } from '../../src/index'; import { Connection, Keypair, Transaction, LAMPORTS_PER_SOL, sendAndConfirmTransaction } from '@solana/web3.js'; test('transaction: live', async () => { - const url = 'http://localhost:8899'; + const url = 'http://127.0.0.1:8899'; const connection = new Connection(url, 'confirmed'); await connection.getVersion(); const signer = new Keypair(); // also fee-payer diff --git a/memo/program/Cargo.toml b/memo/program/Cargo.toml index 188bf7255e5..ea14b889ad4 100644 --- a/memo/program/Cargo.toml +++ b/memo/program/Cargo.toml @@ -12,11 +12,11 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/name-service/js/package.json b/name-service/js/package.json index 35135e851dc..ed79f9a3604 100644 --- a/name-service/js/package.json +++ b/name-service/js/package.json @@ -1,6 +1,6 @@ { "name": "@solana/spl-name-service", - "version": "0.1.4", + "version": "0.2.0", "description": "SPL Name Service JavaScript API", "license": "MIT", "author": "Solana Labs Maintainers ", @@ -28,7 +28,7 @@ "doc": "yarn typedoc src/index.ts", "test": "yarn test:unit && yarn test:e2e", "test:unit": "mocha test/unit", - "test:e2e": "start-server-and-test 'solana-test-validator --bpf-program namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX ../../target/deploy/spl_name_service.so --reset --quiet' http://localhost:8899/health 'mocha test/e2e'" + "test:e2e": "start-server-and-test 'solana-test-validator --bpf-program namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX ../../target/deploy/spl_name_service.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'" }, "prettier": { "singleQuote": true @@ -46,15 +46,15 @@ "chai": "^4.3.7", "chai-as-promised": "^7.1.1", "eslint": "^7.8.0", - "eslint-config-prettier": "^8.8.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-functional": "^3.0.2", "eslint-plugin-import": "^2.22.0", "mocha": "^10.2.0", - "prettier": "^2.2.1", + "prettier": "^3.0.0", "start-server-and-test": "^2.0.0", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" }, "dependencies": { diff --git a/name-service/js/src/bindings.ts b/name-service/js/src/bindings.ts index 492228201e5..b0f9a095b84 100644 --- a/name-service/js/src/bindings.ts +++ b/name-service/js/src/bindings.ts @@ -24,7 +24,7 @@ import { //////////////////////////////////////////////////////////// export const NAME_PROGRAM_ID = new PublicKey( - 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX' + 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX', ); export const HASH_PREFIX = 'SPL Name Service'; @@ -50,13 +50,13 @@ export async function createNameRegistry( nameOwner: PublicKey, lamports?: number, nameClass?: PublicKey, - parentName?: PublicKey + parentName?: PublicKey, ): Promise { const hashed_name = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashed_name, nameClass, - parentName + parentName, ); const balance = lamports @@ -80,7 +80,7 @@ export async function createNameRegistry( new Numberu32(space), nameClass, parentName, - nameParentOwner + nameParentOwner, ); return createNameInstr; @@ -102,13 +102,13 @@ export async function updateNameRegistryData( offset: number, input_data: Buffer, nameClass?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): Promise { const hashed_name = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashed_name, nameClass, - nameParent + nameParent, ); let signer: PublicKey; @@ -125,7 +125,7 @@ export async function updateNameRegistryData( new Numberu32(offset), input_data, signer, - nameParent + nameParent, ); return updateInstr; @@ -147,13 +147,13 @@ export async function transferNameOwnership( name: string, newOwner: PublicKey, nameClass?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): Promise { const hashed_name = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashed_name, nameClass, - nameParent + nameParent, ); let curentNameOwner: PublicKey; @@ -171,7 +171,7 @@ export async function transferNameOwnership( newOwner, curentNameOwner, nameClass, - nameParent + nameParent, ); return transferInstr; @@ -192,13 +192,13 @@ export async function deleteNameRegistry( name: string, refundTargetKey: PublicKey, nameClass?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): Promise { const hashed_name = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashed_name, nameClass, - nameParent + nameParent, ); let nameOwner: PublicKey; @@ -213,7 +213,7 @@ export async function deleteNameRegistry( NAME_PROGRAM_ID, nameAccountKey, refundTargetKey, - nameOwner + nameOwner, ); return changeAuthoritiesInstr; @@ -236,13 +236,13 @@ export async function reallocNameAccount( space: number, payerKey: PublicKey, nameClass?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): Promise { const hashedName = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashedName, nameClass, - nameParent + nameParent, ); let nameOwner: PublicKey; @@ -259,7 +259,7 @@ export async function reallocNameAccount( payerKey, nameAccountKey, nameOwner, - new Numberu32(space) + new Numberu32(space), ); return reallocInstr; diff --git a/name-service/js/src/instructions.ts b/name-service/js/src/instructions.ts index 40899232d3b..9d48d60bb0f 100644 --- a/name-service/js/src/instructions.ts +++ b/name-service/js/src/instructions.ts @@ -13,7 +13,7 @@ export function createInstruction( space: Numberu32, nameClassKey?: PublicKey, nameParent?: PublicKey, - nameParentOwner?: PublicKey + nameParentOwner?: PublicKey, ): TransactionInstruction { const buffers = [ Buffer.from(Int8Array.from([0])), @@ -95,7 +95,7 @@ export function updateInstruction( offset: Numberu32, input_data: Buffer, nameUpdateSigner: PublicKey, - parentNameKey: PublicKey | undefined + parentNameKey: PublicKey | undefined, ): TransactionInstruction { const buffers = [ Buffer.from(Int8Array.from([1])), @@ -139,7 +139,7 @@ export function transferInstruction( newOwnerKey: PublicKey, currentNameOwnerKey: PublicKey, nameClassKey?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): TransactionInstruction { const buffers = [Buffer.from(Int8Array.from([2])), newOwnerKey.toBuffer()]; @@ -185,7 +185,7 @@ export function deleteInstruction( nameProgramId: PublicKey, nameAccountKey: PublicKey, refundTargetKey: PublicKey, - nameOwnerKey: PublicKey + nameOwnerKey: PublicKey, ): TransactionInstruction { const buffers = [Buffer.from(Int8Array.from([3]))]; @@ -221,7 +221,7 @@ export function reallocInstruction( payerKey: PublicKey, nameAccountKey: PublicKey, nameOwnerKey: PublicKey, - space: Numberu32 + space: Numberu32, ): TransactionInstruction { const buffers = [Buffer.from(Int8Array.from([4])), space.toBuffer()]; diff --git a/name-service/js/src/state.ts b/name-service/js/src/state.ts index 5b56bebc4c4..3cfecdb7a79 100644 --- a/name-service/js/src/state.ts +++ b/name-service/js/src/state.ts @@ -33,11 +33,11 @@ export class NameRegistryState { public static async retrieve( connection: Connection, - nameAccountKey: PublicKey + nameAccountKey: PublicKey, ): Promise { const nameAccount = await connection.getAccountInfo( nameAccountKey, - 'processed' + 'processed', ); if (!nameAccount) { throw new Error('Invalid name account provided'); @@ -46,7 +46,7 @@ export class NameRegistryState { const res: NameRegistryState = deserializeUnchecked( this.schema, NameRegistryState, - nameAccount.data + nameAccount.data, ); res.data = nameAccount.data?.slice(this.HEADER_LEN); diff --git a/name-service/js/src/twitter.ts b/name-service/js/src/twitter.ts index d33b9666f98..fbb77c9d859 100644 --- a/name-service/js/src/twitter.ts +++ b/name-service/js/src/twitter.ts @@ -26,12 +26,12 @@ import { // Global Variables export const TWITTER_VERIFICATION_AUTHORITY = new PublicKey( - 'FvPH7PrVrLGKPfqaf3xJodFTjZriqrAXXLTVWEorTFBi' + 'FvPH7PrVrLGKPfqaf3xJodFTjZriqrAXXLTVWEorTFBi', ); // The address of the name registry that will be a parent to all twitter handle registries, // it should be owned by the TWITTER_VERIFICATION_AUTHORITY and its name is irrelevant export const TWITTER_ROOT_PARENT_REGISTRY_KEY = new PublicKey( - '4YcexoW3r78zz16J2aqmukBLRwGq6rAvWzJpkYAXqebv' + '4YcexoW3r78zz16J2aqmukBLRwGq6rAvWzJpkYAXqebv', ); //////////////////////////////////////////////////// @@ -43,14 +43,14 @@ export async function createVerifiedTwitterRegistry( twitterHandle: string, verifiedPubkey: PublicKey, space: number, // The space that the user will have to write data into the verified registry - payerKey: PublicKey + payerKey: PublicKey, ): Promise { // Create user facing registry const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); let instructions = [ @@ -65,7 +65,7 @@ export async function createVerifiedTwitterRegistry( new Numberu32(space), undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY, - TWITTER_VERIFICATION_AUTHORITY // Twitter authority acts as owner of the parent for all user-facing registries + TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as owner of the parent for all user-facing registries ), ]; @@ -75,8 +75,8 @@ export async function createVerifiedTwitterRegistry( twitterHandle, twitterHandleRegistryKey, verifiedPubkey, - payerKey - ) + payerKey, + ), ); return instructions; @@ -88,13 +88,13 @@ export async function changeTwitterRegistryData( twitterHandle: string, verifiedPubkey: PublicKey, offset: number, // The offset at which to write the input data into the NameRegistryData - input_data: Buffer + input_data: Buffer, ): Promise { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const instructions = [ @@ -104,7 +104,7 @@ export async function changeTwitterRegistryData( new Numberu32(offset), input_data, verifiedPubkey, - undefined + undefined, ), ]; @@ -118,13 +118,13 @@ export async function changeVerifiedPubkey( twitterHandle: string, currentVerifiedPubkey: PublicKey, newVerifiedPubkey: PublicKey, - payerKey: PublicKey + payerKey: PublicKey, ): Promise { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); // Transfer the user-facing registry ownership @@ -134,7 +134,7 @@ export async function changeVerifiedPubkey( twitterHandleRegistryKey, newVerifiedPubkey, currentVerifiedPubkey, - undefined + undefined, ), ]; @@ -145,8 +145,8 @@ export async function changeVerifiedPubkey( currentVerifiedPubkey.toString(), payerKey, TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY - ) + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ), ); // Create the new reverse registry @@ -156,8 +156,8 @@ export async function changeVerifiedPubkey( twitterHandle, twitterHandleRegistryKey, newVerifiedPubkey, - payerKey - ) + payerKey, + ), ); return instructions; @@ -167,20 +167,20 @@ export async function changeVerifiedPubkey( // Signed by the verified pubkey export async function deleteTwitterRegistry( twitterHandle: string, - verifiedPubkey: PublicKey + verifiedPubkey: PublicKey, ): Promise { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey( hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const instructions = [ @@ -189,14 +189,14 @@ export async function deleteTwitterRegistry( NAME_PROGRAM_ID, twitterHandleRegistryKey, verifiedPubkey, - verifiedPubkey + verifiedPubkey, ), // Delete the reverse registry deleteInstruction( NAME_PROGRAM_ID, reverseRegistryKey, verifiedPubkey, - verifiedPubkey + verifiedPubkey, ), ]; @@ -208,47 +208,47 @@ export async function deleteTwitterRegistry( // Returns the key of the user-facing registry export async function getTwitterRegistryKey( - twitter_handle: string + twitter_handle: string, ): Promise { const hashedTwitterHandle = await getHashedName(twitter_handle); return await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); } export async function getTwitterRegistry( connection: Connection, - twitter_handle: string + twitter_handle: string, ): Promise { const hashedTwitterHandle = await getHashedName(twitter_handle); const twitterHandleRegistryKey = await getNameAccountKey( hashedTwitterHandle, undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const registry = NameRegistryState.retrieve( connection, - twitterHandleRegistryKey + twitterHandleRegistryKey, ); return registry; } export async function getHandleAndRegistryKey( connection: Connection, - verifiedPubkey: PublicKey + verifiedPubkey: PublicKey, ): Promise<[string, PublicKey]> { const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey( hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const reverseRegistryState = await ReverseTwitterRegistryState.retrieve( connection, - reverseRegistryKey + reverseRegistryKey, ); return [ reverseRegistryState.twitterHandle, @@ -259,7 +259,7 @@ export async function getHandleAndRegistryKey( // Uses the RPC node filtering feature, execution speed may vary export async function getTwitterHandleandRegistryKeyViaFilters( connection: Connection, - verifiedPubkey: PublicKey + verifiedPubkey: PublicKey, ): Promise<[string, PublicKey]> { const filters = [ { @@ -285,7 +285,7 @@ export async function getTwitterHandleandRegistryKeyViaFilters( const filteredAccounts = await getFilteredProgramAccounts( connection, NAME_PROGRAM_ID, - filters + filters, ); for (const f of filteredAccounts) { @@ -294,7 +294,7 @@ export async function getTwitterHandleandRegistryKeyViaFilters( const state: ReverseTwitterRegistryState = deserialize( ReverseTwitterRegistryState.schema, ReverseTwitterRegistryState, - data + data, ); return [state.twitterHandle, new PublicKey(state.twitterRegistryKey)]; } @@ -306,7 +306,7 @@ export async function getTwitterHandleandRegistryKeyViaFilters( // Does not give you the handle, but is an alternative to getHandlesAndKeysFromVerifiedPubkey + getTwitterRegistry to get the data export async function getTwitterRegistryData( connection: Connection, - verifiedPubkey: PublicKey + verifiedPubkey: PublicKey, ): Promise { const filters = [ { @@ -332,7 +332,7 @@ export async function getTwitterRegistryData( const filteredAccounts = await getFilteredProgramAccounts( connection, NAME_PROGRAM_ID, - filters + filters, ); if (filteredAccounts.length > 1) { @@ -340,7 +340,7 @@ export async function getTwitterRegistryData( } return filteredAccounts[0].accountInfo.data.slice( - NameRegistryState.HEADER_LEN + NameRegistryState.HEADER_LEN, ); } @@ -370,11 +370,11 @@ export class ReverseTwitterRegistryState { public static async retrieve( connection: Connection, - reverseTwitterAccountKey: PublicKey + reverseTwitterAccountKey: PublicKey, ): Promise { const reverseTwitterAccount = await connection.getAccountInfo( reverseTwitterAccountKey, - 'processed' + 'processed', ); if (!reverseTwitterAccount) { throw new Error('Invalid reverse Twitter account provided'); @@ -383,7 +383,7 @@ export class ReverseTwitterRegistryState { const res: ReverseTwitterRegistryState = deserializeUnchecked( this.schema, ReverseTwitterRegistryState, - reverseTwitterAccount.data.slice(NameRegistryState.HEADER_LEN) + reverseTwitterAccount.data.slice(NameRegistryState.HEADER_LEN), ); return res; @@ -395,21 +395,21 @@ export async function createReverseTwitterRegistry( twitterHandle: string, twitterRegistryKey: PublicKey, verifiedPubkey: PublicKey, - payerKey: PublicKey + payerKey: PublicKey, ): Promise { // Create the reverse lookup registry const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey( hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY + TWITTER_ROOT_PARENT_REGISTRY_KEY, ); const reverseTwitterRegistryStateBuff = serialize( ReverseTwitterRegistryState.schema, new ReverseTwitterRegistryState({ twitterRegistryKey: twitterRegistryKey.toBytes(), twitterHandle, - }) + }), ); return [ createInstruction( @@ -421,13 +421,13 @@ export async function createReverseTwitterRegistry( hashedVerifiedPubkey, new Numberu64( await connection.getMinimumBalanceForRentExemption( - reverseTwitterRegistryStateBuff.length - ) + reverseTwitterRegistryStateBuff.length, + ), ), new Numberu32(reverseTwitterRegistryStateBuff.length), TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries TWITTER_ROOT_PARENT_REGISTRY_KEY, // Reverse registries are also children of the root - TWITTER_VERIFICATION_AUTHORITY + TWITTER_VERIFICATION_AUTHORITY, ), updateInstruction( NAME_PROGRAM_ID, @@ -435,7 +435,7 @@ export async function createReverseTwitterRegistry( new Numberu32(0), Buffer.from(reverseTwitterRegistryStateBuff), TWITTER_VERIFICATION_AUTHORITY, - undefined + undefined, ), ]; } diff --git a/name-service/js/src/utils.ts b/name-service/js/src/utils.ts index 293321e2a85..2a91fab0556 100644 --- a/name-service/js/src/utils.ts +++ b/name-service/js/src/utils.ts @@ -41,7 +41,7 @@ export class Numberu32 extends BN { .reverse() .map((i) => `00${i.toString(16)}`.slice(-2)) .join(''), - 16 + 16, ); } } @@ -73,7 +73,7 @@ export class Numberu64 extends BN { .reverse() .map((i) => `00${i.toString(16)}`.slice(-2)) .join(''), - 16 + 16, ); } } @@ -83,7 +83,7 @@ export const signAndSendTransactionInstructions = async ( connection: Connection, signers: Array, feePayer: Keypair, - txInstructions: Array + txInstructions: Array, ): Promise => { const tx = new Transaction(); tx.feePayer = feePayer.publicKey; @@ -101,7 +101,7 @@ export async function getHashedName(name: string): Promise { export async function getNameAccountKey( hashed_name: Buffer, nameClass?: PublicKey, - nameParent?: PublicKey + nameParent?: PublicKey, ): Promise { const seeds = [hashed_name]; if (nameClass) { @@ -116,14 +116,14 @@ export async function getNameAccountKey( } const [nameAccountKey] = await PublicKey.findProgramAddress( seeds, - NAME_PROGRAM_ID + NAME_PROGRAM_ID, ); return nameAccountKey; } export async function getNameOwner( connection: Connection, - nameAccountKey: PublicKey + nameAccountKey: PublicKey, ): Promise { const nameAccount = await connection.getAccountInfo(nameAccountKey); if (!nameAccount) { @@ -135,7 +135,7 @@ export async function getNameOwner( export async function getFilteredProgramAccounts( connection: Connection, programId: PublicKey, - filters + filters, ): Promise<{ publicKey: PublicKey; accountInfo: AccountInfo }[]> { const resp = await connection.getProgramAccounts(programId, { commitment: connection.commitment, @@ -151,6 +151,6 @@ export async function getFilteredProgramAccounts( owner: owner, lamports, }, - }) + }), ); } diff --git a/name-service/js/test/e2e/index.test.ts b/name-service/js/test/e2e/index.test.ts index d4a06cd2fcf..820e7f0db79 100644 --- a/name-service/js/test/e2e/index.test.ts +++ b/name-service/js/test/e2e/index.test.ts @@ -21,7 +21,7 @@ import { } from '../../src'; chai.use(chaiAsPromised); -const url = 'http://localhost:8899'; +const url = 'http://127.0.0.1:8899'; describe('Name Service Program', async () => { const connection = new Connection(url, 'confirmed'); @@ -33,7 +33,7 @@ describe('Name Service Program', async () => { before(async () => { const airdropSignature = await connection.requestAirdrop( payer.publicKey, - LAMPORTS_PER_SOL + LAMPORTS_PER_SOL, ); await connection.confirmTransaction({ signature: airdropSignature, @@ -45,7 +45,7 @@ describe('Name Service Program', async () => { name = Math.random().toString() + '.sol'; nameKey = await getNameKey(name); const lamports = await connection.getMinimumBalanceForRentExemption( - space + NameRegistryState.HEADER_LEN + space + NameRegistryState.HEADER_LEN, ); const inst = await createNameRegistry( connection, @@ -53,7 +53,7 @@ describe('Name Service Program', async () => { space, payer.publicKey, owner.publicKey, - lamports + lamports, ); const tx = new Transaction().add(inst); await sendAndConfirmTransaction(connection, tx, [payer]); @@ -77,7 +77,7 @@ describe('Name Service Program', async () => { const inst = await transferNameOwnership( connection, name, - newOwner.publicKey + newOwner.publicKey, ); const tx = new Transaction().add(inst); await sendAndConfirmTransaction(connection, tx, [payer, owner]); @@ -89,7 +89,7 @@ describe('Name Service Program', async () => { connection, name, space + 10, - payer.publicKey + payer.publicKey, ); const tx = new Transaction().add(inst); await sendAndConfirmTransaction(connection, tx, [payer, owner]); @@ -101,7 +101,7 @@ describe('Name Service Program', async () => { connection, name, space - 10, - payer.publicKey + payer.publicKey, ); const tx = new Transaction().add(inst); await sendAndConfirmTransaction(connection, tx, [payer, owner]); @@ -120,13 +120,13 @@ describe('Name Service Program', async () => { const getNameKey = async ( name: string, nameClass?: PublicKey, - parentName?: PublicKey + parentName?: PublicKey, ) => { const hashedName = await getHashedName(name); const nameAccountKey = await getNameAccountKey( hashedName, nameClass, - parentName + parentName, ); return nameAccountKey; }; diff --git a/name-service/js/test/unit/index.test.ts b/name-service/js/test/unit/index.test.ts index 41ec1f0f9f2..5cc2cc2c607 100644 --- a/name-service/js/test/unit/index.test.ts +++ b/name-service/js/test/unit/index.test.ts @@ -20,7 +20,7 @@ chai.use(chaiAsPromised); describe('SplNameService Instructions', () => { const nameServiceAddress = new PublicKey( - 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX' + 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX', ); const nameAccountKey = Keypair.generate().publicKey; const nameOwnerKey = Keypair.generate().publicKey; @@ -39,7 +39,7 @@ describe('SplNameService Instructions', () => { payerKey, name, new Numberu64(LAMPORTS_PER_SOL), - new Numberu64(10) + new Numberu64(10), ); expect(instruction.keys).to.have.length(6); @@ -63,7 +63,7 @@ describe('SplNameService Instructions', () => { new Numberu64(10), nameClassKey, nameParent, - nameParentOwner + nameParentOwner, ); expect(instruction.keys).to.have.length(7); @@ -84,7 +84,7 @@ describe('SplNameService Instructions', () => { new Numberu32(0), data, nameOwnerKey, - undefined + undefined, ); expect(instruction.keys).to.have.length(2); @@ -98,7 +98,7 @@ describe('SplNameService Instructions', () => { nameServiceAddress, nameAccountKey, newOwner, - nameOwnerKey + nameOwnerKey, ); expect(instruction.keys).to.have.length(2); @@ -111,7 +111,7 @@ describe('SplNameService Instructions', () => { nameServiceAddress, nameAccountKey, payerKey, - nameOwnerKey + nameOwnerKey, ); expect(instruction.keys).to.have.length(3); @@ -127,7 +127,7 @@ describe('SplNameService Instructions', () => { payerKey, nameAccountKey, nameOwnerKey, - new Numberu32(30) + new Numberu32(30), ); expect(instruction.keys).to.have.length(4); diff --git a/name-service/js/yarn.lock b/name-service/js/yarn.lock index ec5c500fb76..28a9788dc28 100644 --- a/name-service/js/yarn.lock +++ b/name-service/js/yarn.lock @@ -9,59 +9,78 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: "@babel/highlight" "^7.18.6" -"@babel/generator@^7.20.7": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: - "@babel/types" "^7.20.7" + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -71,44 +90,67 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.20.13", "@babel/parser@^7.20.7", "@babel/parser@^7.7.0": +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + +"@babel/parser@^7.7.0": version "7.20.15" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== +"@babel/runtime@^7.17.2", "@babel/runtime@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== dependencies: - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" -"@babel/template@^7.18.10": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" "@babel/traverse@^7.7.0": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.7", "@babel/types@^7.7.0": +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@babel/types@^7.7.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== @@ -191,6 +233,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -201,6 +248,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -209,6 +261,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -217,17 +277,17 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@noble/curves@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" - integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== +"@noble/curves@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: - "@noble/hashes" "1.3.0" + "@noble/hashes" "1.3.2" -"@noble/hashes@1.3.0", "@noble/hashes@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== +"@noble/hashes@1.3.2", "@noble/hashes@^1.3.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -275,23 +335,23 @@ buffer "~6.0.3" "@solana/web3.js@^1.11.0": - version "1.77.3" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.77.3.tgz#2cbeaa1dd24f8fa386ac924115be82354dfbebab" - integrity sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w== + version "1.87.3" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.87.3.tgz#36871af8d41221d34bfefcf897f158e1793b3356" + integrity sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ== dependencies: - "@babel/runtime" "^7.12.5" - "@noble/curves" "^1.0.0" - "@noble/hashes" "^1.3.0" + "@babel/runtime" "^7.23.2" + "@noble/curves" "^1.2.0" + "@noble/hashes" "^1.3.1" "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.2.1" + agentkeepalive "^4.3.0" bigint-buffer "^1.1.5" - bn.js "^5.0.0" + bn.js "^5.2.1" borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.3" fast-stable-stringify "^1.0.0" jayson "^4.1.0" - node-fetch "^2.6.7" + node-fetch "^2.6.12" rpc-websockets "^7.5.1" superstruct "^0.14.2" @@ -316,28 +376,28 @@ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@tsconfig/recommended@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.2.tgz#1e198237225933e319718f082e78366e9f159d71" - integrity sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.3.tgz#742540ba9170897a44097e838bca411abf56ccd2" + integrity sha512-+jby/Guq9H8O7NWgCv6X8VAiQE8Dr/nccsCtL74xyHKhu2Knu5EAKmOZj3nLCnLm1KooUzKY+5DsnGVqhM8/wQ== "@types/bn.js@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" - integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + version "5.1.4" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.4.tgz#0853d5f92dfdbc8fe1ae60700411a60845fa7d27" + integrity sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA== dependencies: "@types/node" "*" "@types/chai-as-promised@^7.1.5": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255" - integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ== + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.7.tgz#fd16a981ba9542c83d4e1d2f40c7899aae82aa38" + integrity sha512-APucaP5rlmTRYKtRA6FE5QPP87x76ejw5t5guRJ4y5OgMnwtsvigw7HHhKZlx2MGXLeZd6R/GNZR/IqDHcbtQw== dependencies: "@types/chai" "*" "@types/chai@*", "@types/chai@^4.3.4": - version "4.3.5" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" - integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + version "4.3.9" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.9.tgz#144d762491967db8c6dea38e03d2206c2623feec" + integrity sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg== "@types/connect@^3.4.33": version "3.4.35" @@ -357,14 +417,16 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/mocha@^10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" - integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.3.tgz#4804fe9cd39da26eb62fa65c15ea77615a187812" + integrity sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ== "@types/node@*", "@types/node@^20.0.0": - version "20.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6" - integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw== + version "20.8.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e" + integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w== + dependencies: + undici-types "~5.26.4" "@types/node@^12.12.54": version "12.20.55" @@ -384,16 +446,16 @@ "@types/node" "*" "@typescript-eslint/eslint-plugin@^5.59.5": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz#81382d6ecb92b8dda70e91f9035611cb2fecd1c3" - integrity sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/type-utils" "5.60.1" - "@typescript-eslint/utils" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" semver "^7.3.7" @@ -412,13 +474,13 @@ eslint-utils "^3.0.0" "@typescript-eslint/parser@^5.59.5": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.60.1.tgz#0f2f58209c0862a73e3d5a56099abfdfa21d0fd3" - integrity sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/typescript-estree" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" "@typescript-eslint/scope-manager@4.33.0": @@ -429,21 +491,21 @@ "@typescript-eslint/types" "4.33.0" "@typescript-eslint/visitor-keys" "4.33.0" -"@typescript-eslint/scope-manager@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz#35abdb47f500c68c08f2f2b4f22c7c79472854bb" - integrity sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/visitor-keys" "5.60.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz#17770540e98d65ab4730c7aac618003f702893f4" - integrity sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.60.1" - "@typescript-eslint/utils" "5.60.1" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" @@ -452,10 +514,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== -"@typescript-eslint/types@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.60.1.tgz#a17473910f6b8d388ea83c9d7051af89c4eb7561" - integrity sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== "@typescript-eslint/typescript-estree@4.33.0": version "4.33.0" @@ -470,30 +532,30 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz#8c71824b7165b64d5ebd7aa42968899525959834" - integrity sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/visitor-keys" "5.60.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.60.1.tgz#6861ebedbefba1ac85482d2bdef6f2ff1eb65b80" - integrity sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/typescript-estree" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" semver "^7.3.7" @@ -505,12 +567,12 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz#19a877358bf96318ec35d90bfe6bd1445cce9434" - integrity sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.60.1" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" JSONStream@^1.3.5: @@ -541,13 +603,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== -agentkeepalive@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== +agentkeepalive@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: - debug "^4.1.0" - depd "^1.1.2" humanize-ms "^1.2.1" ajv@^6.10.0, ajv@^6.12.4: @@ -634,15 +694,23 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + is-array-buffer "^3.0.1" + +array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-union@^2.1.0: @@ -650,26 +718,50 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" -array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -751,7 +843,7 @@ bluebird@3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^5.0.0, bn.js@^5.1.3, bn.js@^5.2.0: +bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -822,6 +914,15 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -840,19 +941,19 @@ chai-as-promised@^7.1.1: check-error "^1.0.2" chai@^4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -869,10 +970,12 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +check-error@^1.0.2, check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" check-more-types@2.24.0: version "2.24.0" @@ -977,7 +1080,7 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -deep-eql@^4.1.2: +deep-eql@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== @@ -994,6 +1097,15 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -1002,6 +1114,14 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" @@ -1012,11 +1132,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1065,44 +1180,50 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.13" es-set-tostringtag@^2.0.1: version "2.0.1" @@ -1156,24 +1277,24 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-config-prettier@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-module-utils@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" @@ -1197,25 +1318,27 @@ eslint-plugin-functional@^3.0.2: object.fromentries "^2.0.3" eslint-plugin-import@^2.22.0: - version "2.27.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" - integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + version "2.29.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155" + integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.7.4" - has "^1.0.3" - is-core-module "^2.11.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.6" - resolve "^1.22.1" - semver "^6.3.0" - tsconfig-paths "^3.14.1" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.14.2" eslint-scope@^5.1.1: version "5.1.1" @@ -1505,22 +1628,27 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== -functions-have-names@^1.2.2: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -1530,10 +1658,10 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== +get-func-name@^2.0.0, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.2.0" @@ -1544,6 +1672,26 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -1626,10 +1774,10 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" @@ -1677,6 +1825,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -1735,12 +1890,12 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" @@ -1753,6 +1908,15 @@ is-array-buffer@^3.0.1: get-intrinsic "^1.1.3" is-typed-array "^1.1.10" +is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -1780,12 +1944,12 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.12.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" is-date-object@^1.0.1: version "1.0.5" @@ -1878,6 +2042,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +is-typed-array@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -1890,6 +2061,11 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1974,7 +2150,7 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -2034,7 +2210,7 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loupe@^2.3.1: +loupe@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== @@ -2117,10 +2293,10 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" - integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== +minimatch@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" @@ -2181,10 +2357,10 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@^2.6.7: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== dependencies: whatwg-url "^5.0.0" @@ -2205,7 +2381,12 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -2225,23 +2406,33 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.fromentries@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== +object.fromentries@^2.0.3, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" once@^1.3.0: version "1.4.0" @@ -2337,10 +2528,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.2.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== progress@^2.0.0: version "2.0.3" @@ -2378,19 +2569,19 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" regexpp@^3.1.0: version "3.2.0" @@ -2412,12 +2603,21 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.12.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.12.0: + version "1.22.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283" + integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.12.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -2460,6 +2660,16 @@ rxjs@^7.8.0: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -2474,15 +2684,15 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.2.1, semver@^7.3.5, semver@^7.3.7: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -2493,6 +2703,25 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2556,9 +2785,9 @@ sprintf-js@~1.0.2: integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== start-server-and-test@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.0.0.tgz#0644809d63036a8a001efb70582f3e37ebfdd33d" - integrity sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.0.1.tgz#e110e0b5a54c80963f65b9689c2c320ab8ac9025" + integrity sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ== dependencies: arg "^5.0.2" bluebird "3.7.2" @@ -2585,23 +2814,32 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" @@ -2718,13 +2956,13 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -2752,7 +2990,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -2762,6 +3000,36 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -2771,20 +3039,20 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedoc@^0.24.7: - version "0.24.8" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.8.tgz#cce9f47ba6a8d52389f5e583716a2b3b4335b63e" - integrity sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w== +typedoc@^0.25.0: + version "0.25.3" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.3.tgz#53c6d668e1001b3d488e9a750fcdfb05433554c0" + integrity sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw== dependencies: lunr "^2.3.9" marked "^4.3.0" - minimatch "^9.0.0" + minimatch "^9.0.3" shiki "^0.14.1" typescript@^5.0.4: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== unbox-primitive@^1.0.2: version "1.0.2" @@ -2796,6 +3064,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2870,17 +3143,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -2890,9 +3162,9 @@ which@^2.0.1: isexe "^2.0.0" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== workerpool@6.2.1: version "6.2.1" diff --git a/name-service/program/Cargo.toml b/name-service/program/Cargo.toml index a64fdd9a3b1..ca79f45033f 100644 --- a/name-service/program/Cargo.toml +++ b/name-service/program/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "spl-name-service" description = "Solana Program Library Name Service" -version = "0.2.0" +version = "0.3.0" repository = "https://github.com/solana-labs/solana-program-library" authors = [ "lcchy ", @@ -17,15 +17,15 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" +solana-program = "1.17.2" num-traits = "0.2" borsh = "0.10" -num-derive = "0.4.0" -thiserror = "1.0.40" +num-derive = "0.4.1" +thiserror = "1.0.50" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/name-service/program/src/processor.rs b/name-service/program/src/processor.rs index 730d176561a..80bec1faea2 100644 --- a/name-service/program/src/processor.rs +++ b/name-service/program/src/processor.rs @@ -262,7 +262,7 @@ impl Processor { match name_account.lamports().cmp(&required_lamports) { Ordering::Less => { // Overflow cannot happen here because we already checked the sizes. - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] let lamports_to_add = required_lamports - name_account.lamports(); invoke( &system_instruction::transfer( @@ -279,7 +279,7 @@ impl Processor { } Ordering::Greater => { // Overflow cannot happen here because we already checked the sizes. - #[allow(clippy::integer_arithmetic)] + #[allow(clippy::arithmetic_side_effects)] let lamports_to_remove = name_account.lamports() - required_lamports; let source_amount: &mut u64 = &mut name_account.lamports.borrow_mut(); let dest_amount: &mut u64 = &mut payer_account.lamports.borrow_mut(); diff --git a/record/program/Cargo.toml b/record/program/Cargo.toml index ee4a0f4c6d2..0ef907ea30a 100644 --- a/record/program/Cargo.toml +++ b/record/program/Cargo.toml @@ -15,12 +15,12 @@ test-sbf = [] borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/record/program/tests/functional.rs b/record/program/tests/functional.rs index d194eabfce8..3582d13cbf8 100644 --- a/record/program/tests/functional.rs +++ b/record/program/tests/functional.rs @@ -3,7 +3,7 @@ use { borsh::BorshSerialize, solana_program::{ - borsh::get_packed_len, + borsh0_10::get_packed_len, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, rent::Rent, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f2415f8315c..8142c301269 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.69.0" +channel = "1.73.0" diff --git a/shared-memory/program/Cargo.toml b/shared-memory/program/Cargo.toml index 0b45e53c5bb..2ad0f66d2ec 100644 --- a/shared-memory/program/Cargo.toml +++ b/shared-memory/program/Cargo.toml @@ -12,11 +12,11 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -solana-program = "=1.16.1" +solana-program = "=1.17.2" [dev-dependencies] -solana-program-test = "=1.16.1" -solana-sdk = "=1.16.1" +solana-program-test = "=1.17.2" +solana-sdk = "=1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/shared-memory/program/src/lib.rs b/shared-memory/program/src/lib.rs index cb6496aa1ef..6003a5d24ac 100644 --- a/shared-memory/program/src/lib.rs +++ b/shared-memory/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] //! Shared memory program for the Solana blockchain. // diff --git a/single-pool/README.md b/single-pool/README.md new file mode 100644 index 00000000000..9b8bba716d2 --- /dev/null +++ b/single-pool/README.md @@ -0,0 +1,9 @@ +## Solana Program Library Single-Validator Stake Pool + +The single-validator stake pool program is an upcoming SPL program that enables liquid staking with zero fees, no counterparty, and 100% capital efficiency. + +The program defines a canonical pool for every vote account, which can be initialized permissionlessly, and mints tokens in exchange for stake delegated to its designated validator. + +The program is a stripped-down adaptation of the existing multi-validator stake pool program, with approximately 80% less code, to minimize execution risk. + +On launch, users will only be able to deposit and withdraw active stake, but pending future stake program development, we hope to support instant sol deposits and withdrawals as well. diff --git a/single-pool/cli/Cargo.toml b/single-pool/cli/Cargo.toml new file mode 100644 index 00000000000..5b0ae8eaa15 --- /dev/null +++ b/single-pool/cli/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "spl-single-pool-cli" +version = "1.0.0" +description = "Solana Program Library Single-Validator Stake Pool Command-line Utility" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +tokio = "1.14" +clap = { version = "3.2.23", features = ["derive"] } +console = "0.15.7" +borsh = "0.10" +bincode = "1.3.1" +serde = "1.0.190" +serde_derive = "1.0.103" +serde_json = "1.0.108" +serde_with = "3.4.0" +solana-account-decoder = "=1.17.2" +solana-clap-v3-utils = "=1.17.2" +solana-cli-config = "=1.17.2" +solana-cli-output = "=1.17.2" +solana-client = "=1.17.2" +solana-logger = "=1.17.2" +solana-remote-wallet = "=1.17.2" +solana-sdk = "=1.17.2" +solana-transaction-status = "=1.17.2" +solana-vote-program = "=1.17.2" +spl-token = { version = "4.0", path="../../token/program", features = [ "no-entrypoint" ] } +spl-token-client = { version = "0.8", path="../../token/client" } +spl-associated-token-account = { version = "2.0", path="../../associated-token-account/program", features = [ "no-entrypoint" ] } +spl-single-pool = { version = "1.0.0", path="../program", features = [ "no-entrypoint" ] } + +[dev-dependencies] +solana-test-validator = "=1.17.2" +serial_test = "2.0.0" +test-case = "3.2" +tempfile = "3.8.1" + +[[bin]] +name = "spl-single-pool" +path = "src/main.rs" diff --git a/single-pool/cli/src/cli.rs b/single-pool/cli/src/cli.rs new file mode 100644 index 00000000000..4e653562709 --- /dev/null +++ b/single-pool/cli/src/cli.rs @@ -0,0 +1,454 @@ +use { + crate::config::Error, + clap::{ + builder::{PossibleValuesParser, TypedValueParser}, + ArgGroup, ArgMatches, Args, Parser, Subcommand, + }, + solana_clap_v3_utils::{ + input_parsers::{parse_url_or_moniker, Amount}, + input_validators::{is_valid_pubkey, is_valid_signer}, + keypair::{pubkey_from_path, signer_from_path}, + }, + solana_cli_output::OutputFormat, + solana_remote_wallet::remote_wallet::RemoteWalletManager, + solana_sdk::{pubkey::Pubkey, signer::Signer}, + spl_single_pool::{self, find_pool_address}, + std::{rc::Rc, str::FromStr, sync::Arc}, +}; + +#[derive(Clone, Debug, Parser)] +#[clap(author, version, about, long_about = None)] +pub struct Cli { + /// Configuration file to use + #[clap(global(true), short = 'C', long = "config", id = "PATH")] + pub config_file: Option, + + /// Show additional information + #[clap(global(true), short, long)] + pub verbose: bool, + + /// Simulate transaction instead of executing + #[clap(global(true), long, alias = "dryrun")] + pub dry_run: bool, + + /// URL for Solana's JSON RPC or moniker (or their first letter): + /// [mainnet-beta, testnet, devnet, localhost]. + /// Default from the configuration file. + #[clap( + global(true), + short = 'u', + long = "url", + id = "URL_OR_MONIKER", + value_parser = parse_url_or_moniker, + )] + pub json_rpc_url: Option, + + /// Specify the fee-payer account. This may be a keypair file, the ASK keyword + /// or the pubkey of an offline signer, provided an appropriate --signer argument + /// is also passed. Defaults to the client keypair. + #[clap( + global(true), + long, + id = "PAYER_KEYPAIR", + validator = |s| is_valid_signer(s), + )] + pub fee_payer: Option, + + /// Return information in specified output format + #[clap( + global(true), + long = "output", + id = "FORMAT", + conflicts_with = "verbose", + value_parser = PossibleValuesParser::new(["json", "json-compact"]).map(|o| parse_output_format(&o)), + )] + pub output_format: Option, + + #[clap(subcommand)] + pub command: Command, +} + +#[derive(Clone, Debug, Subcommand)] +pub enum Command { + /// Commands used to initialize or manage existing single-validator stake pools. + /// Other than initializing new pools, most users should never need to use these. + Manage(ManageCli), + + /// Deposit delegated stake into a pool in exchange for pool tokens, closing out + /// the original stake account. Provide either a stake account address, or a + /// pool or vote account address along with the --default-stake-account flag to + /// use an account created with create-stake. + Deposit(DepositCli), + + /// Withdraw stake into a new stake account, burning tokens in exchange. + /// Provide either pool or vote account address, plus either an amount of tokens to burn + /// or the ALL keyword to burn all. + Withdraw(WithdrawCli), + + /// Create and delegate a new stake account to a given validator, using a default address + /// linked to the intended depository pool + CreateDefaultStake(CreateStakeCli), + + /// Display info for one or all single-validator stake pool(s) + Display(DisplayCli), +} + +#[derive(Clone, Debug, Parser)] +pub struct ManageCli { + #[clap(subcommand)] + pub manage: ManageCommand, +} + +#[derive(Clone, Debug, Subcommand)] +pub enum ManageCommand { + /// Permissionlessly create the single-validator stake pool for a given validator vote account + /// if one does not already exist. The fee payer also pays rent-exemption for accounts, + /// along with the cluster-configured minimum stake delegation + Initialize(InitializeCli), + + /// Permissionlessly re-stake the pool stake account in the case when it has been deactivated. + /// This may happen if the validator is force-deactivated, and then later reactivated using + /// the same address for its vote account. + ReactivatePoolStake(ReactivateCli), + + /// Permissionlessly create default MPL token metadata for the pool mint. Normally this is done + /// automatically upon initialization, so this does not need to be called. + CreateTokenMetadata(CreateMetadataCli), + + /// Modify the MPL token metadata associated with the pool mint. This action can only be + /// performed by the validator vote account's withdraw authority + UpdateTokenMetadata(UpdateMetadataCli), +} + +#[derive(Clone, Debug, Args)] +pub struct InitializeCli { + /// The vote account to create the pool for + #[clap(value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Pubkey, + + /// Do not create MPL metadata for the pool mint + #[clap(long)] + pub skip_metadata: bool, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group()))] +pub struct ReactivateCli { + /// The pool to reactivate + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to reactivate + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + // backdoor for testing, theres no reason to ever use this + #[clap(long, hide = true)] + pub skip_deactivation_check: bool, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(ArgGroup::new("stake-source").required(true).args(&["stake-account-address", "default-stake-account"])))] +#[clap(group(pool_source_group().required(false)))] +pub struct DepositCli { + /// The stake account to deposit from. Must be in the same activation state as the pool's stake account + #[clap(value_parser = |p: &str| parse_address(p, "stake_account_address"))] + pub stake_account_address: Option, + + /// Instead of using a stake account by address, use the user's default account for a specified pool + #[clap( + short, + long, + conflicts_with = "stake-account-address", + requires = "pool-source" + )] + pub default_stake_account: bool, + + /// The pool to deposit into. Optional when stake account is provided + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to deposit into. Optional when stake account or pool is provided + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + /// Signing authority on the stake account to be deposited. Defaults to the client keypair + #[clap(long = "withdraw-authority", id = "STAKE_WITHDRAW_AUTHORITY_KEYPAIR", validator = |s| is_valid_signer(s))] + pub stake_withdraw_authority: Option, + + /// The token account to mint to. Defaults to the client keypair's associated token account + #[clap(long = "token-account", value_parser = |p: &str| parse_address(p, "token_account_address"))] + pub token_account_address: Option, + + /// The wallet to refund stake account rent to. Defaults to the client keypair's pubkey + #[clap(long = "recipient", value_parser = |p: &str| parse_address(p, "lamport_recipient_address"))] + pub lamport_recipient_address: Option, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group()))] +pub struct WithdrawCli { + /// Amount of tokens to burn for withdrawal + #[clap(value_parser = Amount::parse_decimal_or_all)] + pub token_amount: Amount, + + /// The token account to withdraw from. Defaults to the associated token account for the pool mint + #[clap(long = "token-account", value_parser = |p: &str| parse_address(p, "token_account_address"))] + pub token_account_address: Option, + + /// The pool to withdraw from + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to withdraw from + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + /// Signing authority on the token account. Defaults to the client keypair + #[clap(long = "token-authority", id = "TOKEN_AUTHORITY_KEYPAIR", validator = |s| is_valid_signer(s))] + pub token_authority: Option, + + /// Authority to assign to the new stake account. Defaults to the pubkey of the client keypair + #[clap(long = "stake-authority", value_parser = |p: &str| parse_address(p, "stake_authority_address"))] + pub stake_authority_address: Option, + + /// Deactivate stake account after withdrawal + #[clap(long)] + pub deactivate: bool, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group()))] +pub struct CreateMetadataCli { + /// The pool to create default MPL token metadata for + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to create metadata for + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group()))] +pub struct UpdateMetadataCli { + /// New name for the pool token + #[clap(validator = is_valid_token_name)] + pub token_name: String, + + /// New ticker symbol for the pool token + #[clap(validator = is_valid_token_symbol)] + pub token_symbol: String, + + /// Optional external URI for the pool token. Leaving this argument blank will clear any existing value + #[clap(validator = is_valid_token_uri)] + pub token_uri: Option, + + /// The pool to change MPL token metadata for + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to create metadata for + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + /// Authorized withdrawer for the vote account, to prove validator ownership. Defaults to the client keypair + #[clap(long, id = "AUTHORIZED_WITHDRAWER_KEYPAIR", validator = |s| is_valid_signer(s))] + pub authorized_withdrawer: Option, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group()))] +pub struct CreateStakeCli { + /// Number of lamports to stake + pub lamports: u64, + + /// The pool to create a stake account for + #[clap(short, long = "pool", value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to create stake for + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + /// Authority to assign to the new stake account. Defaults to the pubkey of the client keypair + #[clap(long = "stake-authority", value_parser = |p: &str| parse_address(p, "stake_authority_address"))] + pub stake_authority_address: Option, +} + +#[derive(Clone, Debug, Args)] +#[clap(group(pool_source_group().arg("all")))] +pub struct DisplayCli { + /// The pool to display + #[clap(value_parser = |p: &str| parse_address(p, "pool_address"))] + pub pool_address: Option, + + /// The vote account corresponding to the pool to display + #[clap(long = "vote-account", value_parser = |p: &str| parse_address(p, "vote_account_address"))] + pub vote_account_address: Option, + + /// Display all pools + #[clap(long)] + pub all: bool, +} + +fn pool_source_group() -> ArgGroup<'static> { + ArgGroup::new("pool-source") + .required(true) + .args(&["pool-address", "vote-account-address"]) +} + +pub fn parse_address(path: &str, name: &str) -> Result { + if is_valid_pubkey(path).is_ok() { + // this all is ugly but safe + // wallet_manager doesnt need to be shared, it just saves cycles to cache it + // and the only way argmatches default fails with an unchecked lookup is in the prompt branch + // which seems unlikely to ever be used for pubkeys + // the usb lookup in signer_from_path_with_config is safe + // and the pubkey lookups are unreachable because pubkey_from_path short circuits that case + let mut wallet_manager = None; + pubkey_from_path(&ArgMatches::default(), path, name, &mut wallet_manager) + .map_err(|_| format!("Failed to load pubkey {} at {}", name, path)) + } else { + Err(format!("Failed to parse pubkey {} at {}", name, path)) + } +} + +pub fn parse_output_format(output_format: &str) -> OutputFormat { + match output_format { + "json" => OutputFormat::Json, + "json-compact" => OutputFormat::JsonCompact, + _ => unreachable!(), + } +} + +pub fn is_valid_token_name(s: &str) -> Result<(), String> { + if s.len() > 32 { + Err("Maximum token name length is 32 characters".to_string()) + } else { + Ok(()) + } +} + +pub fn is_valid_token_symbol(s: &str) -> Result<(), String> { + if s.len() > 10 { + Err("Maximum token symbol length is 10 characters".to_string()) + } else { + Ok(()) + } +} + +pub fn is_valid_token_uri(s: &str) -> Result<(), String> { + if s.len() > 200 { + Err("Maximum token URI length is 200 characters".to_string()) + } else { + Ok(()) + } +} + +pub fn pool_address_from_args(maybe_pool: Option, maybe_vote: Option) -> Pubkey { + if let Some(pool_address) = maybe_pool { + pool_address + } else if let Some(vote_account_address) = maybe_vote { + find_pool_address(&spl_single_pool::id(), &vote_account_address) + } else { + unreachable!() + } +} + +// all this is because solana clap v3 utils signer handlers dont work with derive syntax +// which means its impossible to parse keypairs or addresses in value_parser +// instead, we take the input into a string wrapper from the cli +// and then once the first pass is over, we do a second manual pass converting to signer wrappers +#[derive(Clone, Debug)] +pub enum SignerArg { + Source(String), + Signer(Arc), +} +impl FromStr for SignerArg { + type Err = std::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self::Source(s.to_string())) + } +} +impl PartialEq for SignerArg { + fn eq(&self, other: &SignerArg) -> bool { + match (self, other) { + (SignerArg::Source(ref a), SignerArg::Source(ref b)) => a == b, + (SignerArg::Signer(ref a), SignerArg::Signer(ref b)) => a == b, + (_, _) => false, + } + } +} + +pub fn signer_from_arg( + signer_arg: Option, + default_signer: &Arc, +) -> Result, Error> { + match signer_arg { + Some(SignerArg::Signer(signer)) => Ok(signer), + Some(SignerArg::Source(_)) => Err("Signer arg string must be converted to signer".into()), + None => Ok(default_signer.clone()), + } +} + +impl Command { + pub fn with_signers( + mut self, + matches: &ArgMatches, + wallet_manager: &mut Option>, + ) -> Result { + match self { + Command::Deposit(ref mut config) => { + config.stake_withdraw_authority = with_signer( + matches, + wallet_manager, + config.stake_withdraw_authority.clone(), + "stake_authority", + )?; + } + Command::Withdraw(ref mut config) => { + config.token_authority = with_signer( + matches, + wallet_manager, + config.token_authority.clone(), + "token_authority", + )?; + } + Command::Manage(ManageCli { + manage: ManageCommand::UpdateTokenMetadata(ref mut config), + }) => { + config.authorized_withdrawer = with_signer( + matches, + wallet_manager, + config.authorized_withdrawer.clone(), + "authorized_withdrawer", + )?; + } + _ => (), + } + + Ok(self) + } +} + +pub fn with_signer( + matches: &ArgMatches, + wallet_manager: &mut Option>, + arg: Option, + name: &str, +) -> Result, Error> { + Ok(match arg { + Some(SignerArg::Source(path)) => { + let signer = if let Ok(signer) = signer_from_path(matches, &path, name, wallet_manager) + { + signer + } else { + return Err(format!("Cannot parse signer {} / {}", name, path).into()); + }; + Some(SignerArg::Signer(Arc::from(signer))) + } + a => a, + }) +} diff --git a/single-pool/cli/src/config.rs b/single-pool/cli/src/config.rs new file mode 100644 index 00000000000..ca42dcf0e75 --- /dev/null +++ b/single-pool/cli/src/config.rs @@ -0,0 +1,130 @@ +use { + clap::ArgMatches, + solana_clap_v3_utils::keypair::signer_from_path, + solana_cli_output::OutputFormat, + solana_client::nonblocking::rpc_client::RpcClient, + solana_remote_wallet::remote_wallet::RemoteWalletManager, + solana_sdk::{commitment_config::CommitmentConfig, signature::Signer}, + spl_token_client::client::{ProgramClient, ProgramRpcClient, ProgramRpcClientSendTransaction}, + std::{process::exit, rc::Rc, sync::Arc}, +}; + +use crate::cli::*; + +pub type Error = Box; + +pub fn println_display(config: &Config, message: String) { + match config.output_format { + OutputFormat::Display | OutputFormat::DisplayVerbose => { + println!("{}", message); + } + _ => {} + } +} + +pub fn eprintln_display(config: &Config, message: String) { + match config.output_format { + OutputFormat::Display | OutputFormat::DisplayVerbose => { + eprintln!("{}", message); + } + _ => {} + } +} + +pub struct Config { + pub rpc_client: Arc, + pub program_client: Arc>, + pub default_signer: Option>, + pub fee_payer: Option>, + pub output_format: OutputFormat, + pub dry_run: bool, +} +impl Config { + pub fn new( + cli: Cli, + matches: ArgMatches, + wallet_manager: &mut Option>, + ) -> Self { + // get the generic cli config struct + let cli_config = if let Some(config_file) = &cli.config_file { + solana_cli_config::Config::load(config_file).unwrap_or_else(|_| { + eprintln!("error: Could not load config file `{}`", config_file); + exit(1); + }) + } else if let Some(config_file) = &*solana_cli_config::CONFIG_FILE { + solana_cli_config::Config::load(config_file).unwrap_or_default() + } else { + solana_cli_config::Config::default() + }; + + // create rpc client + let rpc_client = Arc::new(RpcClient::new_with_commitment( + cli.json_rpc_url.unwrap_or(cli_config.json_rpc_url), + CommitmentConfig::confirmed(), + )); + + // and program client + let program_client = Arc::new(ProgramRpcClient::new( + rpc_client.clone(), + ProgramRpcClientSendTransaction, + )); + + // resolve default signer + let default_keypair = cli_config.keypair_path; + let default_signer = + signer_from_path(&matches, &default_keypair, "default", wallet_manager) + .ok() + .map(Arc::from); + + // resolve fee-payer + let fee_payer_arg = + with_signer(&matches, wallet_manager, cli.fee_payer, "fee_payer").unwrap(); + let fee_payer = default_signer + .clone() + .map(|default_signer| signer_from_arg(fee_payer_arg, &default_signer).unwrap()); + + // determine output format + let output_format = match (cli.output_format, cli.verbose) { + (Some(json_format), _) => json_format, + (None, true) => OutputFormat::DisplayVerbose, + (None, false) => OutputFormat::Display, + }; + + Self { + rpc_client, + program_client, + default_signer, + fee_payer, + output_format, + dry_run: cli.dry_run, + } + } + + // Returns Ok(default signer), or Err if there is no default signer configured + pub fn default_signer(&self) -> Result, Error> { + if let Some(default_signer) = &self.default_signer { + Ok(default_signer.clone()) + } else { + Err("default signer is required, please specify a valid default signer by identifying a \ + valid configuration file using the --config argument, or by creating a valid config \ + at the default location of ~/.config/solana/cli/config.yml using the solana config \ + command".to_string().into()) + } + } + + // Returns Ok(fee payer), or Err if there is no fee payer configured + pub fn fee_payer(&self) -> Result, Error> { + if let Some(fee_payer) = &self.fee_payer { + Ok(fee_payer.clone()) + } else { + Err("fee payer is required, please specify a valid fee payer using the --payer argument, or \ + by identifying a valid configuration file using the --config argument, or by creating a \ + valid config at the default location of ~/.config/solana/cli/config.yml using the solana \ + config command".to_string().into()) + } + } + + pub fn verbose(&self) -> bool { + self.output_format == OutputFormat::DisplayVerbose + } +} diff --git a/single-pool/cli/src/main.rs b/single-pool/cli/src/main.rs new file mode 100644 index 00000000000..c46fa2fe77a --- /dev/null +++ b/single-pool/cli/src/main.rs @@ -0,0 +1,875 @@ +#![allow(clippy::arithmetic_side_effects)] + +use { + borsh::BorshSerialize, + clap::{CommandFactory, Parser}, + solana_clap_v3_utils::input_parsers::Amount, + solana_client::{ + rpc_config::RpcProgramAccountsConfig, + rpc_filter::{Memcmp, RpcFilterType}, + }, + solana_sdk::{ + borsh0_10::try_from_slice_unchecked, + pubkey::Pubkey, + signature::Signature, + signature::{Keypair, Signer}, + stake, + transaction::Transaction, + }, + solana_vote_program::{self as vote_program, vote_state::VoteState}, + spl_single_pool::{ + self, find_default_deposit_account_address, find_pool_address, find_pool_mint_address, + find_pool_stake_address, instruction::SinglePoolInstruction, state::SinglePool, + }, + spl_token_client::token::Token, +}; + +mod config; +use config::*; + +mod cli; +use cli::*; + +mod output; +use output::*; + +mod quarantine; + +#[tokio::main] +async fn main() -> Result<(), Error> { + let cli = Cli::parse(); + let matches = Cli::command().get_matches(); + let mut wallet_manager = None; + + let command = cli + .command + .clone() + .with_signers(&matches, &mut wallet_manager)?; + let config = Config::new(cli, matches, &mut wallet_manager); + + solana_logger::setup_with_default("solana=info"); + + let res = command.execute(&config).await?; + println!("{}", res); + + Ok(()) +} + +pub type CommandResult = Result; + +impl Command { + pub async fn execute(self, config: &Config) -> CommandResult { + match self { + Command::Manage(command) => match command.manage { + ManageCommand::Initialize(command_config) => { + command_initialize(config, command_config).await + } + ManageCommand::ReactivatePoolStake(command_config) => { + command_reactivate_pool_stake(config, command_config).await + } + ManageCommand::CreateTokenMetadata(command_config) => { + command_create_metadata(config, command_config).await + } + ManageCommand::UpdateTokenMetadata(command_config) => { + command_update_metadata(config, command_config).await + } + }, + Command::Deposit(command_config) => command_deposit(config, command_config).await, + Command::Withdraw(command_config) => command_withdraw(config, command_config).await, + Command::CreateDefaultStake(command_config) => { + command_create_stake(config, command_config).await + } + Command::Display(command_config) => command_display(config, command_config).await, + } + } +} + +// initialize a new stake pool for a vote account +async fn command_initialize(config: &Config, command_config: InitializeCli) -> CommandResult { + let payer = config.fee_payer()?; + let vote_account_address = command_config.vote_account_address; + + println_display( + config, + format!( + "Initializing single-validator stake pool for vote account {}\n", + vote_account_address, + ), + ); + + // check if the vote account is valid + let vote_account = config + .program_client + .get_account(vote_account_address) + .await?; + if vote_account.is_none() || vote_account.unwrap().owner != vote_program::id() { + return Err(format!("{} is not a valid vote account", vote_account_address,).into()); + } + + let pool_address = find_pool_address(&spl_single_pool::id(), &vote_account_address); + + // check if the pool has already been initialized + if config + .program_client + .get_account(pool_address) + .await? + .is_some() + { + return Err(format!( + "Pool {} for vote account {} already exists", + pool_address, vote_account_address + ) + .into()); + } + + let mut instructions = spl_single_pool::instruction::initialize( + &spl_single_pool::id(), + &vote_account_address, + &payer.pubkey(), + &quarantine::get_rent(config).await?, + quarantine::get_minimum_delegation(config).await?, + ); + + // get rid of the CreateMetadata instruction if desired, eg if mpl breaks compat + if command_config.skip_metadata { + assert_eq!( + instructions.last().unwrap().data, + SinglePoolInstruction::CreateTokenMetadata + .try_to_vec() + .unwrap() + ); + + instructions.pop(); + } + + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &vec![payer], + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + + Ok(format_output( + config, + "Initialize".to_string(), + StakePoolOutput { + pool_address, + vote_account_address, + available_stake: 0, + token_supply: 0, + signature, + }, + )) +} + +// reactivate pool stake account +async fn command_reactivate_pool_stake( + config: &Config, + command_config: ReactivateCli, +) -> CommandResult { + let payer = config.fee_payer()?; + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + println_display( + config, + format!("Reactivating stake account for pool {}\n", pool_address), + ); + + let vote_account_address = + if let Some(pool_data) = config.program_client.get_account(pool_address).await? { + try_from_slice_unchecked::(&pool_data.data)?.vote_account_address + } else { + return Err(format!("Pool {} has not been initialized", pool_address).into()); + }; + + // the only reason this check is skippable is for testing, otherwise theres no reason + if !command_config.skip_deactivation_check { + let current_epoch = config.rpc_client.get_epoch_info().await?.epoch; + let pool_stake_address = find_pool_stake_address(&spl_single_pool::id(), &pool_address); + let pool_stake_deactivated = quarantine::get_stake_info(config, &pool_stake_address) + .await? + .unwrap() + .1 + .delegation + .deactivation_epoch + <= current_epoch; + + if !pool_stake_deactivated { + return Err("Pool stake account is neither deactivating nor deactivated".into()); + } + } + + let instruction = spl_single_pool::instruction::reactivate_pool_stake( + &spl_single_pool::id(), + &vote_account_address, + ); + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer.pubkey()), + &vec![payer], + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + + Ok(format_output( + config, + "ReactivatePoolStake".to_string(), + SignatureOutput { signature }, + )) +} + +// deposit stake +async fn command_deposit(config: &Config, command_config: DepositCli) -> CommandResult { + let payer = config.fee_payer()?; + let owner = config.default_signer()?; + let stake_authority = signer_from_arg(command_config.stake_withdraw_authority, &owner)?; + let lamport_recipient = command_config + .lamport_recipient_address + .unwrap_or_else(|| owner.pubkey()); + + let current_epoch = config.rpc_client.get_epoch_info().await?.epoch; + + // the cli invocation for this is conceptually simple, but a bit tricky + // the user can provide pool or vote and let the cli infer the stake account address + // but they can also provide pool or vote with the stake account, as a safety check + // first we want to get the pool address if they provided a pool or vote address + let provided_pool_address = command_config.pool_address.or_else(|| { + command_config + .vote_account_address + .map(|address| find_pool_address(&spl_single_pool::id(), &address)) + }); + + // from there we can determine the stake account address + let stake_account_address = + if let Some(stake_account_address) = command_config.stake_account_address { + stake_account_address + } else if let Some(pool_address) = provided_pool_address { + assert!(command_config.default_stake_account); + find_default_deposit_account_address(&pool_address, &stake_authority.pubkey()) + } else { + unreachable!() + }; + + // now we validate the stake account and definitively resolve the pool address + let (pool_address, user_stake_active) = if let Some((meta, stake)) = + quarantine::get_stake_info(config, &stake_account_address).await? + { + let derived_pool_address = + find_pool_address(&spl_single_pool::id(), &stake.delegation.voter_pubkey); + + if let Some(provided_pool_address) = provided_pool_address { + if provided_pool_address != derived_pool_address { + return Err(format!( + "Provided pool address {} does not match stake account-derived address {}", + provided_pool_address, derived_pool_address, + ) + .into()); + } + } + + if meta.authorized.withdrawer != stake_authority.pubkey() { + return Err(format!( + "Incorrect withdraw authority for stake account {}: got {}, expected {}", + stake_account_address, + meta.authorized.withdrawer, + stake_authority.pubkey(), + ) + .into()); + } + + if stake.delegation.deactivation_epoch < std::u64::MAX { + return Err(format!( + "Stake account {} is deactivating or deactivated", + stake_account_address + ) + .into()); + } + + ( + derived_pool_address, + stake.delegation.activation_epoch <= current_epoch, + ) + } else { + return Err(format!("Could not find stake account {}", stake_account_address).into()); + }; + + println_display( + config, + format!( + "Depositing stake from account {} into pool {}\n", + stake_account_address, pool_address + ), + ); + + if config + .program_client + .get_account(pool_address) + .await? + .is_none() + { + return Err(format!("Pool {} has not been initialized", pool_address).into()); + } + + let pool_stake_address = find_pool_stake_address(&spl_single_pool::id(), &pool_address); + let pool_stake_active = quarantine::get_stake_info(config, &pool_stake_address) + .await? + .unwrap() + .1 + .delegation + .activation_epoch + <= current_epoch; + + if user_stake_active != pool_stake_active { + return Err("Activation status mismatch; try again next epoch".into()); + } + + let pool_mint_address = find_pool_mint_address(&spl_single_pool::id(), &pool_address); + let token = Token::new( + config.program_client.clone(), + &spl_token::id(), + &pool_mint_address, + None, + payer.clone(), + ); + + // use token account provided, or get/create the associated account for the client keypair + let token_account_address = if let Some(account) = command_config.token_account_address { + account + } else { + token + .get_or_create_associated_account_info(&owner.pubkey()) + .await?; + token.get_associated_token_address(&owner.pubkey()) + }; + + let previous_token_amount = token + .get_account_info(&token_account_address) + .await? + .base + .amount; + + let instructions = spl_single_pool::instruction::deposit( + &spl_single_pool::id(), + &pool_address, + &stake_account_address, + &token_account_address, + &lamport_recipient, + &stake_authority.pubkey(), + ); + + let mut signers = vec![]; + for signer in [payer.clone(), stake_authority] { + if !signers.contains(&signer) { + signers.push(signer); + } + } + + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &signers, + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + let token_amount = token + .get_account_info(&token_account_address) + .await? + .base + .amount + - previous_token_amount; + + Ok(format_output( + config, + "Deposit".to_string(), + DepositOutput { + pool_address, + token_amount, + signature, + }, + )) +} + +// withdraw stake +async fn command_withdraw(config: &Config, command_config: WithdrawCli) -> CommandResult { + let payer = config.fee_payer()?; + let owner = config.default_signer()?; + let token_authority = signer_from_arg(command_config.token_authority, &owner)?; + let stake_authority_address = command_config + .stake_authority_address + .unwrap_or_else(|| owner.pubkey()); + + let stake_account = Keypair::new(); + let stake_account_address = stake_account.pubkey(); + + // since we cant infer pool from token account, the withdraw invocation is rather simpler + // first get the pool address + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + if config + .program_client + .get_account(pool_address) + .await? + .is_none() + { + return Err(format!("Pool {} has not been initialized", pool_address).into()); + } + + // now all the mint and token info + let pool_mint_address = find_pool_mint_address(&spl_single_pool::id(), &pool_address); + let token = Token::new( + config.program_client.clone(), + &spl_token::id(), + &pool_mint_address, + None, + payer.clone(), + ); + + let token_account_address = command_config + .token_account_address + .unwrap_or_else(|| token.get_associated_token_address(&owner.pubkey())); + + let token_account = token.get_account_info(&token_account_address).await?; + + let token_amount = match command_config.token_amount.sol_to_lamport() { + Amount::All => token_account.base.amount, + Amount::Raw(amount) => amount, + Amount::Decimal(_) => unreachable!(), + }; + + println_display( + config, + format!( + "Withdrawing from pool {} into new stake account {}; burning {} tokens from {}\n", + pool_address, stake_account_address, token_amount, token_account_address, + ), + ); + + if token_amount == 0 { + return Err("Cannot withdraw zero tokens".into()); + } + + if token_amount > token_account.base.amount { + return Err(format!( + "Withdraw amount {} exceeds tokens in account ({})", + token_amount, token_account.base.amount + ) + .into()); + } + + // note a delegate authority is not allowed here because we must authorize the pool authority + if token_account.base.owner != token_authority.pubkey() { + return Err(format!( + "Invalid token authority: got {}, actual {}", + token_account.base.owner, + token_authority.pubkey() + ) + .into()); + } + + // create a blank stake account to withdraw into + let mut instructions = vec![ + quarantine::create_uninitialized_stake_account_instruction( + config, + &payer.pubkey(), + &stake_account_address, + ) + .await?, + ]; + + // perform the withdrawal + instructions.extend(spl_single_pool::instruction::withdraw( + &spl_single_pool::id(), + &pool_address, + &stake_account_address, + &stake_authority_address, + &token_account_address, + &token_authority.pubkey(), + token_amount, + )); + + // possibly deactivate the new stake account + if command_config.deactivate { + instructions.push(stake::instruction::deactivate_stake( + &stake_account_address, + &stake_authority_address, + )); + } + + let mut signers = vec![]; + for signer in [payer.as_ref(), token_authority.as_ref(), &stake_account] { + if !signers.contains(&signer) { + signers.push(signer); + } + } + + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &signers, + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + let stake_amount = if let Some((_, stake)) = + quarantine::get_stake_info(config, &stake_account_address).await? + { + stake.delegation.stake + } else { + 0 + }; + + Ok(format_output( + config, + "Withdraw".to_string(), + WithdrawOutput { + pool_address, + stake_account_address, + stake_amount, + signature, + }, + )) +} + +// create token metadata +async fn command_create_metadata( + config: &Config, + command_config: CreateMetadataCli, +) -> CommandResult { + let payer = config.fee_payer()?; + + // first get the pool address + // i dont check metadata because i dont want to get entangled with mpl + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + println_display( + config, + format!( + "Creating default token metadata for pool {}\n", + pool_address + ), + ); + + if config + .program_client + .get_account(pool_address) + .await? + .is_none() + { + return Err(format!("Pool {} has not been initialized", pool_address).into()); + } + + // and... i guess thats it? + + let instruction = spl_single_pool::instruction::create_token_metadata( + &spl_single_pool::id(), + &pool_address, + &payer.pubkey(), + ); + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer.pubkey()), + &vec![payer], + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + + Ok(format_output( + config, + "CreateTokenMetadata".to_string(), + SignatureOutput { signature }, + )) +} + +// update token metadata +async fn command_update_metadata( + config: &Config, + command_config: UpdateMetadataCli, +) -> CommandResult { + let payer = config.fee_payer()?; + let owner = config.default_signer()?; + let authorized_withdrawer = signer_from_arg(command_config.authorized_withdrawer, &owner)?; + + // first get the pool address + // i dont check metadata because i dont want to get entangled with mpl + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + println_display( + config, + format!("Updating token metadata for pool {}\n", pool_address), + ); + + // we always need the vote account + let vote_account_address = + if let Some(pool_data) = config.program_client.get_account(pool_address).await? { + try_from_slice_unchecked::(&pool_data.data)?.vote_account_address + } else { + return Err(format!("Pool {} has not been initialized", pool_address).into()); + }; + + if let Some(vote_account_data) = config + .program_client + .get_account(vote_account_address) + .await? + { + let vote_account = VoteState::deserialize(&vote_account_data.data)?; + + if authorized_withdrawer.pubkey() != vote_account.authorized_withdrawer { + return Err(format!( + "Invalid authorized withdrawer: got {}, actual {}", + authorized_withdrawer.pubkey(), + vote_account.authorized_withdrawer, + ) + .into()); + } + } else { + // we know the pool exists so the vote account must exist + unreachable!(); + } + + let instruction = spl_single_pool::instruction::update_token_metadata( + &spl_single_pool::id(), + &vote_account_address, + &authorized_withdrawer.pubkey(), + command_config.token_name, + command_config.token_symbol, + command_config.token_uri.unwrap_or_default(), + ); + + let mut signers = vec![]; + for signer in [payer.clone(), authorized_withdrawer] { + if !signers.contains(&signer) { + signers.push(signer); + } + } + + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer.pubkey()), + &signers, + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + + Ok(format_output( + config, + "UpdateTokenMetadata".to_string(), + SignatureOutput { signature }, + )) +} + +// create default stake account +async fn command_create_stake(config: &Config, command_config: CreateStakeCli) -> CommandResult { + let payer = config.fee_payer()?; + let owner = config.default_signer()?; + let stake_authority_address = command_config + .stake_authority_address + .unwrap_or_else(|| owner.pubkey()); + + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + println_display( + config, + format!("Creating default stake account for pool {}\n", pool_address), + ); + + let vote_account_address = + if let Some(vote_account_address) = command_config.vote_account_address { + vote_account_address + } else if let Some(pool_data) = config.program_client.get_account(pool_address).await? { + try_from_slice_unchecked::(&pool_data.data)?.vote_account_address + } else { + return Err(format!( + "Cannot determine vote account address from uninitialized pool {}", + pool_address, + ) + .into()); + }; + + if command_config.vote_account_address.is_some() + && config + .program_client + .get_account(pool_address) + .await? + .is_none() + { + eprintln_display( + config, + format!("warning: Pool {} has not been initialized", pool_address), + ); + } + + let instructions = spl_single_pool::instruction::create_and_delegate_user_stake( + &spl_single_pool::id(), + &vote_account_address, + &stake_authority_address, + &quarantine::get_rent(config).await?, + command_config.lamports, + ); + + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &vec![payer], + config.program_client.get_latest_blockhash().await?, + ); + + let signature = process_transaction(config, transaction).await?; + + Ok(format_output( + config, + "CreateDefaultStake".to_string(), + CreateStakeOutput { + pool_address, + stake_account_address: find_default_deposit_account_address( + &pool_address, + &stake_authority_address, + ), + signature, + }, + )) +} + +// display stake pool(s) +async fn command_display(config: &Config, command_config: DisplayCli) -> CommandResult { + if command_config.all { + // the filter isnt necessary now but makes the cli forward-compatible + let pools = config + .rpc_client + .get_program_accounts_with_config( + &spl_single_pool::id(), + RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 0, + vec![1], + ))]), + ..RpcProgramAccountsConfig::default() + }, + ) + .await?; + + let mut displays = vec![]; + for pool in pools { + let vote_account_address = + try_from_slice_unchecked::(&pool.1.data)?.vote_account_address; + displays.push(get_pool_display(config, pool.0, Some(vote_account_address)).await?); + } + + Ok(format_output( + config, + "DisplayAll".to_string(), + StakePoolListOutput(displays), + )) + } else { + let pool_address = pool_address_from_args( + command_config.pool_address, + command_config.vote_account_address, + ); + + Ok(format_output( + config, + "Display".to_string(), + get_pool_display(config, pool_address, None).await?, + )) + } +} + +async fn get_pool_display( + config: &Config, + pool_address: Pubkey, + maybe_vote_account: Option, +) -> Result { + let vote_account_address = if let Some(address) = maybe_vote_account { + address + } else if let Some(pool_data) = config.program_client.get_account(pool_address).await? { + if let Ok(data) = try_from_slice_unchecked::(&pool_data.data) { + data.vote_account_address + } else { + return Err(format!( + "Failed to parse account at {}; is this a pool?", + pool_address + ) + .into()); + } + } else { + return Err(format!("Pool {} does not exist", pool_address).into()); + }; + + let pool_stake_address = find_pool_stake_address(&spl_single_pool::id(), &pool_address); + let available_stake = + if let Some((_, stake)) = quarantine::get_stake_info(config, &pool_stake_address).await? { + stake.delegation.stake - quarantine::get_minimum_delegation(config).await? + } else { + unreachable!() + }; + + let pool_mint_address = find_pool_mint_address(&spl_single_pool::id(), &pool_address); + let token_supply = config + .rpc_client + .get_token_supply(&pool_mint_address) + .await? + .amount + .parse::()?; + + Ok(StakePoolOutput { + pool_address, + vote_account_address, + available_stake, + token_supply, + signature: None, + }) +} + +async fn process_transaction( + config: &Config, + transaction: Transaction, +) -> Result, Error> { + if config.dry_run { + let simulation_data = config.rpc_client.simulate_transaction(&transaction).await?; + + if config.verbose() { + if let Some(logs) = simulation_data.value.logs { + for log in logs { + println!(" {}", log); + } + } + + println!( + "\nSimulation succeeded, consumed {} compute units", + simulation_data.value.units_consumed.unwrap() + ); + } else { + println_display(config, "Simulation succeeded".to_string()); + } + + Ok(None) + } else { + Ok(Some( + config + .rpc_client + .send_and_confirm_transaction_with_spinner(&transaction) + .await?, + )) + } +} diff --git a/single-pool/cli/src/output.rs b/single-pool/cli/src/output.rs new file mode 100644 index 00000000000..060986542a5 --- /dev/null +++ b/single-pool/cli/src/output.rs @@ -0,0 +1,307 @@ +use { + crate::config::Config, + console::style, + serde::{Deserialize, Serialize}, + serde_with::{serde_as, DisplayFromStr}, + solana_cli_output::{display::writeln_name_value, QuietDisplay, VerboseDisplay}, + solana_sdk::{pubkey::Pubkey, signature::Signature}, + spl_single_pool::{ + self, find_pool_mint_address, find_pool_mint_authority_address, + find_pool_mpl_authority_address, find_pool_stake_address, + find_pool_stake_authority_address, + }, + std::fmt::{Display, Formatter, Result, Write}, +}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct CommandOutput +where + T: Serialize + Display + QuietDisplay + VerboseDisplay, +{ + pub(crate) command_name: String, + pub(crate) command_output: T, +} + +impl Display for CommandOutput +where + T: Serialize + Display + QuietDisplay + VerboseDisplay, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.command_output, f) + } +} + +impl QuietDisplay for CommandOutput +where + T: Serialize + Display + QuietDisplay + VerboseDisplay, +{ + fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result { + QuietDisplay::write_str(&self.command_output, w) + } +} + +impl VerboseDisplay for CommandOutput +where + T: Serialize + Display + QuietDisplay + VerboseDisplay, +{ + fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result { + writeln_name_value(w, "Command:", &self.command_name)?; + VerboseDisplay::write_str(&self.command_output, w) + } +} + +pub fn format_output(config: &Config, command_name: String, command_output: T) -> String +where + T: Serialize + Display + QuietDisplay + VerboseDisplay, +{ + config.output_format.formatted_string(&CommandOutput { + command_name, + command_output, + }) +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SignatureOutput { + #[serde_as(as = "Option")] + pub signature: Option, +} + +impl QuietDisplay for SignatureOutput {} +impl VerboseDisplay for SignatureOutput {} + +impl Display for SignatureOutput { + fn fmt(&self, f: &mut Formatter) -> Result { + writeln!(f)?; + + if let Some(signature) = self.signature { + writeln_name_value(f, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StakePoolOutput { + #[serde_as(as = "DisplayFromStr")] + pub pool_address: Pubkey, + #[serde_as(as = "DisplayFromStr")] + pub vote_account_address: Pubkey, + pub available_stake: u64, + pub token_supply: u64, + #[serde_as(as = "Option")] + pub signature: Option, +} + +impl QuietDisplay for StakePoolOutput {} +impl VerboseDisplay for StakePoolOutput { + fn write_str(&self, w: &mut dyn Write) -> Result { + writeln!(w)?; + writeln!(w, "{}", style("SPL Single-Validator Stake Pool").bold())?; + writeln_name_value(w, " Pool address:", &self.pool_address.to_string())?; + writeln_name_value( + w, + " Vote account address:", + &self.vote_account_address.to_string(), + )?; + + writeln_name_value( + w, + " Pool stake address:", + &find_pool_stake_address(&spl_single_pool::id(), &self.pool_address).to_string(), + )?; + writeln_name_value( + w, + " Pool mint address:", + &find_pool_mint_address(&spl_single_pool::id(), &self.pool_address).to_string(), + )?; + writeln_name_value( + w, + " Pool stake authority address:", + &find_pool_stake_authority_address(&spl_single_pool::id(), &self.pool_address) + .to_string(), + )?; + writeln_name_value( + w, + " Pool mint authority address:", + &find_pool_mint_authority_address(&spl_single_pool::id(), &self.pool_address) + .to_string(), + )?; + writeln_name_value( + w, + " Pool MPL authority address:", + &find_pool_mpl_authority_address(&spl_single_pool::id(), &self.pool_address) + .to_string(), + )?; + + writeln_name_value(w, " Available stake:", &self.available_stake.to_string())?; + writeln_name_value(w, " Token supply:", &self.token_supply.to_string())?; + + if let Some(signature) = self.signature { + writeln!(w)?; + writeln_name_value(w, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} + +impl Display for StakePoolOutput { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + writeln!(f)?; + writeln!(f, "{}", style("SPL Single-Validator Stake Pool").bold())?; + writeln_name_value(f, " Pool address:", &self.pool_address.to_string())?; + writeln_name_value( + f, + " Vote account address:", + &self.vote_account_address.to_string(), + )?; + writeln_name_value(f, " Available stake:", &self.available_stake.to_string())?; + writeln_name_value(f, " Token supply:", &self.token_supply.to_string())?; + + if let Some(signature) = self.signature { + writeln!(f)?; + writeln_name_value(f, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct StakePoolListOutput(pub Vec); + +impl QuietDisplay for StakePoolListOutput {} +impl VerboseDisplay for StakePoolListOutput { + fn write_str(&self, w: &mut dyn Write) -> Result { + let mut stake = 0; + for svsp in &self.0 { + VerboseDisplay::write_str(svsp, w)?; + stake += svsp.available_stake; + } + + writeln!(w)?; + writeln_name_value(w, "Total stake:", &stake.to_string())?; + + Ok(()) + } +} + +impl Display for StakePoolListOutput { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut stake = 0; + for svsp in &self.0 { + svsp.fmt(f)?; + stake += svsp.available_stake; + } + + writeln!(f)?; + writeln_name_value(f, "Total stake:", &stake.to_string())?; + + Ok(()) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DepositOutput { + #[serde_as(as = "DisplayFromStr")] + pub pool_address: Pubkey, + pub token_amount: u64, + #[serde_as(as = "Option")] + pub signature: Option, +} + +impl QuietDisplay for DepositOutput {} +impl VerboseDisplay for DepositOutput {} + +impl Display for DepositOutput { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + writeln!(f)?; + writeln_name_value(f, "Pool address:", &self.pool_address.to_string())?; + writeln_name_value(f, "Token amount:", &self.token_amount.to_string())?; + + if let Some(signature) = self.signature { + writeln!(f)?; + writeln_name_value(f, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WithdrawOutput { + #[serde_as(as = "DisplayFromStr")] + pub pool_address: Pubkey, + #[serde_as(as = "DisplayFromStr")] + pub stake_account_address: Pubkey, + pub stake_amount: u64, + #[serde_as(as = "Option")] + pub signature: Option, +} + +impl QuietDisplay for WithdrawOutput {} +impl VerboseDisplay for WithdrawOutput {} + +impl Display for WithdrawOutput { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + writeln!(f)?; + writeln_name_value(f, "Pool address:", &self.pool_address.to_string())?; + writeln_name_value( + f, + "Stake account address:", + &self.stake_account_address.to_string(), + )?; + writeln_name_value(f, "Stake amount:", &self.stake_amount.to_string())?; + + if let Some(signature) = self.signature { + writeln!(f)?; + writeln_name_value(f, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateStakeOutput { + #[serde_as(as = "DisplayFromStr")] + pub pool_address: Pubkey, + #[serde_as(as = "DisplayFromStr")] + pub stake_account_address: Pubkey, + #[serde_as(as = "Option")] + pub signature: Option, +} + +impl QuietDisplay for CreateStakeOutput {} +impl VerboseDisplay for CreateStakeOutput {} + +impl Display for CreateStakeOutput { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + writeln!(f)?; + writeln_name_value(f, "Pool address:", &self.pool_address.to_string())?; + writeln_name_value( + f, + "Stake account address:", + &self.stake_account_address.to_string(), + )?; + + if let Some(signature) = self.signature { + writeln!(f)?; + writeln_name_value(f, "Signature:", &signature.to_string())?; + } + + Ok(()) + } +} diff --git a/single-pool/cli/src/quarantine.rs b/single-pool/cli/src/quarantine.rs new file mode 100644 index 00000000000..3e79f1ca12b --- /dev/null +++ b/single-pool/cli/src/quarantine.rs @@ -0,0 +1,76 @@ +// XXX this file will be deleted and replaced with a stake program client once i write one + +use solana_sdk::{ + instruction::Instruction, + native_token::LAMPORTS_PER_SOL, + pubkey::Pubkey, + stake::{ + self, + state::{Meta, Stake, StakeStateV2}, + }, + system_instruction, + sysvar::{self, rent::Rent}, +}; + +use crate::config::*; + +pub async fn get_rent(config: &Config) -> Result { + let rent_data = config + .program_client + .get_account(sysvar::rent::id()) + .await? + .unwrap(); + let rent = bincode::deserialize::(&rent_data.data)?; + + Ok(rent) +} + +pub async fn get_minimum_delegation(config: &Config) -> Result { + Ok(std::cmp::max( + config.rpc_client.get_stake_minimum_delegation().await?, + LAMPORTS_PER_SOL, + )) +} + +pub async fn get_stake_info( + config: &Config, + stake_account_address: &Pubkey, +) -> Result, Error> { + if let Some(stake_account) = config + .program_client + .get_account(*stake_account_address) + .await? + { + match bincode::deserialize::(&stake_account.data)? { + StakeStateV2::Stake(meta, stake, _) => Ok(Some((meta, stake))), + StakeStateV2::Initialized(_) => { + Err(format!("Stake account {} is undelegated", stake_account_address).into()) + } + StakeStateV2::Uninitialized => { + Err(format!("Stake account {} is uninitialized", stake_account_address).into()) + } + StakeStateV2::RewardsPool => unimplemented!(), + } + } else { + Ok(None) + } +} + +pub async fn create_uninitialized_stake_account_instruction( + config: &Config, + payer: &Pubkey, + stake_account: &Pubkey, +) -> Result { + let rent_amount = config + .program_client + .get_minimum_balance_for_rent_exemption(std::mem::size_of::()) + .await?; + + Ok(system_instruction::create_account( + payer, + stake_account, + rent_amount, + std::mem::size_of::() as u64, + &stake::program::id(), + )) +} diff --git a/single-pool/cli/tests/test.rs b/single-pool/cli/tests/test.rs new file mode 100644 index 00000000000..bd25bf0d0a9 --- /dev/null +++ b/single-pool/cli/tests/test.rs @@ -0,0 +1,428 @@ +#![allow(clippy::arithmetic_side_effects)] + +use { + serial_test::serial, + solana_cli_config::Config as SolanaConfig, + solana_client::nonblocking::rpc_client::RpcClient, + solana_sdk::{ + bpf_loader_upgradeable, + clock::Epoch, + epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH}, + native_token::LAMPORTS_PER_SOL, + pubkey::Pubkey, + signature::{write_keypair_file, Keypair, Signer}, + stake::{ + self, + state::{Authorized, Lockup, StakeStateV2}, + }, + system_instruction, system_program, + transaction::Transaction, + }, + solana_test_validator::{TestValidator, TestValidatorGenesis, UpgradeableProgramInfo}, + solana_vote_program::{ + vote_instruction::{self, CreateVoteAccountConfig}, + vote_state::{VoteInit, VoteState, VoteStateVersions}, + }, + spl_token_client::client::{ProgramClient, ProgramRpcClient, ProgramRpcClientSendTransaction}, + std::{path::PathBuf, process::Command, str::FromStr, sync::Arc, time::Duration}, + tempfile::NamedTempFile, + test_case::test_case, + tokio::time::sleep, +}; + +type PClient = Arc>; +const SVSP_CLI: &str = "../../target/debug/spl-single-pool"; + +#[allow(dead_code)] +pub struct Env { + pub rpc_client: Arc, + pub program_client: PClient, + pub payer: Keypair, + pub keypair_file_path: String, + pub config_file_path: String, + pub vote_account: Pubkey, + + // persist in struct so they dont scope out but callers dont need to make them + validator: TestValidator, + keypair_file: NamedTempFile, + config_file: NamedTempFile, +} + +async fn setup(initialize: bool) -> Env { + // start test validator + let (validator, payer) = start_validator().await; + + // make clients + let rpc_client = Arc::new(validator.get_async_rpc_client()); + let program_client: PClient = Arc::new(ProgramRpcClient::new( + rpc_client.clone(), + ProgramRpcClientSendTransaction, + )); + + // write the payer to disk + let keypair_file = NamedTempFile::new().unwrap(); + write_keypair_file(&payer, &keypair_file).unwrap(); + + // write a full config file with our rpc and payer to disk + let config_file = NamedTempFile::new().unwrap(); + let config_file_path = config_file.path().to_str().unwrap(); + let solana_config = SolanaConfig { + json_rpc_url: validator.rpc_url(), + websocket_url: validator.rpc_pubsub_url(), + keypair_path: keypair_file.path().to_str().unwrap().to_string(), + ..SolanaConfig::default() + }; + solana_config.save(config_file_path).unwrap(); + + // make vote and stake accounts + let vote_account = create_vote_account(&program_client, &payer, &payer.pubkey()).await; + if initialize { + let status = Command::new(SVSP_CLI) + .args([ + "manage", + "initialize", + "-C", + config_file_path, + &vote_account.to_string(), + ]) + .status() + .unwrap(); + assert!(status.success()); + } + + Env { + rpc_client, + program_client, + payer, + keypair_file_path: keypair_file.path().to_str().unwrap().to_string(), + config_file_path: config_file_path.to_string(), + vote_account, + validator, + keypair_file, + config_file, + } +} + +async fn start_validator() -> (TestValidator, Keypair) { + solana_logger::setup(); + let mut test_validator_genesis = TestValidatorGenesis::default(); + + test_validator_genesis.epoch_schedule(EpochSchedule::custom( + MINIMUM_SLOTS_PER_EPOCH, + MINIMUM_SLOTS_PER_EPOCH, + false, + )); + + test_validator_genesis.add_upgradeable_programs_with_path(&[ + UpgradeableProgramInfo { + program_id: Pubkey::from_str("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s").unwrap(), + loader: bpf_loader_upgradeable::id(), + program_path: PathBuf::from("../program/tests/fixtures/mpl_token_metadata.so"), + upgrade_authority: Pubkey::default(), + }, + UpgradeableProgramInfo { + program_id: spl_single_pool::id(), + loader: bpf_loader_upgradeable::id(), + program_path: PathBuf::from("../../target/deploy/spl_single_pool.so"), + upgrade_authority: Pubkey::default(), + }, + ]); + test_validator_genesis.start_async().await +} + +async fn wait_for_next_epoch(rpc_client: &RpcClient) -> Epoch { + let current_epoch = rpc_client.get_epoch_info().await.unwrap().epoch; + println!("current epoch {}, advancing to next...", current_epoch); + loop { + let epoch_info = rpc_client.get_epoch_info().await.unwrap(); + if epoch_info.epoch > current_epoch { + return epoch_info.epoch; + } + + sleep(Duration::from_millis(200)).await; + } +} + +async fn create_vote_account( + program_client: &PClient, + payer: &Keypair, + withdrawer: &Pubkey, +) -> Pubkey { + let validator = Keypair::new(); + let vote_account = Keypair::new(); + let voter = Keypair::new(); + + let zero_rent = program_client + .get_minimum_balance_for_rent_exemption(0) + .await + .unwrap(); + + let vote_rent = program_client + .get_minimum_balance_for_rent_exemption(VoteState::size_of() * 2) + .await + .unwrap(); + + let blockhash = program_client.get_latest_blockhash().await.unwrap(); + + let mut instructions = vec![system_instruction::create_account( + &payer.pubkey(), + &validator.pubkey(), + zero_rent, + 0, + &system_program::id(), + )]; + instructions.append(&mut vote_instruction::create_account_with_config( + &payer.pubkey(), + &vote_account.pubkey(), + &VoteInit { + node_pubkey: validator.pubkey(), + authorized_voter: voter.pubkey(), + authorized_withdrawer: *withdrawer, + ..VoteInit::default() + }, + vote_rent, + CreateVoteAccountConfig { + space: VoteStateVersions::vote_state_size_of(true) as u64, + ..Default::default() + }, + )); + + let mut transaction = Transaction::new_with_payer(&instructions, Some(&payer.pubkey())); + + transaction + .try_partial_sign(&vec![payer], blockhash) + .unwrap(); + transaction + .try_partial_sign(&vec![&validator, &vote_account], blockhash) + .unwrap(); + + program_client.send_transaction(&transaction).await.unwrap(); + + vote_account.pubkey() +} + +async fn create_and_delegate_stake_account( + program_client: &PClient, + payer: &Keypair, + vote_account: &Pubkey, +) -> Pubkey { + let stake_account = Keypair::new(); + + let stake_rent = program_client + .get_minimum_balance_for_rent_exemption(StakeStateV2::size_of()) + .await + .unwrap(); + let blockhash = program_client.get_latest_blockhash().await.unwrap(); + + let mut transaction = Transaction::new_with_payer( + &stake::instruction::create_account( + &payer.pubkey(), + &stake_account.pubkey(), + &Authorized::auto(&payer.pubkey()), + &Lockup::default(), + stake_rent + LAMPORTS_PER_SOL, + ), + Some(&payer.pubkey()), + ); + + transaction + .try_partial_sign(&vec![payer], blockhash) + .unwrap(); + transaction + .try_partial_sign(&vec![&stake_account], blockhash) + .unwrap(); + + program_client.send_transaction(&transaction).await.unwrap(); + + let mut transaction = Transaction::new_with_payer( + &[stake::instruction::delegate_stake( + &stake_account.pubkey(), + &payer.pubkey(), + vote_account, + )], + Some(&payer.pubkey()), + ); + + transaction.sign(&vec![payer], blockhash); + + program_client.send_transaction(&transaction).await.unwrap(); + + stake_account.pubkey() +} + +#[tokio::test] +#[serial] +async fn reactivate_pool_stake() { + let env = setup(true).await; + + // setting up a test validator for this to succeed is hell, and success is tested in program tests + // so we just make sure the cli can send a well-formed instruction + let output = Command::new(SVSP_CLI) + .args([ + "manage", + "reactivate-pool-stake", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + "--skip-deactivation-check", + ]) + .output() + .unwrap(); + assert!(String::from_utf8(output.stderr) + .unwrap() + .contains("custom program error: 0xc")); +} + +#[test_case(true; "default_stake")] +#[test_case(false; "normal_stake")] +#[tokio::test] +#[serial] +async fn deposit(use_default: bool) { + let env = setup(true).await; + + let stake_account = if use_default { + let status = Command::new(SVSP_CLI) + .args([ + "create-default-stake", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + &LAMPORTS_PER_SOL.to_string(), + ]) + .status() + .unwrap(); + assert!(status.success()); + + Pubkey::default() + } else { + create_and_delegate_stake_account(&env.program_client, &env.payer, &env.vote_account).await + }; + + wait_for_next_epoch(&env.rpc_client).await; + + let mut args = vec![ + "deposit".to_string(), + "-C".to_string(), + env.config_file_path, + ]; + + if use_default { + args.extend([ + "--vote-account".to_string(), + env.vote_account.to_string(), + "--default-stake-account".to_string(), + ]); + } else { + args.push(stake_account.to_string()); + }; + + let status = Command::new(SVSP_CLI).args(&args).status().unwrap(); + assert!(status.success()); +} + +#[tokio::test] +#[serial] +async fn withdraw() { + let env = setup(true).await; + let stake_account = + create_and_delegate_stake_account(&env.program_client, &env.payer, &env.vote_account).await; + + wait_for_next_epoch(&env.rpc_client).await; + + let status = Command::new(SVSP_CLI) + .args([ + "deposit", + "-C", + &env.config_file_path, + &stake_account.to_string(), + ]) + .status() + .unwrap(); + assert!(status.success()); + + let status = Command::new(SVSP_CLI) + .args([ + "withdraw", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + "ALL", + ]) + .status() + .unwrap(); + assert!(status.success()); +} + +#[tokio::test] +#[serial] +async fn create_metadata() { + let env = setup(false).await; + + let status = Command::new(SVSP_CLI) + .args([ + "manage", + "initialize", + "-C", + &env.config_file_path, + "--skip-metadata", + &env.vote_account.to_string(), + ]) + .status() + .unwrap(); + assert!(status.success()); + + let status = Command::new(SVSP_CLI) + .args([ + "manage", + "create-token-metadata", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + ]) + .status() + .unwrap(); + assert!(status.success()); +} + +#[tokio::test] +#[serial] +async fn update_metadata() { + let env = setup(true).await; + + let status = Command::new(SVSP_CLI) + .args([ + "manage", + "update-token-metadata", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + "whatever", + "idk", + ]) + .status() + .unwrap(); + assert!(status.success()); + + // testing this flag because the match is rather torturous + let status = Command::new(SVSP_CLI) + .args([ + "manage", + "update-token-metadata", + "-C", + &env.config_file_path, + "--vote-account", + &env.vote_account.to_string(), + "--authorized-withdrawer", + &env.keypair_file_path, + "something", + "new", + ]) + .status() + .unwrap(); + assert!(status.success()); +} diff --git a/single-pool/js/LICENSE b/single-pool/js/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/single-pool/js/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/single-pool/js/packages/classic/.eslintignore b/single-pool/js/packages/classic/.eslintignore new file mode 100644 index 00000000000..58542507948 --- /dev/null +++ b/single-pool/js/packages/classic/.eslintignore @@ -0,0 +1,4 @@ +dist +node_modules +.vscode +.idea diff --git a/single-pool/js/packages/classic/.eslintrc.cjs b/single-pool/js/packages/classic/.eslintrc.cjs new file mode 100644 index 00000000000..63db7b8405f --- /dev/null +++ b/single-pool/js/packages/classic/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { + es6: true, + node: true, + jest: true, + }, + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + plugins: ['@typescript-eslint/eslint-plugin'], + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + }, +}; diff --git a/single-pool/js/packages/classic/.prettierrc.cjs b/single-pool/js/packages/classic/.prettierrc.cjs new file mode 100644 index 00000000000..8446d684477 --- /dev/null +++ b/single-pool/js/packages/classic/.prettierrc.cjs @@ -0,0 +1,7 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', + printWidth: 100, + endOfLine: 'lf', + semi: true, +}; diff --git a/single-pool/js/packages/classic/README.md b/single-pool/js/packages/classic/README.md new file mode 100644 index 00000000000..f5416d3e10c --- /dev/null +++ b/single-pool/js/packages/classic/README.md @@ -0,0 +1,11 @@ +# `@solana/spl-single-pool-classic` + +A TypeScript library for interacting with the SPL Single-Validator Stake Pool program, targeting `@solana/web3.js` 1.x. +**If you are working on the new, bleeding-edge web3.js, you want `@solana/spl-single-pool`.** + +For information on installation and usage, see [SPL docs](https://spl.solana.com/single-pool). + +For support, please ask questions on the [Solana Stack Exchange](https://solana.stackexchange.com). + +If you've found a bug or you'd like to request a feature, please +[open an issue](https://github.com/solana-labs/solana-program-library/issues/new). diff --git a/single-pool/js/packages/classic/package.json b/single-pool/js/packages/classic/package.json new file mode 100644 index 00000000000..d02208e6829 --- /dev/null +++ b/single-pool/js/packages/classic/package.json @@ -0,0 +1,41 @@ +{ + "name": "@solana/single-pool-classic", + "version": "1.0.0", + "scripts": { + "build": "pnpm tsc", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint . --fix", + "test": "NODE_OPTIONS='--import=tsx' ava" + }, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "exports": { + "require": "./dist/index.js", + "import": "./dist/index.js" + }, + "devDependencies": { + "@ava/typescript": "^4.1.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "ava": "^5.3.1", + "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "solana-bankrun": "^0.2.0", + "ts-node": "^10.9.1", + "tsx": "^3.12.7", + "typescript": "^5.2.2" + }, + "dependencies": { + "@solana/web3.js": "^1.78.4", + "@solana/single-pool": "workspace:*" + }, + "ava": { + "extensions": { + "ts": "commonjs" + }, + "nodeArguments": [ + "--import=tsx" + ] + } +} diff --git a/single-pool/js/packages/classic/src/addresses.ts b/single-pool/js/packages/classic/src/addresses.ts new file mode 100644 index 00000000000..b547661d606 --- /dev/null +++ b/single-pool/js/packages/classic/src/addresses.ts @@ -0,0 +1,45 @@ +import { PublicKey } from '@solana/web3.js'; +import { + findPoolAddress as findPoolModern, + findPoolStakeAddress as findStakeModern, + findPoolMintAddress as findMintModern, + findPoolStakeAuthorityAddress as findStakeAuthorityModern, + findPoolMintAuthorityAddress as findMintAuthorityModern, + findPoolMplAuthorityAddress as findMplAuthorityModern, + findDefaultDepositAccountAddress as findDefaultDepositModern, +} from '@solana/single-pool'; + +export async function findPoolAddress(programId: PublicKey, voteAccountAddress: PublicKey) { + return new PublicKey(await findPoolModern(programId.toBase58(), voteAccountAddress.toBase58())); +} + +export async function findPoolStakeAddress(programId: PublicKey, poolAddress: PublicKey) { + return new PublicKey(await findStakeModern(programId.toBase58(), poolAddress.toBase58())); +} + +export async function findPoolMintAddress(programId: PublicKey, poolAddress: PublicKey) { + return new PublicKey(await findMintModern(programId.toBase58(), poolAddress.toBase58())); +} + +export async function findPoolStakeAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { + return new PublicKey( + await findStakeAuthorityModern(programId.toBase58(), poolAddress.toBase58()), + ); +} + +export async function findPoolMintAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { + return new PublicKey(await findMintAuthorityModern(programId.toBase58(), poolAddress.toBase58())); +} + +export async function findPoolMplAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { + return new PublicKey(await findMplAuthorityModern(programId.toBase58(), poolAddress.toBase58())); +} + +export async function findDefaultDepositAccountAddress( + poolAddress: PublicKey, + userWallet: PublicKey, +) { + return new PublicKey( + await findDefaultDepositModern(poolAddress.toBase58(), userWallet.toBase58()), + ); +} diff --git a/single-pool/js/packages/classic/src/index.ts b/single-pool/js/packages/classic/src/index.ts new file mode 100644 index 00000000000..415bdcec605 --- /dev/null +++ b/single-pool/js/packages/classic/src/index.ts @@ -0,0 +1,15 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { getVoteAccountAddressForPool as getVoteModern } from '@solana/single-pool'; + +import { rpc } from './internal'; + +export * from './mpl_metadata'; +export * from './addresses'; +export * from './instructions'; +export * from './transactions'; + +export async function getVoteAccountAddressForPool(connection: Connection, poolAddress: PublicKey) { + const voteAccountModern = await getVoteModern(rpc(connection), poolAddress.toBase58()); + + return new PublicKey(voteAccountModern); +} diff --git a/single-pool/js/packages/classic/src/instructions.ts b/single-pool/js/packages/classic/src/instructions.ts new file mode 100644 index 00000000000..e783a8b5311 --- /dev/null +++ b/single-pool/js/packages/classic/src/instructions.ts @@ -0,0 +1,76 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { SinglePoolInstruction as PoolInstructionModern } from '@solana/single-pool'; + +import { modernInstructionToLegacy } from './internal'; + +export class SinglePoolInstruction { + static async initializePool(voteAccount: PublicKey): Promise { + const instruction = await PoolInstructionModern.initializePool(voteAccount.toBase58()); + return modernInstructionToLegacy(instruction); + } + + static async reactivatePoolStake(voteAccount: PublicKey): Promise { + const instruction = await PoolInstructionModern.reactivatePoolStake(voteAccount.toBase58()); + return modernInstructionToLegacy(instruction); + } + + static async depositStake( + pool: PublicKey, + userStakeAccount: PublicKey, + userTokenAccount: PublicKey, + userLamportAccount: PublicKey, + ): Promise { + const instruction = await PoolInstructionModern.depositStake( + pool.toBase58(), + userStakeAccount.toBase58(), + userTokenAccount.toBase58(), + userLamportAccount.toBase58(), + ); + return modernInstructionToLegacy(instruction); + } + + static async withdrawStake( + pool: PublicKey, + userStakeAccount: PublicKey, + userStakeAuthority: PublicKey, + userTokenAccount: PublicKey, + tokenAmount: number | bigint, + ): Promise { + const instruction = await PoolInstructionModern.withdrawStake( + pool.toBase58(), + userStakeAccount.toBase58(), + userStakeAuthority.toBase58(), + userTokenAccount.toBase58(), + BigInt(tokenAmount), + ); + return modernInstructionToLegacy(instruction); + } + + static async createTokenMetadata( + pool: PublicKey, + payer: PublicKey, + ): Promise { + const instruction = await PoolInstructionModern.createTokenMetadata( + pool.toBase58(), + payer.toBase58(), + ); + return modernInstructionToLegacy(instruction); + } + + static async updateTokenMetadata( + voteAccount: PublicKey, + authorizedWithdrawer: PublicKey, + tokenName: string, + tokenSymbol: string, + tokenUri?: string, + ): Promise { + const instruction = await PoolInstructionModern.updateTokenMetadata( + voteAccount.toBase58(), + authorizedWithdrawer.toBase58(), + tokenName, + tokenSymbol, + tokenUri, + ); + return modernInstructionToLegacy(instruction); + } +} diff --git a/single-pool/js/packages/classic/src/internal.ts b/single-pool/js/packages/classic/src/internal.ts new file mode 100644 index 00000000000..e1628ee76a8 --- /dev/null +++ b/single-pool/js/packages/classic/src/internal.ts @@ -0,0 +1,71 @@ +import { Connection, Transaction, TransactionInstruction, PublicKey } from '@solana/web3.js'; +import { Buffer } from 'buffer'; + +export function rpc(connection: Connection) { + return { + getAccountInfo(address: string) { + return { + async send() { + const pubkey = new PublicKey(address); + return await connection.getAccountInfo(pubkey); + }, + }; + }, + getMinimumBalanceForRentExemption(size: bigint) { + return { + async send() { + return BigInt(await connection.getMinimumBalanceForRentExemption(Number(size))); + }, + }; + }, + getStakeMinimumDelegation() { + return { + async send() { + const minimumDelegation = await connection.getStakeMinimumDelegation(); + return { value: BigInt(minimumDelegation.value) }; + }, + }; + }, + }; +} + +export function modernInstructionToLegacy(modernInstruction: any): TransactionInstruction { + const keys = []; + for (const account of modernInstruction.accounts) { + keys.push({ + pubkey: new PublicKey(account.address), + isSigner: !!(account.role & 2), + isWritable: !!(account.role & 1), + }); + } + + return new TransactionInstruction({ + programId: new PublicKey(modernInstruction.programAddress), + keys, + data: Buffer.from(modernInstruction.data), + }); +} + +export function modernTransactionToLegacy(modernTransaction: any): Transaction { + const legacyTransaction = new Transaction(); + legacyTransaction.add(...modernTransaction.instructions.map(modernInstructionToLegacy)); + + return legacyTransaction; +} + +export function paramsToModern(params: any) { + const modernParams = {} as any; + for (const k of Object.keys(params)) { + if (k == 'connection') { + modernParams.rpc = rpc(params[k]); + } else if (params[k] instanceof PublicKey || params[k].constructor.name == 'PublicKey') { + modernParams[k] = params[k].toBase58(); + } else if (typeof params[k] == 'number') { + modernParams[k] = BigInt(params[k]); + } else { + modernParams[k] = params[k]; + } + } + + return modernParams; +} diff --git a/single-pool/js/packages/classic/src/mpl_metadata.ts b/single-pool/js/packages/classic/src/mpl_metadata.ts new file mode 100644 index 00000000000..31c52ae2a50 --- /dev/null +++ b/single-pool/js/packages/classic/src/mpl_metadata.ts @@ -0,0 +1,12 @@ +import { PublicKey } from '@solana/web3.js'; +import { Buffer } from 'buffer'; + +export const MPL_METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'); + +export function findMplMetadataAddress(poolMintAddress: PublicKey) { + const [publicKey] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), MPL_METADATA_PROGRAM_ID.toBuffer(), poolMintAddress.toBuffer()], + MPL_METADATA_PROGRAM_ID, + ); + return publicKey; +} diff --git a/single-pool/js/packages/classic/src/transactions.ts b/single-pool/js/packages/classic/src/transactions.ts new file mode 100644 index 00000000000..f422c8fa004 --- /dev/null +++ b/single-pool/js/packages/classic/src/transactions.ts @@ -0,0 +1,111 @@ +import { PublicKey, Connection } from '@solana/web3.js'; +import { SinglePoolProgram as PoolProgramModern } from '@solana/single-pool'; + +import { paramsToModern, modernTransactionToLegacy, rpc } from './internal'; + +interface DepositParams { + connection: Connection; + pool: PublicKey; + userWallet: PublicKey; + userStakeAccount?: PublicKey; + depositFromDefaultAccount?: boolean; + userTokenAccount?: PublicKey; + userLamportAccount?: PublicKey; + userWithdrawAuthority?: PublicKey; +} + +interface WithdrawParams { + connection: Connection; + pool: PublicKey; + userWallet: PublicKey; + userStakeAccount: PublicKey; + tokenAmount: number | bigint; + createStakeAccount?: boolean; + userStakeAuthority?: PublicKey; + userTokenAccount?: PublicKey; + userTokenAuthority?: PublicKey; +} + +export class SinglePoolProgram { + static programId: PublicKey = new PublicKey(PoolProgramModern.programAddress); + static space: number = Number(PoolProgramModern.space); + + static async initialize( + connection: Connection, + voteAccount: PublicKey, + payer: PublicKey, + skipMetadata = false, + ) { + const modernTransaction = await PoolProgramModern.initialize( + rpc(connection), + voteAccount.toBase58(), + payer.toBase58(), + skipMetadata, + ); + + return modernTransactionToLegacy(modernTransaction); + } + + static async reactivatePoolStake(connection: Connection, voteAccount: PublicKey) { + const modernTransaction = await PoolProgramModern.reactivatePoolStake(voteAccount.toBase58()); + + return modernTransactionToLegacy(modernTransaction); + } + + static async deposit(params: DepositParams) { + const modernParams = paramsToModern(params); + const modernTransaction = await PoolProgramModern.deposit(modernParams); + + return modernTransactionToLegacy(modernTransaction); + } + + static async withdraw(params: WithdrawParams) { + const modernParams = paramsToModern(params); + const modernTransaction = await PoolProgramModern.withdraw(modernParams); + + return modernTransactionToLegacy(modernTransaction); + } + + static async createTokenMetadata(pool: PublicKey, payer: PublicKey) { + const modernTransaction = await PoolProgramModern.createTokenMetadata( + pool.toBase58(), + payer.toBase58(), + ); + + return modernTransactionToLegacy(modernTransaction); + } + + static async updateTokenMetadata( + voteAccount: PublicKey, + authorizedWithdrawer: PublicKey, + name: string, + symbol: string, + uri?: string, + ) { + const modernTransaction = await PoolProgramModern.updateTokenMetadata( + voteAccount.toBase58(), + authorizedWithdrawer.toBase58(), + name, + symbol, + uri, + ); + + return modernTransactionToLegacy(modernTransaction); + } + + static async createAndDelegateUserStake( + connection: Connection, + voteAccount: PublicKey, + userWallet: PublicKey, + stakeAmount: number | bigint, + ) { + const modernTransaction = await PoolProgramModern.createAndDelegateUserStake( + rpc(connection), + voteAccount.toBase58(), + userWallet.toBase58(), + BigInt(stakeAmount), + ); + + return modernTransactionToLegacy(modernTransaction); + } +} diff --git a/single-pool/js/packages/classic/tests/fixtures/mpl_token_metadata.so b/single-pool/js/packages/classic/tests/fixtures/mpl_token_metadata.so new file mode 120000 index 00000000000..df4d35160ee --- /dev/null +++ b/single-pool/js/packages/classic/tests/fixtures/mpl_token_metadata.so @@ -0,0 +1 @@ +../../../../../../stake-pool/program/tests/fixtures/mpl_token_metadata.so \ No newline at end of file diff --git a/single-pool/js/packages/classic/tests/fixtures/spl_single_pool.so b/single-pool/js/packages/classic/tests/fixtures/spl_single_pool.so new file mode 120000 index 00000000000..5fe6f8bd60e --- /dev/null +++ b/single-pool/js/packages/classic/tests/fixtures/spl_single_pool.so @@ -0,0 +1 @@ +../../../../../../target/deploy/spl_single_pool.so \ No newline at end of file diff --git a/single-pool/js/packages/classic/tests/transactions.test.ts b/single-pool/js/packages/classic/tests/transactions.test.ts new file mode 100644 index 00000000000..c6e1cb9cc5d --- /dev/null +++ b/single-pool/js/packages/classic/tests/transactions.test.ts @@ -0,0 +1,433 @@ +import test from 'ava'; +import { start, BanksClient, ProgramTestContext } from 'solana-bankrun'; +import { + Keypair, + PublicKey, + Transaction, + Authorized, + TransactionInstruction, + StakeProgram, + VoteProgram, +} from '@solana/web3.js'; +import { Buffer } from 'buffer'; +import { + getVoteAccountAddressForPool, + findDefaultDepositAccountAddress, + MPL_METADATA_PROGRAM_ID, + SinglePoolProgram, + findPoolAddress, + findPoolStakeAddress, + findPoolMintAddress, + SinglePoolProgram, + findMplMetadataAddress, +} from '../src/index.ts'; +import * as voteAccount from './vote_account.json'; + +const SLOTS_PER_EPOCH: bigint = 432000n; + +class BanksConnection { + constructor(client: BanksClient, payer: Keypair) { + this.client = client; + this.payer = payer; + } + + async getMinimumBalanceForRentExemption(dataLen: number): Promise { + const rent = await this.client.getRent(); + return Number(rent.minimumBalance(BigInt(dataLen))); + } + + async getStakeMinimumDelegation() { + const transaction = new Transaction(); + transaction.add( + new TransactionInstruction({ + programId: StakeProgram.programId, + keys: [], + data: Buffer.from([13, 0, 0, 0]), + }), + ); + transaction.recentBlockhash = (await this.client.getLatestBlockhash())[0]; + transaction.feePayer = this.payer.publicKey; + transaction.sign(this.payer); + + const res = await this.client.simulateTransaction(transaction); + const data = Array.from(res.inner.meta.returnData.data); + const minimumDelegation = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); + + return { value: minimumDelegation }; + } + + async getAccountInfo(address: PublicKey, commitment?: string): Promise> { + const account = await this.client.getAccount(address, commitment); + if (account) { + account.data = Buffer.from(account.data); + } + return account; + } +} + +async function startWithContext(authorizedWithdrawer?: PublicKey) { + const voteAccountData = Uint8Array.from(atob(voteAccount.account.data[0]), (c) => + c.charCodeAt(0), + ); + + if (authorizedWithdrawer != null) { + voteAccountData.set(authorizedWithdrawer.toBytes(), 36); + } + + return await start( + [ + { name: 'spl_single_pool', programId: SinglePoolProgram.programId }, + { name: 'mpl_token_metadata', programId: MPL_METADATA_PROGRAM_ID }, + ], + [ + { + address: new PublicKey(voteAccount.pubkey), + info: { + lamports: voteAccount.account.lamports, + data: voteAccountData, + owner: VoteProgram.programId, + executable: false, + }, + }, + ], + ); +} + +async function processTransaction( + context: ProgramTestContext, + transaction: Transaction, + signers = [], +) { + transaction.recentBlockhash = context.lastBlockhash; + transaction.feePayer = context.payer.publicKey; + transaction.sign(...[context.payer].concat(signers)); + return context.banksClient.processTransaction(transaction); +} + +async function createAndDelegateStakeAccount( + context: ProgramTestContext, + voteAccountAddress: PublicKey, +): Promise { + const connection = new BanksConnection(context.banksClient, context.payer); + let userStakeAccount = new Keypair(); + + const stakeRent = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); + const minimumDelegation = (await connection.getStakeMinimumDelegation()).value; + let transaction = StakeProgram.createAccount({ + authorized: new Authorized(context.payer.publicKey, context.payer.publicKey), + fromPubkey: context.payer.publicKey, + lamports: stakeRent + minimumDelegation, + stakePubkey: userStakeAccount.publicKey, + }); + await processTransaction(context, transaction, [userStakeAccount]); + userStakeAccount = userStakeAccount.publicKey; + + transaction = StakeProgram.delegate({ + authorizedPubkey: context.payer.publicKey, + stakePubkey: userStakeAccount, + votePubkey: voteAccountAddress, + }); + await processTransaction(context, transaction); + + return userStakeAccount; +} + +test('initialize', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + + // initialize pool + const transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + t.truthy(await client.getAccount(poolAddress), 'pool has been created'); + t.truthy( + await client.getAccount( + findMplMetadataAddress(await findPoolMintAddress(SinglePoolProgram.programId, poolAddress)), + ), + 'metadata has been created', + ); +}); + +test('reactivate pool stake', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + + // initialize pool + let transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + const slot = await client.getSlot(); + context.warpToSlot(slot + SLOTS_PER_EPOCH); + + // reactivate pool stake + transaction = await SinglePoolProgram.reactivatePoolStake(connection, voteAccountAddress); + + // setting up the validator state for this to succeed is very annoying + // we test success in program tests; here we just confirm we submit a well-formed transaction + let message = ''; + try { + await processTransaction(context, transaction); + } catch (e) { + message = e.message; + } finally { + t.true(message.includes('custom program error: 0xc'), 'got expected stake mismatch error'); + } +}); + +test('deposit', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + const poolStakeAddress = await findPoolStakeAddress(SinglePoolProgram.programId, poolAddress); + const userStakeAccount = await createAndDelegateStakeAccount(context, voteAccountAddress); + + // initialize pool + let transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + const slot = await client.getSlot(); + context.warpToSlot(slot + SLOTS_PER_EPOCH); + + // deposit + transaction = await SinglePoolProgram.deposit({ + connection, + pool: poolAddress, + userWallet: payer.publicKey, + userStakeAccount, + }); + await processTransaction(context, transaction); + + const stakeRent = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); + const minimumDelegation = (await connection.getStakeMinimumDelegation()).value; + const poolStakeAccount = await client.getAccount(poolStakeAddress); + t.is(poolStakeAccount.lamports, minimumDelegation * 2 + stakeRent, 'stake has been deposited'); +}); + +test('deposit from default', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + const poolStakeAddress = await findPoolStakeAddress(SinglePoolProgram.programId, poolAddress); + + // create default account + const minimumDelegation = (await connection.getStakeMinimumDelegation()).value; + let transaction = await SinglePoolProgram.createAndDelegateUserStake( + connection, + voteAccountAddress, + payer.publicKey, + minimumDelegation, + ); + await processTransaction(context, transaction); + + // initialize pool + transaction = await SinglePoolProgram.initialize(connection, voteAccountAddress, payer.publicKey); + await processTransaction(context, transaction); + + const slot = await client.getSlot(); + context.warpToSlot(slot + SLOTS_PER_EPOCH); + + // deposit + transaction = await SinglePoolProgram.deposit({ + connection, + pool: poolAddress, + userWallet: payer.publicKey, + depositFromDefaultAccount: true, + }); + await processTransaction(context, transaction); + + const stakeRent = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); + const poolStakeAccount = await client.getAccount(poolStakeAddress); + t.is(poolStakeAccount.lamports, minimumDelegation * 2 + stakeRent, 'stake has been deposited'); +}); + +test('withdraw', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + const poolStakeAddress = await findPoolStakeAddress(SinglePoolProgram.programId, poolAddress); + const depositAccount = await createAndDelegateStakeAccount(context, voteAccountAddress); + + // initialize pool + let transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + const slot = await client.getSlot(); + context.warpToSlot(slot + SLOTS_PER_EPOCH); + + // deposit + transaction = await SinglePoolProgram.deposit({ + connection, + pool: poolAddress, + userWallet: payer.publicKey, + userStakeAccount: depositAccount, + }); + await processTransaction(context, transaction); + + const minimumDelegation = (await connection.getStakeMinimumDelegation()).value; + const poolStakeAccount = await client.getAccount(poolStakeAddress); + t.true(poolStakeAccount.lamports > minimumDelegation * 2, 'stake has been deposited'); + + // withdraw + const withdrawAccount = new Keypair(); + transaction = await SinglePoolProgram.withdraw({ + connection, + pool: poolAddress, + userWallet: payer.publicKey, + userStakeAccount: withdrawAccount.publicKey, + tokenAmount: minimumDelegation, + createStakeAccount: true, + }); + await processTransaction(context, transaction, [withdrawAccount]); + + const stakeRent = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); + const userStakeAccount = await client.getAccount(withdrawAccount.publicKey); + t.is(userStakeAccount.lamports, minimumDelegation + stakeRent, 'stake has been withdrawn'); +}); + +test('create metadata', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + + // initialize pool without metadata + let transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + true, + ); + await processTransaction(context, transaction); + + t.truthy(await client.getAccount(poolAddress), 'pool has been created'); + t.falsy( + await client.getAccount( + findMplMetadataAddress(await findPoolMintAddress(SinglePoolProgram.programId, poolAddress)), + ), + 'metadata has not been created', + ); + + // create metadata + transaction = await SinglePoolProgram.createTokenMetadata(poolAddress, payer.publicKey); + await processTransaction(context, transaction); + + t.truthy( + await client.getAccount( + findMplMetadataAddress(await findPoolMintAddress(SinglePoolProgram.programId, poolAddress)), + ), + 'metadata has been created', + ); +}); + +test('update metadata', async (t) => { + const authorizedWithdrawer = new Keypair(); + + const context = await startWithContext(authorizedWithdrawer.publicKey); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + const poolMintAddress = await findPoolMintAddress(SinglePoolProgram.programId, poolAddress); + const poolMetadataAddress = findMplMetadataAddress(poolMintAddress); + + // initialize pool + let transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + // update metadata + const newName = 'hana wuz here'; + transaction = await SinglePoolProgram.updateTokenMetadata( + voteAccountAddress, + authorizedWithdrawer.publicKey, + newName, + '', + ); + await processTransaction(context, transaction, [authorizedWithdrawer]); + + const metadataAccount = await client.getAccount(poolMetadataAddress); + t.true( + new TextDecoder('ascii').decode(metadataAccount.data).indexOf(newName) > -1, + 'metadata name has been updated', + ); +}); + +test('get vote account address', async (t) => { + const context = await startWithContext(); + const client = context.banksClient; + const payer = context.payer; + const connection = new BanksConnection(client, payer); + + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const poolAddress = await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress); + + // initialize pool + const transaction = await SinglePoolProgram.initialize( + connection, + voteAccountAddress, + payer.publicKey, + ); + await processTransaction(context, transaction); + + const chainVoteAccount = await getVoteAccountAddressForPool(connection, poolAddress); + t.true(chainVoteAccount.equals(voteAccountAddress), 'got correct vote account'); +}); + +test('default account address', async (t) => { + const voteAccountAddress = new PublicKey(voteAccount.pubkey); + const owner = new PublicKey('GtaYCtXWCrciizttN5mx9P38niTQPGWpfu6DnSgAr3Cj'); + const expectedDefault = new PublicKey('BbfrNeJrd82cSFsULXT9zG8SvLLB8WsTc1gQsDFy3Sed'); + + const actualDefault = await findDefaultDepositAccountAddress( + await findPoolAddress(SinglePoolProgram.programId, voteAccountAddress), + owner, + ); + + t.true(actualDefault.equals(expectedDefault), 'got correct default account address'); +}); diff --git a/single-pool/js/packages/classic/tests/vote_account.json b/single-pool/js/packages/classic/tests/vote_account.json new file mode 100644 index 00000000000..44a5efd12d8 --- /dev/null +++ b/single-pool/js/packages/classic/tests/vote_account.json @@ -0,0 +1,14 @@ +{ + "pubkey": "KRAKEnMdmT4EfM8ykTFH6yLoCd5vNLcQvJwF66Y2dag", + "account": { + "lamports": 1300578700922, + "data": [ + "AQAAAAs8CYpjxAGc9BKIFsvo43erJeAPq9FLBOZuVf7zcXQwDtalO9ClDHolg+JcQCSa0sIFkdUQpQh5ufXK07iakuhkHwAAAAAAAAACxGIMAAAAAB8AAAADxGIMAAAAAB4AAAAExGIMAAAAAB0AAAAFxGIMAAAAABwAAAAGxGIMAAAAABsAAAAHxGIMAAAAABoAAAAIxGIMAAAAABkAAAAJxGIMAAAAABgAAAAKxGIMAAAAABcAAAALxGIMAAAAABYAAAAMxGIMAAAAABUAAAANxGIMAAAAABQAAAAOxGIMAAAAABMAAAAPxGIMAAAAABIAAAAQxGIMAAAAABEAAAARxGIMAAAAABAAAAASxGIMAAAAAA8AAAATxGIMAAAAAA4AAAAUxGIMAAAAAA0AAAAVxGIMAAAAAAwAAAAWxGIMAAAAAAsAAAAXxGIMAAAAAAoAAAAYxGIMAAAAAAkAAAAZxGIMAAAAAAgAAAAaxGIMAAAAAAcAAAAbxGIMAAAAAAYAAAAcxGIMAAAAAAUAAAAdxGIMAAAAAAQAAAAexGIMAAAAAAMAAAAfxGIMAAAAAAIAAAAgxGIMAAAAAAEAAAABAcRiDAAAAAABAAAAAAAAAOEBAAAAAAAACzwJimPEAZz0EogWy+jjd6sl4A+r0UsE5m5V/vNxdDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAFAAAAAAAAAAKIBAAAAAAAA1diYBAAAAAAvw5IEAAAAAKMBAAAAAAAA++WeBAAAAADV2JgEAAAAAKQBAAAAAAAA5dukBAAAAAD75Z4EAAAAAKUBAAAAAAAAbf6qBAAAAADl26QEAAAAAKYBAAAAAAAA1hGxBAAAAABt/qoEAAAAAKcBAAAAAAAAaiS3BAAAAADWEbEEAAAAAKgBAAAAAAAARHK9BAAAAABqJLcEAAAAAKkBAAAAAAAAacPDBAAAAABEcr0EAAAAAKoBAAAAAAAAWQ3KBAAAAABpw8MEAAAAAKsBAAAAAAAAUHLQBAAAAABZDcoEAAAAAKwBAAAAAAAAk9nWBAAAAABQctAEAAAAAK0BAAAAAAAAxTHdBAAAAACT2dYEAAAAAK4BAAAAAAAA34bjBAAAAADFMd0EAAAAAK8BAAAAAAAA0+vpBAAAAADfhuMEAAAAALABAAAAAAAAnFLwBAAAAADT6+kEAAAAALEBAAAAAAAAt7z2BAAAAACcUvAEAAAAALIBAAAAAAAAoyT9BAAAAAC3vPYEAAAAALMBAAAAAAAAXX0DBQAAAACjJP0EAAAAALQBAAAAAAAA6NcJBQAAAABdfQMFAAAAALUBAAAAAAAA5wQQBQAAAADo1wkFAAAAALYBAAAAAAAAvAMWBQAAAADnBBAFAAAAALcBAAAAAAAA6DkcBQAAAAC8AxYFAAAAALgBAAAAAAAAx34iBQAAAADoORwFAAAAALkBAAAAAAAAm80oBQAAAADHfiIFAAAAALoBAAAAAAAAriQvBQAAAACbzSgFAAAAALsBAAAAAAAAsHE1BQAAAACuJC8FAAAAALwBAAAAAAAADpM7BQAAAACwcTUFAAAAAL0BAAAAAAAANsdBBQAAAAAOkzsFAAAAAL4BAAAAAAAAXgNIBQAAAAA2x0EFAAAAAL8BAAAAAAAAnBJOBQAAAABeA0gFAAAAAMABAAAAAAAAukpUBQAAAACcEk4FAAAAAMEBAAAAAAAALIxaBQAAAAC6SlQFAAAAAMIBAAAAAAAAzddgBQAAAAAsjFoFAAAAAMMBAAAAAAAAaS9nBQAAAADN12AFAAAAAMQBAAAAAAAATG1tBQAAAABpL2cFAAAAAMUBAAAAAAAAqptzBQAAAABMbW0FAAAAAMYBAAAAAAAACvJ5BQAAAACqm3MFAAAAAMcBAAAAAAAARUmABQAAAAAK8nkFAAAAAMgBAAAAAAAATJGGBQAAAABFSYAFAAAAAMkBAAAAAAAAZ+CMBQAAAABMkYYFAAAAAMoBAAAAAAAAsyGTBQAAAABn4IwFAAAAAMsBAAAAAAAAT2GZBQAAAACzIZMFAAAAAMwBAAAAAAAAEHKfBQAAAABPYZkFAAAAAM0BAAAAAAAAzbClBQAAAAAQcp8FAAAAAM4BAAAAAAAA0gWsBQAAAADNsKUFAAAAAM8BAAAAAAAAP2eyBQAAAADSBawFAAAAANABAAAAAAAAOLu4BQAAAAA/Z7IFAAAAANEBAAAAAAAAVQC/BQAAAAA4u7gFAAAAANIBAAAAAAAAilLFBQAAAABVAL8FAAAAANMBAAAAAAAAfaLLBQAAAACKUsUFAAAAANQBAAAAAAAAGfrRBQAAAAB9ossFAAAAANUBAAAAAAAA/1DYBQAAAAAZ+tEFAAAAANYBAAAAAAAA06reBQAAAAD/UNgFAAAAANcBAAAAAAAAwwXlBQAAAADTqt4FAAAAANgBAAAAAAAAnVvrBQAAAADDBeUFAAAAANkBAAAAAAAAvbXxBQAAAACdW+sFAAAAANoBAAAAAAAAMQH4BQAAAAC9tfEFAAAAANsBAAAAAAAANT/+BQAAAAAxAfgFAAAAANwBAAAAAAAA04wEBgAAAAA1P/4FAAAAAN0BAAAAAAAAhNIKBgAAAADTjAQGAAAAAN4BAAAAAAAADCkRBgAAAACE0goGAAAAAN8BAAAAAAAAL4MXBgAAAAAMKREGAAAAAOABAAAAAAAAF9odBgAAAAAvgxcGAAAAAOEBAAAAAAAAjfQdBgAAAAAX2h0GAAAAACDEYgwAAAAAzUXCZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "base64" + ], + "owner": "Vote111111111111111111111111111111111111111", + "executable": false, + "rentEpoch": 361, + "space": 3731 + } +} \ No newline at end of file diff --git a/single-pool/js/packages/classic/tsconfig.json b/single-pool/js/packages/classic/tsconfig.json new file mode 100644 index 00000000000..e2d884ce607 --- /dev/null +++ b/single-pool/js/packages/classic/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "es2020", + "baseUrl": "./src", + "outDir": "dist", + "declaration": true, + "declarationDir": "dist", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "skipLibCheck": true // needed to avoid re-export errors from borsh + }, + "include": ["src/**/*.ts"] +} diff --git a/single-pool/js/packages/modern/.eslintignore b/single-pool/js/packages/modern/.eslintignore new file mode 100644 index 00000000000..58542507948 --- /dev/null +++ b/single-pool/js/packages/modern/.eslintignore @@ -0,0 +1,4 @@ +dist +node_modules +.vscode +.idea diff --git a/single-pool/js/packages/modern/.eslintrc.cjs b/single-pool/js/packages/modern/.eslintrc.cjs new file mode 100644 index 00000000000..63db7b8405f --- /dev/null +++ b/single-pool/js/packages/modern/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { + es6: true, + node: true, + jest: true, + }, + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + plugins: ['@typescript-eslint/eslint-plugin'], + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + }, +}; diff --git a/single-pool/js/packages/modern/.prettierrc.cjs b/single-pool/js/packages/modern/.prettierrc.cjs new file mode 100644 index 00000000000..8446d684477 --- /dev/null +++ b/single-pool/js/packages/modern/.prettierrc.cjs @@ -0,0 +1,7 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', + printWidth: 100, + endOfLine: 'lf', + semi: true, +}; diff --git a/single-pool/js/packages/modern/README.md b/single-pool/js/packages/modern/README.md new file mode 100644 index 00000000000..2cd1ff2c225 --- /dev/null +++ b/single-pool/js/packages/modern/README.md @@ -0,0 +1,11 @@ +# `@solana/spl-single-pool` + +A TypeScript library for interacting with the SPL Single-Validator Stake Pool program, targeting `@solana/web3.js` 2.0. +**If you are working on the legacy web3.js (if you're not sure, you probably are!), you want `@solana/spl-single-pool-classic`.** + +For information on installation and usage, see [SPL docs](https://spl.solana.com/single-pool). + +For support, please ask questions on the [Solana Stack Exchange](https://solana.stackexchange.com). + +If you've found a bug or you'd like to request a feature, please +[open an issue](https://github.com/solana-labs/solana-program-library/issues/new). diff --git a/single-pool/js/packages/modern/package.json b/single-pool/js/packages/modern/package.json new file mode 100644 index 00000000000..25df938007b --- /dev/null +++ b/single-pool/js/packages/modern/package.json @@ -0,0 +1,40 @@ +{ + "name": "@solana/single-pool", + "version": "1.0.0", + "scripts": { + "build": "pnpm tsc", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint . --fix", + "test": "NODE_OPTIONS='--loader=tsx' ava" + }, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "exports": { + "require": "./dist/index.js", + "import": "./dist/index.js" + }, + "devDependencies": { + "@ava/typescript": "^4.1.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "ava": "^5.3.1", + "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "solana-bankrun": "^0.2.0", + "ts-node": "^10.9.1", + "tsx": "^3.12.7", + "typescript": "^5.2.2" + }, + "dependencies": { + "@solana/web3.js": "=2.0.0-experimental.21e994f" + }, + "ava": { + "extensions": { + "ts": "module" + }, + "nodeArguments": [ + "--loader=tsx" + ] + } +} diff --git a/single-pool/js/packages/modern/src/addresses.ts b/single-pool/js/packages/modern/src/addresses.ts new file mode 100644 index 00000000000..64ebd7ad6e3 --- /dev/null +++ b/single-pool/js/packages/modern/src/addresses.ts @@ -0,0 +1,125 @@ +import { + address, + getAddressCodec, + Base58EncodedAddress, + getProgramDerivedAddress, + createAddressWithSeed, +} from '@solana/web3.js'; + +import { MPL_METADATA_PROGRAM_ID } from './internal'; +import { STAKE_PROGRAM_ID } from './quarantine'; + +export const SINGLE_POOL_PROGRAM_ID = address('SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE'); + +export type VoteAccountAddress = + Base58EncodedAddress & { + readonly __voteAccountAddress: unique symbol; + }; + +export type PoolAddress = Base58EncodedAddress & { + readonly __poolAddress: unique symbol; +}; + +export type PoolStakeAddress = Base58EncodedAddress & { + readonly __poolStakeAddress: unique symbol; +}; + +export type PoolMintAddress = Base58EncodedAddress & { + readonly __poolMintAddress: unique symbol; +}; + +export type PoolStakeAuthorityAddress = + Base58EncodedAddress & { + readonly __poolStakeAuthorityAddress: unique symbol; + }; + +export type PoolMintAuthorityAddress = + Base58EncodedAddress & { + readonly __poolMintAuthorityAddress: unique symbol; + }; + +export type PoolMplAuthorityAddress = + Base58EncodedAddress & { + readonly __poolMplAuthorityAddress: unique symbol; + }; + +export async function findPoolAddress( + programId: Base58EncodedAddress, + voteAccountAddress: VoteAccountAddress, +): Promise { + return (await findPda(programId, voteAccountAddress, 'pool')) as PoolAddress; +} + +export async function findPoolStakeAddress( + programId: Base58EncodedAddress, + poolAddress: PoolAddress, +): Promise { + return (await findPda(programId, poolAddress, 'stake')) as PoolStakeAddress; +} + +export async function findPoolMintAddress( + programId: Base58EncodedAddress, + poolAddress: PoolAddress, +): Promise { + return (await findPda(programId, poolAddress, 'mint')) as PoolMintAddress; +} + +export async function findPoolStakeAuthorityAddress( + programId: Base58EncodedAddress, + poolAddress: PoolAddress, +): Promise { + return (await findPda(programId, poolAddress, 'stake_authority')) as PoolStakeAuthorityAddress; +} + +export async function findPoolMintAuthorityAddress( + programId: Base58EncodedAddress, + poolAddress: PoolAddress, +): Promise { + return (await findPda(programId, poolAddress, 'mint_authority')) as PoolMintAuthorityAddress; +} + +export async function findPoolMplAuthorityAddress( + programId: Base58EncodedAddress, + poolAddress: PoolAddress, +): Promise { + return (await findPda(programId, poolAddress, 'mpl_authority')) as PoolMplAuthorityAddress; +} + +async function findPda( + programId: Base58EncodedAddress, + baseAddress: Base58EncodedAddress, + prefix: string, +) { + const { serialize } = getAddressCodec(); + const [pda] = await getProgramDerivedAddress({ + programAddress: programId, + seeds: [prefix, serialize(baseAddress)], + }); + + return pda; +} + +export async function findDefaultDepositAccountAddress( + poolAddress: PoolAddress, + userWallet: Base58EncodedAddress, +) { + return createAddressWithSeed({ + baseAddress: userWallet, + seed: defaultDepositAccountSeed(poolAddress), + programAddress: STAKE_PROGRAM_ID, + }); +} + +export function defaultDepositAccountSeed(poolAddress: PoolAddress): string { + return 'svsp' + poolAddress.slice(0, 28); +} + +export async function findMplMetadataAddress(poolMintAddress: PoolMintAddress) { + const { serialize } = getAddressCodec(); + const [pda] = await getProgramDerivedAddress({ + programAddress: MPL_METADATA_PROGRAM_ID, + seeds: ['metadata', serialize(MPL_METADATA_PROGRAM_ID), serialize(poolMintAddress)], + }); + + return pda; +} diff --git a/single-pool/js/packages/modern/src/index.ts b/single-pool/js/packages/modern/src/index.ts new file mode 100644 index 00000000000..f8697e33160 --- /dev/null +++ b/single-pool/js/packages/modern/src/index.ts @@ -0,0 +1,19 @@ +import { getAddressCodec } from '@solana/web3.js'; + +import { PoolAddress, VoteAccountAddress } from './addresses'; + +export * from './addresses'; +export * from './instructions'; +export * from './transactions'; + +export async function getVoteAccountAddressForPool( + rpc: any, // XXX not exported: Rpc, + poolAddress: PoolAddress, + abortSignal?: AbortSignal, +): Promise { + const poolAccount = await rpc.getAccountInfo(poolAddress).send(abortSignal); + if (!(poolAccount && poolAccount.data[0] === 1)) { + throw 'invalid pool address'; + } + return getAddressCodec().deserialize(poolAccount.data.slice(1))[0] as VoteAccountAddress; +} diff --git a/single-pool/js/packages/modern/src/instructions.ts b/single-pool/js/packages/modern/src/instructions.ts new file mode 100644 index 00000000000..3317355dbd2 --- /dev/null +++ b/single-pool/js/packages/modern/src/instructions.ts @@ -0,0 +1,382 @@ +import { + getAddressCodec, + Base58EncodedAddress, + ReadonlySignerAccount, + ReadonlyAccount, + IInstructionWithAccounts, + IInstructionWithData, + WritableAccount, + WritableSignerAccount, + IInstruction, + AccountRole, +} from '@solana/web3.js'; + +import { + PoolMintAuthorityAddress, + PoolMintAddress, + PoolMplAuthorityAddress, + PoolStakeAuthorityAddress, + PoolStakeAddress, + findMplMetadataAddress, + findPoolMplAuthorityAddress, + findPoolAddress, + VoteAccountAddress, + PoolAddress, + findPoolStakeAddress, + findPoolMintAddress, + findPoolMintAuthorityAddress, + findPoolStakeAuthorityAddress, + SINGLE_POOL_PROGRAM_ID, +} from './addresses'; +import { MPL_METADATA_PROGRAM_ID } from './internal'; +import { + SYSTEM_PROGRAM_ID, + SYSVAR_RENT_ID, + SYSVAR_CLOCK_ID, + STAKE_PROGRAM_ID, + SYSVAR_STAKE_HISTORY_ID, + STAKE_CONFIG_ID, + TOKEN_PROGRAM_ID, + u32, + u64, +} from './quarantine'; + +type InitializePoolInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + WritableAccount, + WritableAccount, + WritableAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ] + > & + IInstructionWithData; + +type ReactivatePoolStakeInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + ReadonlyAccount, + WritableAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ] + > & + IInstructionWithData; + +type DepositStakeInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + WritableAccount, + WritableAccount, + ReadonlyAccount, + ReadonlyAccount, + WritableAccount, // user stake + WritableAccount, // user token + WritableAccount, // user lamport + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ] + > & + IInstructionWithData; + +type WithdrawStakeInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + WritableAccount, + WritableAccount, + ReadonlyAccount, + ReadonlyAccount, + WritableAccount, // user stake + WritableAccount, // user token + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ] + > & + IInstructionWithData; + +type CreateTokenMetadataInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + WritableSignerAccount, // mpl payer + WritableAccount, // mpl account + ReadonlyAccount, + ReadonlyAccount, + ] + > & + IInstructionWithData; + +type UpdateTokenMetadataInstruction = IInstruction & + IInstructionWithAccounts< + [ + ReadonlyAccount, + ReadonlyAccount, + ReadonlyAccount, + ReadonlySignerAccount, // authorized withdrawer + WritableAccount, // mpl account + ReadonlyAccount, + ] + > & + IInstructionWithData; + +const enum SinglePoolInstructionType { + InitializePool = 0, + ReactivatePoolStake, + DepositStake, + WithdrawStake, + CreateTokenMetadata, + UpdateTokenMetadata, +} + +export const SinglePoolInstruction = { + initializePool: initializePoolInstruction, + reactivatePoolStake: reactivatePoolStakeInstruction, + depositStake: depositStakeInstruction, + withdrawStake: withdrawStakeInstruction, + createTokenMetadata: createTokenMetadataInstruction, + updateTokenMetadata: updateTokenMetadataInstruction, +}; + +export async function initializePoolInstruction( + voteAccount: VoteAccountAddress, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + const pool = await findPoolAddress(programAddress, voteAccount); + const [stake, mint, stakeAuthority, mintAuthority] = await Promise.all([ + findPoolStakeAddress(programAddress, pool), + findPoolMintAddress(programAddress, pool), + findPoolStakeAuthorityAddress(programAddress, pool), + findPoolMintAuthorityAddress(programAddress, pool), + ]); + + const data = new Uint8Array([SinglePoolInstructionType.InitializePool]); + + return { + data, + accounts: [ + { address: voteAccount, role: AccountRole.READONLY }, + { address: pool, role: AccountRole.WRITABLE }, + { address: stake, role: AccountRole.WRITABLE }, + { address: mint, role: AccountRole.WRITABLE }, + { address: stakeAuthority, role: AccountRole.READONLY }, + { address: mintAuthority, role: AccountRole.READONLY }, + { address: SYSVAR_RENT_ID, role: AccountRole.READONLY }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: SYSVAR_STAKE_HISTORY_ID, role: AccountRole.READONLY }, + { address: STAKE_CONFIG_ID, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ID, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ID, role: AccountRole.READONLY }, + { address: STAKE_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} + +export async function reactivatePoolStakeInstruction( + voteAccount: VoteAccountAddress, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + const pool = await findPoolAddress(programAddress, voteAccount); + const [stake, stakeAuthority] = await Promise.all([ + findPoolStakeAddress(programAddress, pool), + findPoolStakeAuthorityAddress(programAddress, pool), + ]); + + const data = new Uint8Array([SinglePoolInstructionType.ReactivatePoolStake]); + + return { + data, + accounts: [ + { address: voteAccount, role: AccountRole.READONLY }, + { address: pool, role: AccountRole.READONLY }, + { address: stake, role: AccountRole.WRITABLE }, + { address: stakeAuthority, role: AccountRole.READONLY }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: SYSVAR_STAKE_HISTORY_ID, role: AccountRole.READONLY }, + { address: STAKE_CONFIG_ID, role: AccountRole.READONLY }, + { address: STAKE_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} + +export async function depositStakeInstruction( + pool: PoolAddress, + userStakeAccount: Base58EncodedAddress, + userTokenAccount: Base58EncodedAddress, + userLamportAccount: Base58EncodedAddress, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + const [stake, mint, stakeAuthority, mintAuthority] = await Promise.all([ + findPoolStakeAddress(programAddress, pool), + findPoolMintAddress(programAddress, pool), + findPoolStakeAuthorityAddress(programAddress, pool), + findPoolMintAuthorityAddress(programAddress, pool), + ]); + + const data = new Uint8Array([SinglePoolInstructionType.DepositStake]); + + return { + data, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: stake, role: AccountRole.WRITABLE }, + { address: mint, role: AccountRole.WRITABLE }, + { address: stakeAuthority, role: AccountRole.READONLY }, + { address: mintAuthority, role: AccountRole.READONLY }, + { address: userStakeAccount, role: AccountRole.WRITABLE }, + { address: userTokenAccount, role: AccountRole.WRITABLE }, + { address: userLamportAccount, role: AccountRole.WRITABLE }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: SYSVAR_STAKE_HISTORY_ID, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ID, role: AccountRole.READONLY }, + { address: STAKE_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} + +export async function withdrawStakeInstruction( + pool: PoolAddress, + userStakeAccount: Base58EncodedAddress, + userStakeAuthority: Base58EncodedAddress, + userTokenAccount: Base58EncodedAddress, + tokenAmount: bigint, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + const [stake, mint, stakeAuthority, mintAuthority] = await Promise.all([ + findPoolStakeAddress(programAddress, pool), + findPoolMintAddress(programAddress, pool), + findPoolStakeAuthorityAddress(programAddress, pool), + findPoolMintAuthorityAddress(programAddress, pool), + ]); + + const { serialize } = getAddressCodec(); + const data = new Uint8Array([ + SinglePoolInstructionType.WithdrawStake, + ...serialize(userStakeAuthority), + ...u64(tokenAmount), + ]); + + return { + data, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: stake, role: AccountRole.WRITABLE }, + { address: mint, role: AccountRole.WRITABLE }, + { address: stakeAuthority, role: AccountRole.READONLY }, + { address: mintAuthority, role: AccountRole.READONLY }, + { address: userStakeAccount, role: AccountRole.WRITABLE }, + { address: userTokenAccount, role: AccountRole.WRITABLE }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ID, role: AccountRole.READONLY }, + { address: STAKE_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} + +export async function createTokenMetadataInstruction( + pool: PoolAddress, + payer: Base58EncodedAddress, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + const mint = await findPoolMintAddress(programAddress, pool); + const [mintAuthority, mplAuthority, mplMetadata] = await Promise.all([ + findPoolMintAuthorityAddress(programAddress, pool), + findPoolMplAuthorityAddress(programAddress, pool), + findMplMetadataAddress(mint), + ]); + + const data = new Uint8Array([SinglePoolInstructionType.CreateTokenMetadata]); + + return { + data, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: mint, role: AccountRole.READONLY }, + { address: mintAuthority, role: AccountRole.READONLY }, + { address: mplAuthority, role: AccountRole.READONLY }, + { address: payer, role: AccountRole.WRITABLE_SIGNER }, + { address: mplMetadata, role: AccountRole.WRITABLE }, + { address: MPL_METADATA_PROGRAM_ID, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} + +export async function updateTokenMetadataInstruction( + voteAccount: VoteAccountAddress, + authorizedWithdrawer: Base58EncodedAddress, + tokenName: string, + tokenSymbol: string, + tokenUri?: string, +): Promise { + const programAddress = SINGLE_POOL_PROGRAM_ID; + tokenUri = tokenUri || ''; + + if (tokenName.length > 32) { + throw 'maximum token name length is 32 characters'; + } + + if (tokenSymbol.length > 10) { + throw 'maximum token symbol length is 10 characters'; + } + + if (tokenUri.length > 200) { + throw 'maximum token uri length is 200 characters'; + } + + const pool = await findPoolAddress(programAddress, voteAccount); + const [mint, mplAuthority] = await Promise.all([ + findPoolMintAddress(programAddress, pool), + findPoolMplAuthorityAddress(programAddress, pool), + ]); + const mplMetadata = await findMplMetadataAddress(mint); + + const text = new TextEncoder(); + const data = new Uint8Array([ + SinglePoolInstructionType.UpdateTokenMetadata, + ...u32(tokenName.length), + ...text.encode(tokenName), + ...u32(tokenSymbol.length), + ...text.encode(tokenSymbol), + ...u32(tokenUri.length), + ...text.encode(tokenUri), + ]); + + return { + data, + accounts: [ + { address: voteAccount, role: AccountRole.READONLY }, + { address: pool, role: AccountRole.READONLY }, + { address: mplAuthority, role: AccountRole.READONLY }, + { address: authorizedWithdrawer, role: AccountRole.READONLY_SIGNER }, + { address: mplMetadata, role: AccountRole.WRITABLE }, + { address: MPL_METADATA_PROGRAM_ID, role: AccountRole.READONLY }, + ], + programAddress, + }; +} diff --git a/single-pool/js/packages/modern/src/internal.ts b/single-pool/js/packages/modern/src/internal.ts new file mode 100644 index 00000000000..cdea7d11bae --- /dev/null +++ b/single-pool/js/packages/modern/src/internal.ts @@ -0,0 +1,3 @@ +import { address } from '@solana/web3.js'; + +export const MPL_METADATA_PROGRAM_ID = address('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'); diff --git a/single-pool/js/packages/modern/src/quarantine.ts b/single-pool/js/packages/modern/src/quarantine.ts new file mode 100644 index 00000000000..be688b48156 --- /dev/null +++ b/single-pool/js/packages/modern/src/quarantine.ts @@ -0,0 +1,261 @@ +import { + address, + getAddressCodec, + Base58EncodedAddress, + AccountRole, + getProgramDerivedAddress, +} from '@solana/web3.js'; + +// HERE BE DRAGONS +// this is all the stuff that shouldnt be in our library once we can import from elsewhere + +export const SYSTEM_PROGRAM_ID = address('11111111111111111111111111111111'); +export const STAKE_PROGRAM_ID = address('Stake11111111111111111111111111111111111111'); +export const SYSVAR_RENT_ID = address('SysvarRent111111111111111111111111111111111'); +export const SYSVAR_CLOCK_ID = address('SysvarC1ock11111111111111111111111111111111'); +export const SYSVAR_STAKE_HISTORY_ID = address('SysvarStakeHistory1111111111111111111111111'); +export const STAKE_CONFIG_ID = address('StakeConfig11111111111111111111111111111111'); +export const STAKE_ACCOUNT_SIZE = 200n; + +export const TOKEN_PROGRAM_ID = address('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'); +export const ATOKEN_PROGRAM_ID = address('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'); +export const MINT_SIZE = 82n; + +export function u32(n: number): Uint8Array { + const bns = Uint32Array.from([n]); + return new Uint8Array(bns.buffer); +} + +export function u64(n: bigint): Uint8Array { + const bns = BigUint64Array.from([n]); + return new Uint8Array(bns.buffer); +} + +export class SystemInstruction { + static createAccount(params: { + from: Base58EncodedAddress; + newAccount: Base58EncodedAddress; + lamports: bigint; + space: bigint; + programAddress: Base58EncodedAddress; + }) { + const { serialize } = getAddressCodec(); + const data = new Uint8Array([ + ...u32(0), + ...u64(params.lamports), + ...u64(params.space), + ...serialize(params.programAddress), + ]); + + const accounts = [ + { address: params.from, role: AccountRole.WRITABLE_SIGNER }, + { address: params.newAccount, role: AccountRole.WRITABLE_SIGNER }, + ]; + + return { + data, + accounts, + programAddress: SYSTEM_PROGRAM_ID, + }; + } + + static transfer(params: { + from: Base58EncodedAddress; + to: Base58EncodedAddress; + lamports: bigint; + }) { + const data = new Uint8Array([...u32(2), ...u64(params.lamports)]); + + const accounts = [ + { address: params.from, role: AccountRole.WRITABLE_SIGNER }, + { address: params.to, role: AccountRole.WRITABLE }, + ]; + + return { + data, + accounts, + programAddress: SYSTEM_PROGRAM_ID, + }; + } + + static createAccountWithSeed(params: { + from: Base58EncodedAddress; + newAccount: Base58EncodedAddress; + base: Base58EncodedAddress; + seed: string; + lamports: bigint; + space: bigint; + programAddress: Base58EncodedAddress; + }) { + const { serialize } = getAddressCodec(); + const data = new Uint8Array([ + ...u32(3), + ...serialize(params.base), + ...u64(BigInt(params.seed.length)), + ...new TextEncoder().encode(params.seed), + ...u64(params.lamports), + ...u64(params.space), + ...serialize(params.programAddress), + ]); + + const accounts = [ + { address: params.from, role: AccountRole.WRITABLE_SIGNER }, + { address: params.newAccount, role: AccountRole.WRITABLE }, + ]; + if (params.base != params.from) { + accounts.push({ address: params.base, role: AccountRole.READONLY_SIGNER }); + } + + return { + data, + accounts, + programAddress: SYSTEM_PROGRAM_ID, + }; + } +} + +export class TokenInstruction { + static approve(params: { + account: Base58EncodedAddress; + delegate: Base58EncodedAddress; + owner: Base58EncodedAddress; + amount: bigint; + }) { + const data = new Uint8Array([...u32(4), ...u64(params.amount)]); + + const accounts = [ + { address: params.account, role: AccountRole.WRITABLE }, + { address: params.delegate, role: AccountRole.READONLY }, + { address: params.owner, role: AccountRole.READONLY_SIGNER }, + ]; + + return { + data, + accounts, + programAddress: TOKEN_PROGRAM_ID, + }; + } + + static createAssociatedTokenAccount(params: { + payer: Base58EncodedAddress; + associatedAccount: Base58EncodedAddress; + owner: Base58EncodedAddress; + mint: Base58EncodedAddress; + }) { + const data = new Uint8Array([0]); + + const accounts = [ + { address: params.payer, role: AccountRole.WRITABLE_SIGNER }, + { address: params.associatedAccount, role: AccountRole.WRITABLE }, + { address: params.owner, role: AccountRole.READONLY }, + { address: params.mint, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ID, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ID, role: AccountRole.READONLY }, + ]; + + return { + data, + accounts, + programAddress: ATOKEN_PROGRAM_ID, + }; + } +} + +export enum StakeAuthorizationType { + Staker, + Withdrawer, +} + +export class StakeInstruction { + // idc about doing it right unless this goes in a lib + static initialize(params: { + stakeAccount: Base58EncodedAddress; + staker: Base58EncodedAddress; + withdrawer: Base58EncodedAddress; + }) { + const { serialize } = getAddressCodec(); + const data = new Uint8Array([ + ...u32(0), + ...serialize(params.staker), + ...serialize(params.withdrawer), + ...Array(48).fill(0), + ]); + + const accounts = [ + { address: params.stakeAccount, role: AccountRole.WRITABLE }, + { address: SYSVAR_RENT_ID, role: AccountRole.READONLY }, + ]; + + return { + data, + accounts, + programAddress: STAKE_PROGRAM_ID, + }; + } + + static authorize(params: { + stakeAccount: Base58EncodedAddress; + authorized: Base58EncodedAddress; + newAuthorized: Base58EncodedAddress; + authorizationType: StakeAuthorizationType; + custodian?: Base58EncodedAddress; + }) { + const { serialize } = getAddressCodec(); + const data = new Uint8Array([ + ...u32(1), + ...serialize(params.newAuthorized), + ...u32(params.authorizationType), + ]); + + const accounts = [ + { address: params.stakeAccount, role: AccountRole.WRITABLE }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: params.authorized, role: AccountRole.READONLY_SIGNER }, + ]; + if (params.custodian) { + accounts.push({ address: params.custodian, role: AccountRole.READONLY }); + } + + return { + data, + accounts, + programAddress: STAKE_PROGRAM_ID, + }; + } + + static delegate(params: { + stakeAccount: Base58EncodedAddress; + authorized: Base58EncodedAddress; + voteAccount: Base58EncodedAddress; + }) { + const data = new Uint8Array(u32(2)); + + const accounts = [ + { address: params.stakeAccount, role: AccountRole.WRITABLE }, + { address: params.voteAccount, role: AccountRole.READONLY }, + { address: SYSVAR_CLOCK_ID, role: AccountRole.READONLY }, + { address: SYSVAR_STAKE_HISTORY_ID, role: AccountRole.READONLY }, + { address: STAKE_CONFIG_ID, role: AccountRole.READONLY }, + { address: params.authorized, role: AccountRole.READONLY_SIGNER }, + ]; + + return { + data, + accounts, + programAddress: STAKE_PROGRAM_ID, + }; + } +} + +export async function getAssociatedTokenAddress( + mint: Base58EncodedAddress, + owner: Base58EncodedAddress, +) { + const { serialize } = getAddressCodec(); + const [pda] = await getProgramDerivedAddress({ + programAddress: ATOKEN_PROGRAM_ID, + seeds: [serialize(owner), serialize(TOKEN_PROGRAM_ID), serialize(mint)], + }); + + return pda; +} diff --git a/single-pool/js/packages/modern/src/transactions.ts b/single-pool/js/packages/modern/src/transactions.ts new file mode 100644 index 00000000000..72b6255bf4d --- /dev/null +++ b/single-pool/js/packages/modern/src/transactions.ts @@ -0,0 +1,346 @@ +import { + appendTransactionInstruction, + Transaction, + TransactionVersion, + Base58EncodedAddress, +} from '@solana/web3.js'; + +import { + findPoolAddress, + VoteAccountAddress, + PoolAddress, + findPoolStakeAddress, + findPoolMintAddress, + defaultDepositAccountSeed, + findDefaultDepositAccountAddress, + findPoolMintAuthorityAddress, + findPoolStakeAuthorityAddress, + SINGLE_POOL_PROGRAM_ID, +} from './addresses'; +import { + initializePoolInstruction, + reactivatePoolStakeInstruction, + depositStakeInstruction, + withdrawStakeInstruction, + createTokenMetadataInstruction, + updateTokenMetadataInstruction, +} from './instructions'; +import { + STAKE_PROGRAM_ID, + STAKE_ACCOUNT_SIZE, + MINT_SIZE, + StakeInstruction, + SystemInstruction, + TokenInstruction, + StakeAuthorizationType, + getAssociatedTokenAddress, +} from './quarantine'; + +interface DepositParams { + rpc: any; // XXX Rpc + pool: PoolAddress; + userWallet: Base58EncodedAddress; + userStakeAccount?: Base58EncodedAddress; + depositFromDefaultAccount?: boolean; + userTokenAccount?: Base58EncodedAddress; + userLamportAccount?: Base58EncodedAddress; + userWithdrawAuthority?: Base58EncodedAddress; +} + +interface WithdrawParams { + rpc: any; // XXX Rpc + pool: PoolAddress; + userWallet: Base58EncodedAddress; + userStakeAccount: Base58EncodedAddress; + tokenAmount: bigint; + createStakeAccount?: boolean; + userStakeAuthority?: Base58EncodedAddress; + userTokenAccount?: Base58EncodedAddress; + userTokenAuthority?: Base58EncodedAddress; +} + +export const SINGLE_POOL_ACCOUNT_SIZE = 33n; + +export const SinglePoolProgram = { + programAddress: SINGLE_POOL_PROGRAM_ID, + space: SINGLE_POOL_ACCOUNT_SIZE, + initialize: initializeTransaction, + reactivatePoolStake: reactivatePoolStakeTransaction, + deposit: depositTransaction, + withdraw: withdrawTransaction, + createTokenMetadata: createTokenMetadataTransaction, + updateTokenMetadata: updateTokenMetadataTransaction, + createAndDelegateUserStake: createAndDelegateUserStakeTransaction, +}; + +export async function initializeTransaction( + rpc: any, // XXX not exported: Rpc, + voteAccount: VoteAccountAddress, + payer: Base58EncodedAddress, + skipMetadata = false, +): Promise { + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + + const pool = await findPoolAddress(SINGLE_POOL_PROGRAM_ID, voteAccount); + const [stake, mint, poolRent, stakeRent, mintRent, minimumDelegationObj] = await Promise.all([ + findPoolStakeAddress(SINGLE_POOL_PROGRAM_ID, pool), + findPoolMintAddress(SINGLE_POOL_PROGRAM_ID, pool), + rpc.getMinimumBalanceForRentExemption(SINGLE_POOL_ACCOUNT_SIZE).send(), + rpc.getMinimumBalanceForRentExemption(STAKE_ACCOUNT_SIZE).send(), + rpc.getMinimumBalanceForRentExemption(MINT_SIZE).send(), + rpc.getStakeMinimumDelegation().send(), + ]); + const minimumDelegation = minimumDelegationObj.value; + + transaction = appendTransactionInstruction( + SystemInstruction.transfer({ + from: payer, + to: pool, + lamports: poolRent, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + SystemInstruction.transfer({ + from: payer, + to: stake, + lamports: stakeRent + minimumDelegation, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + SystemInstruction.transfer({ + from: payer, + to: mint, + lamports: mintRent, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + await initializePoolInstruction(voteAccount), + transaction, + ); + + if (!skipMetadata) { + transaction = appendTransactionInstruction( + await createTokenMetadataInstruction(pool, payer), + transaction, + ); + } + + return transaction; +} + +export async function reactivatePoolStakeTransaction( + voteAccount: VoteAccountAddress, +): Promise { + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + transaction = appendTransactionInstruction( + await reactivatePoolStakeInstruction(voteAccount), + transaction, + ); + + return transaction; +} + +export async function depositTransaction(params: DepositParams) { + const { rpc, pool, userWallet } = params; + + // note this is just xnor + if (!params.userStakeAccount == !params.depositFromDefaultAccount) { + throw 'must either provide userStakeAccount or true depositFromDefaultAccount'; + } + + const userStakeAccount = ( + params.depositFromDefaultAccount + ? await findDefaultDepositAccountAddress(pool, userWallet) + : params.userStakeAccount + ) as Base58EncodedAddress; + + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + + const [mint, poolStakeAuthority] = await Promise.all([ + findPoolMintAddress(SINGLE_POOL_PROGRAM_ID, pool), + findPoolStakeAuthorityAddress(SINGLE_POOL_PROGRAM_ID, pool), + ]); + + const userAssociatedTokenAccount = await getAssociatedTokenAddress(mint, userWallet); + const userTokenAccount = params.userTokenAccount || userAssociatedTokenAccount; + const userLamportAccount = params.userLamportAccount || userWallet; + const userWithdrawAuthority = params.userWithdrawAuthority || userWallet; + + if ( + userTokenAccount == userAssociatedTokenAccount && + (await rpc.getAccountInfo(userAssociatedTokenAccount).send()) == null + ) { + transaction = appendTransactionInstruction( + TokenInstruction.createAssociatedTokenAccount({ + payer: userWallet, + associatedAccount: userAssociatedTokenAccount, + owner: userWallet, + mint, + }), + transaction, + ); + } + + transaction = appendTransactionInstruction( + StakeInstruction.authorize({ + stakeAccount: userStakeAccount, + authorized: userWithdrawAuthority, + newAuthorized: poolStakeAuthority, + authorizationType: StakeAuthorizationType.Staker, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + StakeInstruction.authorize({ + stakeAccount: userStakeAccount, + authorized: userWithdrawAuthority, + newAuthorized: poolStakeAuthority, + authorizationType: StakeAuthorizationType.Withdrawer, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + await depositStakeInstruction(pool, userStakeAccount, userTokenAccount, userLamportAccount), + transaction, + ); + + return transaction; +} + +export async function withdrawTransaction(params: WithdrawParams) { + const { rpc, pool, userWallet, userStakeAccount, tokenAmount, createStakeAccount } = params; + + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + + const poolMintAuthority = await findPoolMintAuthorityAddress(SINGLE_POOL_PROGRAM_ID, pool); + + const userStakeAuthority = params.userStakeAuthority || userWallet; + const userTokenAccount = + params.userTokenAccount || + (await getAssociatedTokenAddress( + await findPoolMintAddress(SINGLE_POOL_PROGRAM_ID, pool), + userWallet, + )); + const userTokenAuthority = params.userTokenAuthority || userWallet; + + if (createStakeAccount) { + transaction = appendTransactionInstruction( + SystemInstruction.createAccount({ + from: userWallet, + lamports: await rpc.getMinimumBalanceForRentExemption(STAKE_ACCOUNT_SIZE).send(), + newAccount: userStakeAccount, + programAddress: STAKE_PROGRAM_ID, + space: STAKE_ACCOUNT_SIZE, + }), + transaction, + ); + } + + transaction = appendTransactionInstruction( + TokenInstruction.approve({ + account: userTokenAccount, + delegate: poolMintAuthority, + owner: userTokenAuthority, + amount: tokenAmount, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + await withdrawStakeInstruction( + pool, + userStakeAccount, + userStakeAuthority, + userTokenAccount, + tokenAmount, + ), + transaction, + ); + + return transaction; +} + +export async function createTokenMetadataTransaction( + pool: PoolAddress, + payer: Base58EncodedAddress, +): Promise { + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + transaction = appendTransactionInstruction( + await createTokenMetadataInstruction(pool, payer), + transaction, + ); + + return transaction; +} + +export async function updateTokenMetadataTransaction( + voteAccount: VoteAccountAddress, + authorizedWithdrawer: Base58EncodedAddress, + name: string, + symbol: string, + uri?: string, +): Promise { + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + transaction = appendTransactionInstruction( + await updateTokenMetadataInstruction(voteAccount, authorizedWithdrawer, name, symbol, uri), + transaction, + ); + + return transaction; +} + +export async function createAndDelegateUserStakeTransaction( + rpc: any, // XXX not exported: Rpc, + voteAccount: VoteAccountAddress, + userWallet: Base58EncodedAddress, + stakeAmount: bigint, +): Promise { + let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; + + const pool = await findPoolAddress(SINGLE_POOL_PROGRAM_ID, voteAccount); + const [stakeAccount, stakeRent] = await Promise.all([ + findDefaultDepositAccountAddress(pool, userWallet), + await rpc.getMinimumBalanceForRentExemption(STAKE_ACCOUNT_SIZE).send(), + ]); + + transaction = appendTransactionInstruction( + SystemInstruction.createAccountWithSeed({ + base: userWallet, + from: userWallet, + lamports: stakeAmount + stakeRent, + newAccount: stakeAccount, + programAddress: STAKE_PROGRAM_ID, + seed: defaultDepositAccountSeed(pool), + space: STAKE_ACCOUNT_SIZE, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + StakeInstruction.initialize({ + stakeAccount, + staker: userWallet, + withdrawer: userWallet, + }), + transaction, + ); + + transaction = appendTransactionInstruction( + StakeInstruction.delegate({ + stakeAccount, + authorized: userWallet, + voteAccount, + }), + transaction, + ); + + return transaction; +} diff --git a/single-pool/js/packages/modern/tsconfig.json b/single-pool/js/packages/modern/tsconfig.json new file mode 100644 index 00000000000..c3b7b616df8 --- /dev/null +++ b/single-pool/js/packages/modern/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "es2020", + "baseUrl": "./src", + "outDir": "dist", + "declaration": true, + "declarationDir": "dist", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "lib": ["DOM"], + "skipLibCheck": true // needed to avoid re-export errors from borsh + }, + "include": ["src/**/*.ts"] +} diff --git a/single-pool/js/pnpm-workspace.yaml b/single-pool/js/pnpm-workspace.yaml new file mode 100644 index 00000000000..dee51e928d8 --- /dev/null +++ b/single-pool/js/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - "packages/*" diff --git a/stake-pool/single-pool/Cargo.toml b/single-pool/program/Cargo.toml similarity index 80% rename from stake-pool/single-pool/Cargo.toml rename to single-pool/program/Cargo.toml index 313d3422e48..cd43d6aace6 100644 --- a/stake-pool/single-pool/Cargo.toml +++ b/single-pool/program/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "spl-single-validator-pool" +name = "spl-single-pool" version = "1.0.0" description = "Solana Program Library Single-Validator Stake Pool" authors = ["Solana Labs Maintainers "] @@ -16,17 +16,17 @@ arrayref = "0.3.7" borsh = "0.10" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" -solana-program = "1.16.1" +num_enum = "0.7.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = [ "no-entrypoint" ] } thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -solana-vote-program = "1.16.1" -test-case = "3.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +solana-vote-program = "1.17.2" +test-case = "3.2" bincode = "1.3.1" rand = "0.8.5" approx = "0.5.1" diff --git a/single-pool/program/program-id.md b/single-pool/program/program-id.md new file mode 100644 index 00000000000..e873b000f15 --- /dev/null +++ b/single-pool/program/program-id.md @@ -0,0 +1 @@ +SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE diff --git a/stake-pool/single-pool/src/entrypoint.rs b/single-pool/program/src/entrypoint.rs similarity index 100% rename from stake-pool/single-pool/src/entrypoint.rs rename to single-pool/program/src/entrypoint.rs diff --git a/stake-pool/single-pool/src/error.rs b/single-pool/program/src/error.rs similarity index 98% rename from stake-pool/single-pool/src/error.rs rename to single-pool/program/src/error.rs index 43c2a871d1d..ef620c7027d 100644 --- a/stake-pool/single-pool/src/error.rs +++ b/single-pool/program/src/error.rs @@ -55,8 +55,8 @@ pub enum SinglePoolError { #[error("SignatureMissing")] SignatureMissing, /// Stake account is not in the state expected by the program. - #[error("WrongStakeState")] - WrongStakeState, + #[error("WrongStakeStake")] + WrongStakeStake, /// Unsigned subtraction crossed the zero. #[error("ArithmeticOverflow")] ArithmeticOverflow, @@ -127,7 +127,7 @@ impl PrintProgramError for SinglePoolError { msg!("Error: Not enough stake to cover the provided quantity of pool tokens. \ (Generally this should not happen absent user error, but may if the minimum delegation increases.)"), SinglePoolError::SignatureMissing => msg!("Error: Required signature is missing."), - SinglePoolError::WrongStakeState => msg!("Error: Stake account is not in the state expected by the program."), + SinglePoolError::WrongStakeStake => msg!("Error: Stake account is not in the state expected by the program."), SinglePoolError::ArithmeticOverflow => msg!("Error: Unsigned subtraction crossed the zero."), SinglePoolError::UnexpectedMathError => msg!("Error: A calculation failed unexpectedly. \ diff --git a/single-pool/program/src/inline_mpl_token_metadata.rs b/single-pool/program/src/inline_mpl_token_metadata.rs new file mode 120000 index 00000000000..144225f4bfc --- /dev/null +++ b/single-pool/program/src/inline_mpl_token_metadata.rs @@ -0,0 +1 @@ +../../../stake-pool/program/src/inline_mpl_token_metadata.rs \ No newline at end of file diff --git a/stake-pool/single-pool/src/instruction.rs b/single-pool/program/src/instruction.rs similarity index 88% rename from stake-pool/single-pool/src/instruction.rs rename to single-pool/program/src/instruction.rs index 306a0e959eb..d9f440efed4 100644 --- a/stake-pool/single-pool/src/instruction.rs +++ b/single-pool/program/src/instruction.rs @@ -24,7 +24,7 @@ use { #[repr(C)] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)] pub enum SinglePoolInstruction { - /// Initialize the mint and stake account for a new single-validator pool. + /// Initialize the mint and stake account for a new single-validator stake pool. /// The pool stake account must contain the rent-exempt minimum plus the minimum delegation. /// No tokens will be minted: to deposit more, use `Deposit` after `InitializeStake`. /// @@ -43,6 +43,19 @@ pub enum SinglePoolInstruction { /// 12. `[]` Stake program InitializePool, + /// Restake the pool stake account if it was deactivated. This can happen through the + /// stake program's `DeactivateDelinquent` instruction, or during a cluster restart. + /// + /// 0. `[]` Validator vote account + /// 1. `[]` Pool account + /// 2. `[w]` Pool stake account + /// 3. `[]` Pool stake authority + /// 4. `[]` Clock sysvar + /// 5. `[]` Stake history sysvar + /// 6. `[]` Stake config sysvar + /// 7. `[]` Stake program + ReactivatePoolStake, + /// Deposit stake into the pool. The output is a "pool" token representing fractional /// ownership of the pool stake. Inputs are converted to the current ratio. /// @@ -124,7 +137,7 @@ pub fn initialize( let pool_rent = rent.minimum_balance(std::mem::size_of::()); let stake_address = find_pool_stake_address(program_id, &pool_address); - let stake_space = std::mem::size_of::(); + let stake_space = std::mem::size_of::(); let stake_rent_plus_minimum = rent .minimum_balance(stake_space) .saturating_add(minimum_delegation); @@ -163,6 +176,7 @@ pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> In AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(spl_token::id(), false), @@ -176,6 +190,35 @@ pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> In } } +/// Creates a `ReactivatePoolStake` instruction. +pub fn reactivate_pool_stake(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction { + let pool_address = find_pool_address(program_id, vote_account_address); + + let data = SinglePoolInstruction::ReactivatePoolStake + .try_to_vec() + .unwrap(); + let accounts = vec![ + AccountMeta::new_readonly(*vote_account_address, false), + AccountMeta::new_readonly(pool_address, false), + AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false), + AccountMeta::new_readonly( + find_pool_stake_authority_address(program_id, &pool_address), + false, + ), + AccountMeta::new_readonly(sysvar::clock::id(), false), + AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] + AccountMeta::new_readonly(stake::config::id(), false), + AccountMeta::new_readonly(stake::program::id(), false), + ]; + + Instruction { + program_id: *program_id, + accounts, + data, + } +} + /// Creates all necessary instructions to deposit stake. pub fn deposit( program_id: &Pubkey, @@ -252,7 +295,7 @@ pub fn deposit_stake( /// Creates all necessary instructions to withdraw stake into a given stake account. /// If a new stake account is required, the user should first include `system_instruction::create_account` -/// with account size `std::mem::size_of::()` and owner `stake::program::id()`. +/// with account size `std::mem::size_of::()` and owner `stake::program::id()`. pub fn withdraw( program_id: &Pubkey, pool_address: &Pubkey, @@ -329,24 +372,26 @@ pub fn withdraw_stake( /// Uses a fixed address for each wallet and vote account combination to make it easier to find for deposits. /// This is an optional helper function; deposits can come from any owned stake account without lockup. pub fn create_and_delegate_user_stake( - pool_address: &Pubkey, + program_id: &Pubkey, + vote_account_address: &Pubkey, user_wallet: &Pubkey, rent: &Rent, stake_amount: u64, ) -> Vec { - let stake_space = std::mem::size_of::(); + let pool_address = find_pool_address(program_id, vote_account_address); + let stake_space = std::mem::size_of::(); let lamports = rent .minimum_balance(stake_space) .saturating_add(stake_amount); let (deposit_address, deposit_seed) = - find_default_deposit_account_address_and_seed(pool_address, user_wallet); + find_default_deposit_account_address_and_seed(&pool_address, user_wallet); stake::instruction::create_account_with_seed_and_delegate_stake( user_wallet, &deposit_address, user_wallet, &deposit_seed, - pool_address, + vote_account_address, &stake::state::Authorized::auto(user_wallet), &stake::state::Lockup::default(), lamports, diff --git a/stake-pool/single-pool/src/lib.rs b/single-pool/program/src/lib.rs similarity index 81% rename from stake-pool/single-pool/src/lib.rs rename to single-pool/program/src/lib.rs index 60937420054..f34bd52b517 100644 --- a/stake-pool/single-pool/src/lib.rs +++ b/single-pool/program/src/lib.rs @@ -15,9 +15,7 @@ pub mod entrypoint; pub use solana_program; use solana_program::{pubkey::Pubkey, stake}; -// XXX TODO FIXME change this -// (XXX ask how do we as a company handle privkeys for our onchain programs?) -solana_program::declare_id!("3cqnsMsT6LE96pxv7GR4di5rLqHDZZbR3FbeSUeRLFqY"); +solana_program::declare_id!("SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE"); const POOL_PREFIX: &[u8] = b"pool"; const POOL_STAKE_PREFIX: &[u8] = b"stake"; @@ -32,45 +30,46 @@ const VOTE_STATE_DISCRIMINATOR_END: usize = 4; const VOTE_STATE_AUTHORIZED_WITHDRAWER_START: usize = 36; const VOTE_STATE_AUTHORIZED_WITHDRAWER_END: usize = 68; -fn find_address_and_bump( - program_id: &Pubkey, - pool_address: &Pubkey, - prefix: &[u8], -) -> (Pubkey, u8) { - Pubkey::find_program_address(&[prefix, pool_address.as_ref()], program_id) -} - fn find_pool_address_and_bump(program_id: &Pubkey, vote_account_address: &Pubkey) -> (Pubkey, u8) { - find_address_and_bump(program_id, vote_account_address, POOL_PREFIX) + Pubkey::find_program_address(&[POOL_PREFIX, vote_account_address.as_ref()], program_id) } fn find_pool_stake_address_and_bump(program_id: &Pubkey, pool_address: &Pubkey) -> (Pubkey, u8) { - find_address_and_bump(program_id, pool_address, POOL_STAKE_PREFIX) + Pubkey::find_program_address(&[POOL_STAKE_PREFIX, pool_address.as_ref()], program_id) } fn find_pool_mint_address_and_bump(program_id: &Pubkey, pool_address: &Pubkey) -> (Pubkey, u8) { - find_address_and_bump(program_id, pool_address, POOL_MINT_PREFIX) + Pubkey::find_program_address(&[POOL_MINT_PREFIX, pool_address.as_ref()], program_id) } fn find_pool_stake_authority_address_and_bump( program_id: &Pubkey, pool_address: &Pubkey, ) -> (Pubkey, u8) { - find_address_and_bump(program_id, pool_address, POOL_STAKE_AUTHORITY_PREFIX) + Pubkey::find_program_address( + &[POOL_STAKE_AUTHORITY_PREFIX, pool_address.as_ref()], + program_id, + ) } fn find_pool_mint_authority_address_and_bump( program_id: &Pubkey, pool_address: &Pubkey, ) -> (Pubkey, u8) { - find_address_and_bump(program_id, pool_address, POOL_MINT_AUTHORITY_PREFIX) + Pubkey::find_program_address( + &[POOL_MINT_AUTHORITY_PREFIX, pool_address.as_ref()], + program_id, + ) } fn find_pool_mpl_authority_address_and_bump( program_id: &Pubkey, pool_address: &Pubkey, ) -> (Pubkey, u8) { - find_address_and_bump(program_id, pool_address, POOL_MPL_AUTHORITY_PREFIX) + Pubkey::find_program_address( + &[POOL_MPL_AUTHORITY_PREFIX, pool_address.as_ref()], + program_id, + ) } fn find_default_deposit_account_address_and_seed( diff --git a/stake-pool/single-pool/src/processor.rs b/single-pool/program/src/processor.rs similarity index 94% rename from stake-pool/single-pool/src/processor.rs rename to single-pool/program/src/processor.rs index f4b9e2e90fb..501c966f061 100644 --- a/stake-pool/single-pool/src/processor.rs +++ b/single-pool/program/src/processor.rs @@ -19,7 +19,7 @@ use { borsh::BorshDeserialize, solana_program::{ account_info::{next_account_info, AccountInfo}, - borsh::{get_packed_len, try_from_slice_unchecked}, + borsh0_10::{get_packed_len, try_from_slice_unchecked}, entrypoint::ProgramResult, msg, native_token::LAMPORTS_PER_SOL, @@ -30,7 +30,7 @@ use { rent::Rent, stake::{ self, - state::{Meta, Stake, StakeState}, + state::{Meta, Stake, StakeStateV2}, }, stake_history::Epoch, system_instruction, system_program, @@ -75,11 +75,11 @@ fn calculate_withdraw_amount( /// Deserialize the stake state from AccountInfo fn get_stake_state(stake_account_info: &AccountInfo) -> Result<(Meta, Stake), ProgramError> { - let stake_state = try_from_slice_unchecked::(&stake_account_info.data.borrow())?; + let stake_state = try_from_slice_unchecked::(&stake_account_info.data.borrow())?; match stake_state { - StakeState::Stake(meta, stake) => Ok((meta, stake)), - _ => Err(SinglePoolError::WrongStakeState.into()), + StakeStateV2::Stake(meta, stake, _) => Ok((meta, stake)), + _ => Err(SinglePoolError::WrongStakeStake.into()), } } @@ -652,7 +652,7 @@ impl Processor { // create the pool stake account. user has already transferred in rent plus at least the minimum let minimum_delegation = minimum_delegation()?; - let stake_space = std::mem::size_of::(); + let stake_space = std::mem::size_of::(); let stake_rent_plus_initial = rent .minimum_balance(stake_space) .saturating_add(minimum_delegation); @@ -707,6 +707,67 @@ impl Processor { Ok(()) } + fn process_reactivate_pool_stake( + program_id: &Pubkey, + accounts: &[AccountInfo], + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let vote_account_info = next_account_info(account_info_iter)?; + let pool_info = next_account_info(account_info_iter)?; + let pool_stake_info = next_account_info(account_info_iter)?; + let pool_stake_authority_info = next_account_info(account_info_iter)?; + let clock_info = next_account_info(account_info_iter)?; + let clock = &Clock::from_account_info(clock_info)?; + let stake_history_info = next_account_info(account_info_iter)?; + let stake_config_info = next_account_info(account_info_iter)?; + let stake_program_info = next_account_info(account_info_iter)?; + + check_vote_account(vote_account_info)?; + check_pool_address(program_id, vote_account_info.key, pool_info.key)?; + + SinglePool::from_account_info(pool_info, program_id)?; + + check_pool_stake_address(program_id, pool_info.key, pool_stake_info.key)?; + let stake_authority_bump_seed = check_pool_stake_authority_address( + program_id, + pool_info.key, + pool_stake_authority_info.key, + )?; + check_stake_program(stake_program_info.key)?; + + let (_, pool_stake_state) = get_stake_state(pool_stake_info)?; + if pool_stake_state.delegation.deactivation_epoch > clock.epoch { + return Err(SinglePoolError::WrongStakeStake.into()); + } + + let stake_authority_seeds = &[ + POOL_STAKE_AUTHORITY_PREFIX, + pool_info.key.as_ref(), + &[stake_authority_bump_seed], + ]; + let stake_authority_signers = &[&stake_authority_seeds[..]]; + + // delegate stake so it activates + invoke_signed( + &stake::instruction::delegate_stake( + pool_stake_info.key, + pool_stake_authority_info.key, + vote_account_info.key, + ), + &[ + pool_stake_info.clone(), + vote_account_info.clone(), + clock_info.clone(), + stake_history_info.clone(), + stake_config_info.clone(), + pool_stake_authority_info.clone(), + ], + stake_authority_signers, + )?; + + Ok(()) + } + fn process_deposit_stake(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let pool_info = next_account_info(account_info_iter)?; @@ -754,11 +815,13 @@ impl Processor { msg!("Available stake pre merge {}", pre_pool_stake); // user can deposit active stake into an active pool or inactive stake into an activating pool - let (_, user_stake_state) = get_stake_state(user_stake_info)?; - if is_stake_active_without_history(&pool_stake_state, clock.epoch) - != is_stake_active_without_history(&user_stake_state, clock.epoch) + let (user_stake_meta, user_stake_state) = get_stake_state(user_stake_info)?; + if user_stake_meta.authorized + != stake::state::Authorized::auto(pool_stake_authority_info.key) + || is_stake_active_without_history(&pool_stake_state, clock.epoch) + != is_stake_active_without_history(&user_stake_state, clock.epoch) { - return Err(SinglePoolError::WrongStakeState.into()); + return Err(SinglePoolError::WrongStakeStake.into()); } // merge the user stake account, which is preauthed to us, into the pool stake account @@ -1118,6 +1181,10 @@ impl Processor { msg!("Instruction: InitializePool"); Self::process_initialize_pool(program_id, accounts) } + SinglePoolInstruction::ReactivatePoolStake => { + msg!("Instruction: ReactivatePoolStake"); + Self::process_reactivate_pool_stake(program_id, accounts) + } SinglePoolInstruction::DepositStake => { msg!("Instruction: DepositStake"); Self::process_deposit_stake(program_id, accounts) @@ -1147,7 +1214,7 @@ impl Processor { } #[cfg(test)] -#[allow(clippy::integer_arithmetic)] +#[allow(clippy::arithmetic_side_effects)] mod tests { use { super::*, diff --git a/stake-pool/single-pool/src/state.rs b/single-pool/program/src/state.rs similarity index 93% rename from stake-pool/single-pool/src/state.rs rename to single-pool/program/src/state.rs index 285ed75fc86..0174263fe63 100644 --- a/stake-pool/single-pool/src/state.rs +++ b/single-pool/program/src/state.rs @@ -4,8 +4,8 @@ use { crate::{error::SinglePoolError, find_pool_address}, borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, solana_program::{ - account_info::AccountInfo, borsh::try_from_slice_unchecked, program_error::ProgramError, - pubkey::Pubkey, + account_info::AccountInfo, borsh0_10::try_from_slice_unchecked, + program_error::ProgramError, pubkey::Pubkey, }, }; diff --git a/stake-pool/single-pool/tests/accounts.rs b/single-pool/program/tests/accounts.rs similarity index 93% rename from stake-pool/single-pool/tests/accounts.rs rename to single-pool/program/tests/accounts.rs index a3115983feb..5d8a928e0bb 100644 --- a/stake-pool/single-pool/tests/accounts.rs +++ b/single-pool/program/tests/accounts.rs @@ -1,4 +1,5 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; @@ -10,7 +11,7 @@ use { instruction::Instruction, program_error::ProgramError, pubkey::Pubkey, signature::Signer, stake, system_program, transaction::Transaction, }, - spl_single_validator_pool::{ + spl_single_pool::{ error::SinglePoolError, id, instruction::{self, SinglePoolInstruction}, @@ -33,8 +34,8 @@ async fn build_instructions( test_mode: TestMode, ) -> (Vec, usize) { let initialize_instructions = if test_mode == TestMode::Initialize { - let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); create_vote( &mut context.banks_client, @@ -196,6 +197,7 @@ async fn fail_account_checks(test_mode: TestMode) { // make an individual instruction for all program instructions // the match is just so this will error if new instructions are added +// if you are reading this because of that error, add the case to the `consistent_account_order` test!!! fn make_basic_instruction( accounts: &SinglePoolAccounts, instruction_type: SinglePoolInstruction, @@ -204,6 +206,9 @@ fn make_basic_instruction( SinglePoolInstruction::InitializePool => { instruction::initialize_pool(&id(), &accounts.vote_account.pubkey()) } + SinglePoolInstruction::ReactivatePoolStake => { + instruction::reactivate_pool_stake(&id(), &accounts.vote_account.pubkey()) + } SinglePoolInstruction::DepositStake => instruction::deposit_stake( &id(), &accounts.pool, @@ -258,6 +263,7 @@ fn consistent_account_order() { let instructions = vec![ make_basic_instruction(&accounts, SinglePoolInstruction::InitializePool), + make_basic_instruction(&accounts, SinglePoolInstruction::ReactivatePoolStake), make_basic_instruction(&accounts, SinglePoolInstruction::DepositStake), make_basic_instruction( &accounts, diff --git a/stake-pool/single-pool/tests/create_pool_token_metadata.rs b/single-pool/program/tests/create_pool_token_metadata.rs similarity index 95% rename from stake-pool/single-pool/tests/create_pool_token_metadata.rs rename to single-pool/program/tests/create_pool_token_metadata.rs index 40728753631..ac499cb2dd5 100644 --- a/stake-pool/single-pool/tests/create_pool_token_metadata.rs +++ b/single-pool/program/tests/create_pool_token_metadata.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -10,7 +10,7 @@ use { instruction::InstructionError, pubkey::Pubkey, signature::Signer, system_instruction::SystemError, transaction::Transaction, }, - spl_single_validator_pool::{id, instruction}, + spl_single_pool::{id, instruction}, }; fn assert_metadata(vote_account: &Pubkey, metadata: &Metadata) { diff --git a/stake-pool/single-pool/tests/deposit.rs b/single-pool/program/tests/deposit.rs similarity index 95% rename from stake-pool/single-pool/tests/deposit.rs rename to single-pool/program/tests/deposit.rs index 36ab53c033b..e0ae74d05f3 100644 --- a/stake-pool/single-pool/tests/deposit.rs +++ b/single-pool/program/tests/deposit.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,17 +7,13 @@ use { helpers::*, solana_program_test::*, solana_sdk::{ - instruction::InstructionError, signature::Signer, signer::keypair::Keypair, - stake::{ - instruction::StakeError, - state::{Authorized, Lockup}, - }, + stake::state::{Authorized, Lockup}, transaction::Transaction, }, spl_associated_token_account as atoken, - spl_single_validator_pool::{ + spl_single_pool::{ error::SinglePoolError, find_default_deposit_account_address, id, instruction, }, test_case::test_case, @@ -169,12 +165,11 @@ async fn success_with_seed(activate: bool) { let accounts = SinglePoolAccounts::default(); let rent = context.banks_client.get_rent().await.unwrap(); let minimum_stake = accounts.initialize(&mut context).await; - let alice_default_stake = find_default_deposit_account_address( - &accounts.vote_account.pubkey(), - &accounts.alice.pubkey(), - ); + let alice_default_stake = + find_default_deposit_account_address(&accounts.pool, &accounts.alice.pubkey()); let instructions = instruction::create_and_delegate_user_stake( + &id(), &accounts.vote_account.pubkey(), &accounts.alice.pubkey(), &rent, @@ -269,8 +264,8 @@ async fn fail_uninitialized(activate: bool) { let accounts = SinglePoolAccounts::default(); let stake_account = Keypair::new(); - let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); create_vote( &mut context.banks_client, @@ -378,7 +373,7 @@ async fn fail_bad_account(activate: bool, automorph: bool) { if automorph { check_error(e, SinglePoolError::InvalidPoolStakeAccountUsage); } else { - check_error::(e, StakeError::MergeMismatch.into()); + check_error(e, SinglePoolError::WrongStakeStake); } } @@ -459,5 +454,5 @@ async fn fail_activation_mismatch(pool_first: bool) { .process_transaction(transaction) .await .unwrap_err(); - check_error(e, SinglePoolError::WrongStakeState); + check_error(e, SinglePoolError::WrongStakeStake); } diff --git a/single-pool/program/tests/fixtures/mpl_token_metadata.so b/single-pool/program/tests/fixtures/mpl_token_metadata.so new file mode 120000 index 00000000000..43816797329 --- /dev/null +++ b/single-pool/program/tests/fixtures/mpl_token_metadata.so @@ -0,0 +1 @@ +../../../../stake-pool/program/tests/fixtures/mpl_token_metadata.so \ No newline at end of file diff --git a/stake-pool/single-pool/tests/helpers/mod.rs b/single-pool/program/tests/helpers/mod.rs similarity index 97% rename from stake-pool/single-pool/tests/helpers/mod.rs rename to single-pool/program/tests/helpers/mod.rs index 20adfa02878..c35d30a8386 100644 --- a/stake-pool/single-pool/tests/helpers/mod.rs +++ b/single-pool/program/tests/helpers/mod.rs @@ -17,7 +17,7 @@ use { vote_state::{VoteInit, VoteState, VoteStateVersions}, }, spl_associated_token_account as atoken, - spl_single_validator_pool::{ + spl_single_pool::{ find_pool_address, find_pool_mint_address, find_pool_mint_authority_address, find_pool_mpl_authority_address, find_pool_stake_address, find_pool_stake_authority_address, id, inline_mpl_token_metadata, instruction, @@ -38,11 +38,7 @@ pub fn program_test() -> ProgramTest { let mut program_test = ProgramTest::default(); program_test.add_program("mpl_token_metadata", inline_mpl_token_metadata::id(), None); - program_test.add_program( - "spl_single_validator_pool", - id(), - processor!(Processor::process), - ); + program_test.add_program("spl_single_pool", id(), processor!(Processor::process)); program_test.prefer_bpf(false); program_test @@ -213,8 +209,8 @@ impl SinglePoolAccounts { // creates a vote account and stake pool for it. also sets up two users with sol and token accounts // note this leaves the pool in an activating state. caller can advance to next epoch if they please pub async fn initialize(&self, context: &mut ProgramTestContext) -> u64 { - let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); create_vote( &mut context.banks_client, diff --git a/stake-pool/single-pool/tests/helpers/stake.rs b/single-pool/program/tests/helpers/stake.rs similarity index 91% rename from stake-pool/single-pool/tests/helpers/stake.rs rename to single-pool/program/tests/helpers/stake.rs index 1b1afdebca6..f2449e7fcbe 100644 --- a/stake-pool/single-pool/tests/helpers/stake.rs +++ b/single-pool/program/tests/helpers/stake.rs @@ -10,7 +10,7 @@ use { signature::{Keypair, Signer}, stake::{ self, - state::{Meta, Stake, StakeState}, + state::{Meta, Stake, StakeStateV2}, }, system_instruction, transaction::Transaction, @@ -26,16 +26,16 @@ pub async fn get_stake_account( ) -> (Meta, Option, u64) { let stake_account = get_account(banks_client, pubkey).await; let lamports = stake_account.lamports; - match deserialize::(&stake_account.data).unwrap() { - StakeState::Initialized(meta) => (meta, None, lamports), - StakeState::Stake(meta, stake) => (meta, Some(stake), lamports), + match deserialize::(&stake_account.data).unwrap() { + StakeStateV2::Initialized(meta) => (meta, None, lamports), + StakeStateV2::Stake(meta, stake, _) => (meta, Some(stake), lamports), _ => unimplemented!(), } } pub async fn get_stake_account_rent(banks_client: &mut BanksClient) -> u64 { let rent = banks_client.get_rent().await.unwrap(); - rent.minimum_balance(std::mem::size_of::()) + rent.minimum_balance(std::mem::size_of::()) } pub async fn get_minimum_delegation( @@ -104,7 +104,7 @@ pub async fn create_blank_stake_account( &rent_payer.pubkey(), &stake.pubkey(), lamports, - std::mem::size_of::() as u64, + std::mem::size_of::() as u64, &stake::program::id(), )], Some(&fee_payer.pubkey()), diff --git a/stake-pool/single-pool/tests/helpers/token.rs b/single-pool/program/tests/helpers/token.rs similarity index 94% rename from stake-pool/single-pool/tests/helpers/token.rs rename to single-pool/program/tests/helpers/token.rs index d3db254ccd0..dec49bca27e 100644 --- a/stake-pool/single-pool/tests/helpers/token.rs +++ b/single-pool/program/tests/helpers/token.rs @@ -4,7 +4,7 @@ use { borsh::BorshDeserialize, solana_program_test::BanksClient, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, hash::Hash, program_pack::Pack, pubkey::Pubkey, @@ -12,7 +12,7 @@ use { transaction::Transaction, }, spl_associated_token_account as atoken, - spl_single_validator_pool::inline_mpl_token_metadata::pda::find_metadata_account, + spl_single_pool::inline_mpl_token_metadata::pda::find_metadata_account, spl_token::state::{Account, Mint}, }; diff --git a/stake-pool/single-pool/tests/initialize.rs b/single-pool/program/tests/initialize.rs similarity index 91% rename from stake-pool/single-pool/tests/initialize.rs rename to single-pool/program/tests/initialize.rs index af8334f6847..e4b42bf1677 100644 --- a/stake-pool/single-pool/tests/initialize.rs +++ b/single-pool/program/tests/initialize.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{program_pack::Pack, signature::Signer, stake, transaction::Transaction}, - spl_single_validator_pool::{error::SinglePoolError, id, instruction}, + spl_single_pool::{error::SinglePoolError, id, instruction}, spl_token::state::Mint, }; @@ -55,3 +55,5 @@ async fn fail_double_init() { .unwrap_err(); check_error(e, SinglePoolError::PoolAlreadyInitialized); } + +// TODO test that init can succeed without mpl program diff --git a/single-pool/program/tests/reactivate.rs b/single-pool/program/tests/reactivate.rs new file mode 100644 index 00000000000..1c03b39805e --- /dev/null +++ b/single-pool/program/tests/reactivate.rs @@ -0,0 +1,156 @@ +#![allow(clippy::arithmetic_side_effects)] +#![cfg(feature = "test-sbf")] + +mod helpers; + +use { + helpers::*, + solana_program_test::*, + solana_sdk::{ + account::AccountSharedData, + signature::Signer, + stake::{ + stake_flags::StakeFlags, + state::{Delegation, Stake, StakeStateV2}, + }, + transaction::Transaction, + }, + spl_single_pool::{error::SinglePoolError, id, instruction}, + test_case::test_case, +}; + +#[tokio::test] +async fn success() { + let mut context = program_test().start_with_context().await; + let accounts = SinglePoolAccounts::default(); + accounts + .initialize_for_deposit(&mut context, TEST_STAKE_AMOUNT, None) + .await; + advance_epoch(&mut context).await; + + // deactivate the pool stake account + let (meta, stake, _) = + get_stake_account(&mut context.banks_client, &accounts.stake_account).await; + let delegation = Delegation { + activation_epoch: 0, + deactivation_epoch: 0, + ..stake.unwrap().delegation + }; + let mut account_data = vec![0; std::mem::size_of::()]; + bincode::serialize_into( + &mut account_data[..], + &StakeStateV2::Stake( + meta, + Stake { + delegation, + ..stake.unwrap() + }, + StakeFlags::empty(), + ), + ) + .unwrap(); + + let mut stake_account = get_account(&mut context.banks_client, &accounts.stake_account).await; + stake_account.data = account_data; + context.set_account( + &accounts.stake_account, + &AccountSharedData::from(stake_account), + ); + + // make sure deposit fails + let instructions = instruction::deposit( + &id(), + &accounts.pool, + &accounts.alice_stake.pubkey(), + &accounts.alice_token, + &accounts.alice.pubkey(), + &accounts.alice.pubkey(), + ); + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&context.payer.pubkey()), + &[&context.payer, &accounts.alice], + context.last_blockhash, + ); + + let e = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err(); + check_error(e, SinglePoolError::WrongStakeStake); + + // reactivate + let instruction = instruction::reactivate_pool_stake(&id(), &accounts.vote_account.pubkey()); + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + advance_epoch(&mut context).await; + + // deposit works again + let instructions = instruction::deposit( + &id(), + &accounts.pool, + &accounts.alice_stake.pubkey(), + &accounts.alice_token, + &accounts.alice.pubkey(), + &accounts.alice.pubkey(), + ); + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&context.payer.pubkey()), + &[&context.payer, &accounts.alice], + context.last_blockhash, + ); + + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + assert!(context + .banks_client + .get_account(accounts.alice_stake.pubkey()) + .await + .expect("get_account") + .is_none()); +} + +#[test_case(true; "activated")] +#[test_case(false; "activating")] +#[tokio::test] +async fn fail_not_deactivated(activate: bool) { + let mut context = program_test().start_with_context().await; + let accounts = SinglePoolAccounts::default(); + accounts.initialize(&mut context).await; + + if activate { + advance_epoch(&mut context).await; + } + + let instruction = instruction::reactivate_pool_stake(&id(), &accounts.vote_account.pubkey()); + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + let e = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err(); + check_error(e, SinglePoolError::WrongStakeStake); +} diff --git a/stake-pool/single-pool/tests/update_pool_token_metadata.rs b/single-pool/program/tests/update_pool_token_metadata.rs similarity index 97% rename from stake-pool/single-pool/tests/update_pool_token_metadata.rs rename to single-pool/program/tests/update_pool_token_metadata.rs index b6f7e0e22ea..3f6e86d61b5 100644 --- a/stake-pool/single-pool/tests/update_pool_token_metadata.rs +++ b/single-pool/program/tests/update_pool_token_metadata.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{signature::Signer, transaction::Transaction}, - spl_single_validator_pool::{error::SinglePoolError, id, instruction}, + spl_single_pool::{error::SinglePoolError, id, instruction}, test_case::test_case, }; diff --git a/stake-pool/single-pool/tests/withdraw.rs b/single-pool/program/tests/withdraw.rs similarity index 98% rename from stake-pool/single-pool/tests/withdraw.rs rename to single-pool/program/tests/withdraw.rs index 7a851e89531..d92c85dfb51 100644 --- a/stake-pool/single-pool/tests/withdraw.rs +++ b/single-pool/program/tests/withdraw.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{signature::Signer, transaction::Transaction}, - spl_single_validator_pool::{error::SinglePoolError, id, instruction}, + spl_single_pool::{error::SinglePoolError, id, instruction}, test_case::test_case, }; diff --git a/stake-pool/cli/Cargo.toml b/stake-pool/cli/Cargo.toml index ff95defd68c..f559672df19 100644 --- a/stake-pool/cli/Cargo.toml +++ b/stake-pool/cli/Cargo.toml @@ -11,19 +11,19 @@ version = "0.7.0" [dependencies] borsh = "0.10" clap = "2.33.3" -serde = "1.0.164" +serde = "1.0.190" serde_derive = "1.0.130" -serde_json = "1.0.99" -solana-account-decoder = "=1.16.1" -solana-clap-utils = "=1.16.1" -solana-cli-config = "=1.16.1" -solana-cli-output = "=1.16.1" -solana-client = "=1.16.1" -solana-logger = "=1.16.1" -solana-program = "=1.16.1" -solana-remote-wallet = "=1.16.1" -solana-sdk = "=1.16.1" -spl-associated-token-account = { version = "=2.0", path="../../associated-token-account/program", features = [ "no-entrypoint" ] } +serde_json = "1.0.108" +solana-account-decoder = "=1.17.2" +solana-clap-utils = "=1.17.2" +solana-cli-config = "=1.17.2" +solana-cli-output = "=1.17.2" +solana-client = "=1.17.2" +solana-logger = "=1.17.2" +solana-program = "=1.17.2" +solana-remote-wallet = "=1.17.2" +solana-sdk = "=1.17.2" +spl-associated-token-account = { version = "=2.2", path="../../associated-token-account/program", features = [ "no-entrypoint" ] } spl-stake-pool = { version = "=0.7.0", path="../program", features = [ "no-entrypoint" ] } spl-token = { version = "=4.0", path="../../token/program", features = [ "no-entrypoint" ] } bs58 = "0.4.0" diff --git a/stake-pool/cli/src/client.rs b/stake-pool/cli/src/client.rs index 039d057cef8..9deae1a807c 100644 --- a/stake-pool/cli/src/client.rs +++ b/stake-pool/cli/src/client.rs @@ -7,7 +7,9 @@ use { rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, rpc_filter::{Memcmp, RpcFilterType}, }, - solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey, stake}, + solana_program::{ + borsh0_10::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey, stake, + }, spl_stake_pool::{ find_withdraw_authority_program_address, state::{StakePool, ValidatorList}, @@ -71,7 +73,7 @@ pub fn get_token_mint( pub(crate) fn get_stake_state( rpc_client: &RpcClient, stake_address: &Pubkey, -) -> Result { +) -> Result { let account_data = rpc_client.get_account_data(stake_address)?; let stake_state = deserialize(account_data.as_slice()) .map_err(|err| format!("Invalid stake account {}: {}", stake_address, err))?; diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 2319c6e3461..0edaafbc1bf 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] mod client; mod output; @@ -23,7 +23,7 @@ use { solana_cli_output::OutputFormat, solana_client::rpc_client::RpcClient, solana_program::{ - borsh::{get_instance_packed_len, get_packed_len}, + borsh0_10::{get_instance_packed_len, get_packed_len}, instruction::Instruction, program_pack::Pack, pubkey::Pubkey, @@ -51,7 +51,7 @@ use { MINIMUM_RESERVE_LAMPORTS, }, std::cmp::Ordering, - std::{num::NonZeroU32, process::exit, sync::Arc}, + std::{num::NonZeroU32, process::exit, rc::Rc}, }; // use instruction::create_associated_token_account once ATA 1.0.5 is released #[allow(deprecated)] @@ -127,7 +127,7 @@ fn get_signer( matches: &ArgMatches<'_>, keypair_name: &str, keypair_path: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, signer_from_path_config: SignerFromPathConfig, ) -> Box { signer_from_path_with_config( @@ -499,7 +499,7 @@ fn command_vsa_remove( .find(vote_account) .ok_or("Vote account not found in validator list")?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); let (stake_account_address, _) = find_stake_program_address( &spl_stake_pool::id(), vote_account, @@ -520,7 +520,7 @@ fn command_vsa_remove( stake_pool_address, vote_account, validator_seed, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), ), ]; unique_signers!(signers); @@ -545,7 +545,7 @@ fn command_increase_validator_stake( let validator_stake_info = validator_list .find(vote_account) .ok_or("Vote account not found in validator list")?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; unique_signers!(signers); @@ -559,7 +559,7 @@ fn command_increase_validator_stake( vote_account, lamports, validator_seed, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), ), ], &signers, @@ -584,7 +584,7 @@ fn command_decrease_validator_stake( let validator_stake_info = validator_list .find(vote_account) .ok_or("Vote account not found in validator list")?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; unique_signers!(signers); @@ -598,7 +598,7 @@ fn command_decrease_validator_stake( vote_account, lamports, validator_seed, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), ), ], &signers, @@ -683,7 +683,7 @@ fn command_deposit_stake( println!("Depositing stake account {:?}", stake_state); } let vote_account = match stake_state { - stake::state::StakeState::Stake(_, stake) => Ok(stake.delegation.voter_pubkey), + stake::state::StakeStateV2::Stake(_, stake, _) => Ok(stake.delegation.voter_pubkey), _ => Err("Wrong stake account state, must be delegated to validator"), }?; @@ -692,7 +692,7 @@ fn command_deposit_stake( let validator_stake_info = validator_list .find(&vote_account) .ok_or("Vote account not found in the stake pool")?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); // Calculate validator stake account address linked to the pool let (validator_stake_account, _) = find_stake_program_address( @@ -865,14 +865,14 @@ fn command_deposit_all_stake( let stake_state = get_stake_state(&config.rpc_client, &stake_address)?; let vote_account = match stake_state { - stake::state::StakeState::Stake(_, stake) => Ok(stake.delegation.voter_pubkey), + stake::state::StakeStateV2::Stake(_, stake, _) => Ok(stake.delegation.voter_pubkey), _ => Err("Wrong stake account state, must be delegated to validator"), }?; let validator_stake_info = validator_list .find(&vote_account) .ok_or("Vote account not found in the stake pool")?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); // Calculate validator stake account address linked to the pool let (validator_stake_account, _) = find_stake_program_address( @@ -1084,7 +1084,7 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { .validators .iter() .map(|validator| { - let validator_seed = NonZeroU32::new(validator.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator.validator_seed_suffix.into()); let (stake_account_address, _) = find_stake_program_address( &spl_stake_pool::id(), &validator.vote_account_address, @@ -1095,18 +1095,18 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { &spl_stake_pool::id(), &validator.vote_account_address, stake_pool_address, - validator.transient_seed_suffix, + validator.transient_seed_suffix.into(), ); - let update_required = validator.last_update_epoch != epoch_info.epoch; + let update_required = u64::from(validator.last_update_epoch) != epoch_info.epoch; CliStakePoolStakeAccountInfo { vote_account_address: validator.vote_account_address.to_string(), stake_account_address: stake_account_address.to_string(), - validator_active_stake_lamports: validator.active_stake_lamports, - validator_last_update_epoch: validator.last_update_epoch, + validator_active_stake_lamports: validator.active_stake_lamports.into(), + validator_last_update_epoch: validator.last_update_epoch.into(), validator_lamports: validator.stake_lamports().unwrap(), validator_transient_stake_account_address: transient_stake_account_address .to_string(), - validator_transient_stake_lamports: validator.transient_stake_lamports, + validator_transient_stake_lamports: validator.transient_stake_lamports.into(), update_required, } }) @@ -1255,7 +1255,7 @@ fn prepare_withdraw_accounts( &validator_list, stake_pool, |validator| { - let validator_seed = NonZeroU32::new(validator.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator.validator_seed_suffix.into()); let (stake_account_address, _) = find_stake_program_address( &spl_stake_pool::id(), &validator.vote_account_address, @@ -1265,7 +1265,7 @@ fn prepare_withdraw_accounts( ( stake_account_address, - validator.active_stake_lamports, + validator.active_stake_lamports.into(), Some(validator.vote_account_address), ) }, @@ -1279,14 +1279,12 @@ fn prepare_withdraw_accounts( &spl_stake_pool::id(), &validator.vote_account_address, stake_pool_address, - validator.transient_seed_suffix, + validator.transient_seed_suffix.into(), ); ( transient_stake_account_address, - validator - .transient_stake_lamports - .saturating_sub(min_balance), + u64::from(validator.transient_stake_lamports).saturating_sub(min_balance), Some(validator.vote_account_address), ) }, @@ -1401,9 +1399,12 @@ fn command_withdraw_stake( let maybe_stake_receiver_state = stake_receiver_param .map(|stake_receiver_pubkey| { let stake_account = config.rpc_client.get_account(&stake_receiver_pubkey).ok()?; - let stake_state: stake::state::StakeState = deserialize(stake_account.data.as_slice()) - .map_err(|err| format!("Invalid stake account {}: {}", stake_receiver_pubkey, err)) - .ok()?; + let stake_state: stake::state::StakeStateV2 = + deserialize(stake_account.data.as_slice()) + .map_err(|err| { + format!("Invalid stake account {}: {}", stake_receiver_pubkey, err) + }) + .ok()?; if stake_state.delegation().is_some() && stake_account.owner == stake::program::id() { Some(stake_state) } else { @@ -1438,7 +1439,7 @@ fn command_withdraw_stake( let validator_stake_info = validator_list .find(&vote_account) .ok_or(format!("Provided stake account is delegated to a vote account {} which does not exist in the stake pool", vote_account))?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); let (stake_account_address, _) = find_stake_program_address( &spl_stake_pool::id(), &vote_account, @@ -1474,7 +1475,7 @@ fn command_withdraw_stake( "Provided vote account address {} does not exist in the stake pool", vote_account_address ))?; - let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix); + let validator_seed = NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()); let (stake_account_address, _) = find_stake_program_address( &spl_stake_pool::id(), vote_account_address, @@ -1940,6 +1941,7 @@ fn main() { .value_name("URL") .takes_value(true) .validator(is_url) + .global(true) .help("JSON RPC URL for the cluster. Default from the configuration file."), ) .arg( @@ -1948,6 +1950,7 @@ fn main() { .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .global(true) .help("Stake pool staker. [default: cli config keypair]"), ) .arg( @@ -1956,6 +1959,7 @@ fn main() { .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .global(true) .help("Stake pool manager. [default: cli config keypair]"), ) .arg( @@ -1964,6 +1968,7 @@ fn main() { .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .global(true) .help("Stake pool funding authority for deposits or withdrawals. [default: cli config keypair]"), ) .arg( @@ -1972,6 +1977,7 @@ fn main() { .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .global(true) .help("Owner of pool token account [default: cli config keypair]"), ) .arg( @@ -1980,6 +1986,7 @@ fn main() { .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .global(true) .help("Transaction fee payer account [default: cli config keypair]"), ) .subcommand(SubCommand::with_name("create-pool") diff --git a/stake-pool/cli/src/output.rs b/stake-pool/cli/src/output.rs index 097e93340cc..f1305245628 100644 --- a/stake-pool/cli/src/output.rs +++ b/stake-pool/cli/src/output.rs @@ -3,7 +3,9 @@ use { solana_cli_output::{QuietDisplay, VerboseDisplay}, solana_sdk::native_token::Sol, solana_sdk::{pubkey::Pubkey, stake::state::Lockup}, - spl_stake_pool::state::{Fee, StakePool, StakeStatus, ValidatorList, ValidatorStakeInfo}, + spl_stake_pool::state::{ + Fee, PodStakeStatus, StakePool, StakeStatus, ValidatorList, ValidatorStakeInfo, + }, std::fmt::{Display, Formatter, Result, Write}, }; @@ -372,20 +374,21 @@ pub(crate) struct CliStakePoolValidator { impl From for CliStakePoolValidator { fn from(v: ValidatorStakeInfo) -> Self { Self { - active_stake_lamports: v.active_stake_lamports, - transient_stake_lamports: v.transient_stake_lamports, - last_update_epoch: v.last_update_epoch, - transient_seed_suffix: v.transient_seed_suffix, - unused: v.unused, - validator_seed_suffix: v.validator_seed_suffix, + active_stake_lamports: v.active_stake_lamports.into(), + transient_stake_lamports: v.transient_stake_lamports.into(), + last_update_epoch: v.last_update_epoch.into(), + transient_seed_suffix: v.transient_seed_suffix.into(), + unused: v.unused.into(), + validator_seed_suffix: v.validator_seed_suffix.into(), status: CliStakePoolValidatorStakeStatus::from(v.status), vote_account_address: v.vote_account_address.to_string(), } } } -impl From for CliStakePoolValidatorStakeStatus { - fn from(s: StakeStatus) -> CliStakePoolValidatorStakeStatus { +impl From for CliStakePoolValidatorStakeStatus { + fn from(s: PodStakeStatus) -> CliStakePoolValidatorStakeStatus { + let s = StakeStatus::try_from(s).unwrap(); match s { StakeStatus::Active => CliStakePoolValidatorStakeStatus::Active, StakeStatus::DeactivatingTransient => { diff --git a/stake-pool/js/package-lock.json b/stake-pool/js/package-lock.json index d6bd4724d45..b449e8bda49 100644 --- a/stake-pool/js/package-lock.json +++ b/stake-pool/js/package-lock.json @@ -1,15 +1,15 @@ { "name": "@solana/spl-stake-pool", - "version": "0.6.5", + "version": "0.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@solana/spl-stake-pool", - "version": "0.6.5", + "version": "0.7.0", "license": "ISC", "dependencies": { - "@coral-xyz/borsh": "^0.28.0", + "@coral-xyz/borsh": "^0.29.0", "@solana/buffer-layout": "^4.0.1", "@solana/spl-token": "^0.3.7", "@solana/web3.js": "^1.76.0", @@ -22,24 +22,24 @@ "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-multi-entry": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.2", + "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.0", "@types/bn.js": "^5.1.0", "@types/jest": "^27.4.0", "@types/node": "^20.1.2", "@types/node-fetch": "^2.6.3", - "@typescript-eslint/eslint-plugin": "^5.59.5", - "@typescript-eslint/parser": "^5.59.5", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "cross-env": "^7.0.3", "eslint": "^8.40.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "jest": "^27.5.1", - "prettier": "^2.8.8", + "prettier": "^3.0.0", "rimraf": "^5.0.0", - "rollup": "^2.66.1", - "rollup-plugin-dts": "^4.2.3", + "rollup": "^4.0.2", + "rollup-plugin-dts": "^6.1.0", "rollup-plugin-node-polyfills": "^0.2.1", - "rollup-plugin-terser": "^7.0.2", "ts-jest": "^27.1.3", "typescript": "^4.5.4" } @@ -66,17 +66,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.17.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", @@ -126,28 +198,20 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", @@ -176,50 +240,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -278,21 +326,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -322,13 +379,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -407,9 +464,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -581,44 +638,44 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -636,12 +693,13 @@ } }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -655,9 +713,9 @@ "dev": true }, "node_modules/@coral-xyz/borsh": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz", - "integrity": "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz", + "integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==", "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" @@ -685,18 +743,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -717,21 +775,21 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -753,9 +811,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -823,9 +881,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -1224,29 +1282,26 @@ } }, "node_modules/@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dependencies": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1293,10 +1348,30 @@ "node": ">=14" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.2.12", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rollup/plugin-alias": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.0.0.tgz", - "integrity": "sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.0.1.tgz", + "integrity": "sha512-JObvbWdOHoMy9W7SU0lvGhDtWq9PllP5mjpAy+TUslZG/WzOId9u80Hsqq1vCUn9pFJ0cxpdcnAv+QzU2zFH3Q==", "dev": true, "dependencies": { "slash": "^4.0.0" @@ -1305,7 +1380,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1326,9 +1401,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.2.tgz", - "integrity": "sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow==", + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -1336,13 +1411,13 @@ "estree-walker": "^2.0.2", "glob": "^8.0.3", "is-reference": "1.2.1", - "magic-string": "^0.27.0" + "magic-string": "^0.30.3" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.68.0||^3.0.0" + "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1350,6 +1425,12 @@ } } }, + "node_modules/@rollup/plugin-commonjs/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1379,12 +1460,12 @@ } }, "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -1403,9 +1484,9 @@ } }, "node_modules/@rollup/plugin-json": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", - "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.1.tgz", + "integrity": "sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1" @@ -1414,7 +1495,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1423,9 +1504,9 @@ } }, "node_modules/@rollup/plugin-multi-entry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-multi-entry/-/plugin-multi-entry-6.0.0.tgz", - "integrity": "sha512-msBgVncGQwh/ahxeP/rc8MXVZNBOjoVCsBuDk6uqyFzDv/SZN7jksfAsu6DJ2w4r5PaBX3/OXOjVPeCxya2waA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-multi-entry/-/plugin-multi-entry-6.0.1.tgz", + "integrity": "sha512-AXm6toPyTSfbYZWghQGbom1Uh7dHXlrGa+HoiYNhQtDUE3Q7LqoUYdVQx9E1579QWS1uOiu+cZRSE4okO7ySgw==", "dev": true, "dependencies": { "@rollup/plugin-virtual": "^3.0.0", @@ -1435,7 +1516,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1444,9 +1525,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz", - "integrity": "sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==", + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -1460,7 +1541,29 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.78.0||^3.0.0" + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1468,10 +1571,19 @@ } } }, + "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/@rollup/plugin-typescript": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.2.tgz", - "integrity": "sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz", + "integrity": "sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -1481,7 +1593,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.14.0||^3.0.0", + "rollup": "^2.14.0||^3.0.0||^4.0.0", "tslib": "*", "typescript": ">=3.7.0" }, @@ -1495,15 +1607,15 @@ } }, "node_modules/@rollup/plugin-virtual": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.1.tgz", - "integrity": "sha512-fK8O0IL5+q+GrsMLuACVNk2x21g3yaw+sG2qn16SnUd3IlBsQyvWxLMGHmCmXRMecPjGRSZ/1LmZB4rjQm68og==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", + "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==", "dev": true, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1512,9 +1624,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", + "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", @@ -1525,7 +1637,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -1533,6 +1645,162 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.2.0.tgz", + "integrity": "sha512-8PlggAxGxavr+pkCNeV1TM2wTb2o+cUWDg9M1cm9nR27Dsn287uZtSLYXoQqQcmq+sYfF7lHfd3sWJJinH9GmA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.2.0.tgz", + "integrity": "sha512-+71T85hbMFrJI+zKQULNmSYBeIhru55PYoF/u75MyeN2FcxE4HSPw20319b+FcZ4lWx2Nx/Ql9tN+hoaD3GH/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.2.0.tgz", + "integrity": "sha512-IIIQLuG43QIElT1JZqUP/zqIdiJl4t9U/boa0GZnQTw9m1X0k3mlBuysbgYXeloLT1RozdL7bgw4lpSaI8GOXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.2.0.tgz", + "integrity": "sha512-BXcXvnLaea1Xz900omrGJhxHFJfH9jZ0CpJuVsbjjhpniJ6qiLXz3xA8Lekaa4MuhFcJd4f0r+Ky1G4VFbYhWw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.2.0.tgz", + "integrity": "sha512-f4K3MKw9Y4AKi4ANGnmPIglr+S+8tO858YrGVuqAHXxJdVghBmz9CPU9kDpOnGvT4g4vg5uNyIFpOOFvffXyMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.2.0.tgz", + "integrity": "sha512-bNsTYQBgp4H7w6cT7FZhesxpcUPahsSIy4NgdZjH1ZwEoZHxi4XKglj+CsSEkhsKi+x6toVvMylhjRKhEMYfnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.2.0.tgz", + "integrity": "sha512-Jp1NxBJpGLuxRU2ihrQk4IZ+ia5nffobG6sOFUPW5PMYkF0kQtxEbeDuCa69Xif211vUOcxlOnf5IOEIpTEySA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.2.0.tgz", + "integrity": "sha512-3p3iRtQmv2aXw+vtKNyZMLOQ+LSRsqArXjKAh2Oj9cqwfIRe7OXvdkOzWfZOIp1F/x5KJzVAxGxnniF4cMbnsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.2.0.tgz", + "integrity": "sha512-atih7IF/reUZe4LBLC5Izd44hth2tfDIG8LaPp4/cQXdHh9jabcZEvIeRPrpDq0i/Uu487Qu5gl5KwyAnWajnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.2.0.tgz", + "integrity": "sha512-vYxF3tKJeUE4ceYzpNe2p84RXk/fGK30I8frpRfv/MyPStej/mRlojztkN7Jtd1014HHVeq/tYaMBz/3IxkxZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.2.0.tgz", + "integrity": "sha512-1LZJ6zpl93SaPQvas618bMFarVwufWTaczH4ESAbFcwiC4OtznA6Ym+hFPyIGaJaGEB8uMWWac0uXGPXOg5FGA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.2.0.tgz", + "integrity": "sha512-dgQfFdHCNg08nM5zBmqxqc9vrm0DVzhWotpavbPa0j4//MAOKZEB75yGAfzQE9fUJ+4pvM1239Y4IhL8f6sSog==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1593,23 +1861,23 @@ } }, "node_modules/@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -1665,9 +1933,9 @@ } }, "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.4.tgz", + "integrity": "sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA==", "dev": true, "dependencies": { "@types/node": "*" @@ -1731,24 +1999,41 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", + "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==", "dev": true, "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/@types/prettier": { @@ -1764,9 +2049,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1799,32 +2084,33 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1833,25 +2119,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1859,17 +2146,91 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1877,25 +2238,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1904,12 +2265,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1917,21 +2278,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1944,48 +2305,53 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -2295,6 +2661,15 @@ } ] }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/bigint-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", @@ -2338,6 +2713,18 @@ "text-encoding-utf-8": "^1.0.2" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2480,6 +2867,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2677,74 +3079,230 @@ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, "dependencies": { - "ms": "2.1.2" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=12" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/delay": { @@ -2933,15 +3491,6 @@ "source-map": "~0.6.1" } }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -2994,27 +3543,28 @@ } }, "node_modules/eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3024,7 +3574,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -3036,7 +3585,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -3050,9 +3598,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -3062,44 +3610,43 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -3107,15 +3654,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -3123,15 +3666,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3194,9 +3728,9 @@ } }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -3235,15 +3769,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -3256,7 +3781,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -3265,15 +3790,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -3362,9 +3878,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3517,9 +4033,9 @@ } }, "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { "node": ">=14" @@ -3640,9 +4156,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3680,12 +4196,6 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3807,9 +4317,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -3908,6 +4418,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3947,6 +4472,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -4004,6 +4547,33 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4094,9 +4664,9 @@ } }, "node_modules/jackspeak": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.0.tgz", - "integrity": "sha512-r5XBrqIJfwRIjRt/Xr5fv9Wh09qyhHfKnYddDlpM+ibRR20qrYActpCAgU6U+d53EOEjzkvxPMVHSlgR7leXrQ==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.5.tgz", + "integrity": "sha512-Ratx+B8WeXLAtRJn26hrhY8S1+Jz6pxPMrkrdkgb/NstTNiqMhX0/oFVu5wX+g5n6JlEu2LPsDJmY8nRP4+alw==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -5102,9 +5672,9 @@ } }, "node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -5121,16 +5691,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -5239,6 +5803,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5362,13 +5944,13 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -5378,9 +5960,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -5444,15 +6026,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -5524,6 +6106,12 @@ "node": ">=6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5560,9 +6148,9 @@ "dev": true }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/require-directory": { "version": "2.1.1", @@ -5573,6 +6161,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -5640,15 +6234,15 @@ } }, "node_modules/rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^10.2.5" + "glob": "^10.3.7" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": ">=14" @@ -5667,19 +6261,19 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.5.tgz", - "integrity": "sha512-Gj+dFYPZ5hc5dazjXzB0iHg2jKWJZYMjITXYPBRQ/xc2Buw7H0BINknRTwURJ6IC6MEFpYbLvtgVb3qD+DwyuA==", + "version": "10.3.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.9.tgz", + "integrity": "sha512-2tU/LKevAQvDVuVJ9pg9Yv9xcbSh+TqHuTaXTNbQwf+0kDl9Fm6bMovi4Nm5c8TVvfxo2LLcqCGtmO9KoJaGWg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -5689,9 +6283,9 @@ } }, "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5704,49 +6298,68 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.2.0.tgz", + "integrity": "sha512-deaMa9Z+jPVeBD2dKXv+h7EbdKte9++V2potc/ADqvVgEr6DEJ3ia9u0joarjC2lX/ubaCRYz3QVx0TzuVqAJA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.2.0", + "@rollup/rollup-android-arm64": "4.2.0", + "@rollup/rollup-darwin-arm64": "4.2.0", + "@rollup/rollup-darwin-x64": "4.2.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.2.0", + "@rollup/rollup-linux-arm64-gnu": "4.2.0", + "@rollup/rollup-linux-arm64-musl": "4.2.0", + "@rollup/rollup-linux-x64-gnu": "4.2.0", + "@rollup/rollup-linux-x64-musl": "4.2.0", + "@rollup/rollup-win32-arm64-msvc": "4.2.0", + "@rollup/rollup-win32-ia32-msvc": "4.2.0", + "@rollup/rollup-win32-x64-msvc": "4.2.0", "fsevents": "~2.3.2" } }, "node_modules/rollup-plugin-dts": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.3.tgz", - "integrity": "sha512-jlcpItqM2efqfIiKzDB/IKOS9E9fDvbkJSGw5GtK/PqPGS9eC3R3JKyw2VvpTktZA+TNgJRMu1NTv244aTUzzQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", + "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", "dev": true, "dependencies": { - "magic-string": "^0.26.6" + "magic-string": "^0.30.4" }, "engines": { - "node": ">=v12.22.12" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/Swatinem" }, "optionalDependencies": { - "@babel/code-frame": "^7.18.6" + "@babel/code-frame": "^7.22.13" }, "peerDependencies": { - "rollup": "^2.55", - "typescript": "^4.1" + "rollup": "^3.29.4 || ^4", + "typescript": "^4.5 || ^5.0" } }, + "node_modules/rollup-plugin-dts/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "node_modules/rollup-plugin-dts/node_modules/magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "version": "0.30.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", + "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -5779,35 +6392,6 @@ "rollup-plugin-inject": "^3.0.0" } }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, "node_modules/rollup-pluginutils": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", @@ -5862,6 +6446,21 @@ } } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5923,27 +6522,18 @@ } }, "node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -5988,6 +6578,12 @@ "node": ">=8" } }, + "node_modules/smob": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", + "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", + "dev": true + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6185,6 +6781,22 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -6202,13 +6814,13 @@ } }, "node_modules/terser": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz", - "integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", + "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -6255,6 +6867,18 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -6283,14 +6907,15 @@ } }, "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { "node": ">=6" @@ -6308,6 +6933,18 @@ "node": ">=8" } }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "27.1.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", @@ -6356,29 +6993,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "node_modules/type-check": { @@ -6436,15 +7050,29 @@ "node": ">=4.2.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "engines": { "node": ">= 4.0.0" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6454,6 +7082,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", @@ -6582,9 +7220,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6747,12 +7385,71 @@ } }, "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -6793,22 +7490,15 @@ } }, "@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" } }, "@babel/helper-compilation-targets": { @@ -6832,41 +7522,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true }, "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -6910,18 +7587,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -6942,13 +7625,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -7011,9 +7694,9 @@ } }, "@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -7134,38 +7817,38 @@ } }, "@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -7179,12 +7862,13 @@ } }, "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -7195,9 +7879,9 @@ "dev": true }, "@coral-xyz/borsh": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz", - "integrity": "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz", + "integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==", "requires": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" @@ -7213,15 +7897,15 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -7236,18 +7920,18 @@ } }, "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" } @@ -7259,9 +7943,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@isaacs/cliui": { @@ -7308,9 +7992,9 @@ } }, "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { "ansi-regex": "^6.0.1" @@ -7627,17 +8311,17 @@ } }, "@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "requires": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" } }, "@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -7672,10 +8356,24 @@ "dev": true, "optional": true }, + "@pkgr/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.2.12", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.5.0" + } + }, "@rollup/plugin-alias": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.0.0.tgz", - "integrity": "sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.0.1.tgz", + "integrity": "sha512-JObvbWdOHoMy9W7SU0lvGhDtWq9PllP5mjpAy+TUslZG/WzOId9u80Hsqq1vCUn9pFJ0cxpdcnAv+QzU2zFH3Q==", "dev": true, "requires": { "slash": "^4.0.0" @@ -7690,9 +8388,9 @@ } }, "@rollup/plugin-commonjs": { - "version": "25.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.2.tgz", - "integrity": "sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow==", + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", "dev": true, "requires": { "@rollup/pluginutils": "^5.0.1", @@ -7700,9 +8398,15 @@ "estree-walker": "^2.0.2", "glob": "^8.0.3", "is-reference": "1.2.1", - "magic-string": "^0.27.0" + "magic-string": "^0.30.3" }, "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -7726,12 +8430,12 @@ } }, "magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" } }, "minimatch": { @@ -7746,65 +8450,171 @@ } }, "@rollup/plugin-json": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", - "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.1.tgz", + "integrity": "sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==", "dev": true, "requires": { "@rollup/pluginutils": "^5.0.1" } }, "@rollup/plugin-multi-entry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-multi-entry/-/plugin-multi-entry-6.0.0.tgz", - "integrity": "sha512-msBgVncGQwh/ahxeP/rc8MXVZNBOjoVCsBuDk6uqyFzDv/SZN7jksfAsu6DJ2w4r5PaBX3/OXOjVPeCxya2waA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-multi-entry/-/plugin-multi-entry-6.0.1.tgz", + "integrity": "sha512-AXm6toPyTSfbYZWghQGbom1Uh7dHXlrGa+HoiYNhQtDUE3Q7LqoUYdVQx9E1579QWS1uOiu+cZRSE4okO7ySgw==", "dev": true, "requires": { "@rollup/plugin-virtual": "^3.0.0", "matched": "^5.0.1" } }, - "@rollup/plugin-node-resolve": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz", - "integrity": "sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==", + "@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + } + }, + "@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "requires": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "dependencies": { + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } + } + }, + "@rollup/plugin-typescript": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz", + "integrity": "sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "resolve": "^1.22.1" + } + }, + "@rollup/plugin-virtual": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", + "integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==", + "dev": true, + "requires": {} + }, + "@rollup/pluginutils": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", + "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.2.0.tgz", + "integrity": "sha512-8PlggAxGxavr+pkCNeV1TM2wTb2o+cUWDg9M1cm9nR27Dsn287uZtSLYXoQqQcmq+sYfF7lHfd3sWJJinH9GmA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.2.0.tgz", + "integrity": "sha512-+71T85hbMFrJI+zKQULNmSYBeIhru55PYoF/u75MyeN2FcxE4HSPw20319b+FcZ4lWx2Nx/Ql9tN+hoaD3GH/A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.2.0.tgz", + "integrity": "sha512-IIIQLuG43QIElT1JZqUP/zqIdiJl4t9U/boa0GZnQTw9m1X0k3mlBuysbgYXeloLT1RozdL7bgw4lpSaI8GOXw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.2.0.tgz", + "integrity": "sha512-BXcXvnLaea1Xz900omrGJhxHFJfH9jZ0CpJuVsbjjhpniJ6qiLXz3xA8Lekaa4MuhFcJd4f0r+Ky1G4VFbYhWw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.2.0.tgz", + "integrity": "sha512-f4K3MKw9Y4AKi4ANGnmPIglr+S+8tO858YrGVuqAHXxJdVghBmz9CPU9kDpOnGvT4g4vg5uNyIFpOOFvffXyMA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.2.0.tgz", + "integrity": "sha512-bNsTYQBgp4H7w6cT7FZhesxpcUPahsSIy4NgdZjH1ZwEoZHxi4XKglj+CsSEkhsKi+x6toVvMylhjRKhEMYfnA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.2.0.tgz", + "integrity": "sha512-Jp1NxBJpGLuxRU2ihrQk4IZ+ia5nffobG6sOFUPW5PMYkF0kQtxEbeDuCa69Xif211vUOcxlOnf5IOEIpTEySA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.2.0.tgz", + "integrity": "sha512-3p3iRtQmv2aXw+vtKNyZMLOQ+LSRsqArXjKAh2Oj9cqwfIRe7OXvdkOzWfZOIp1F/x5KJzVAxGxnniF4cMbnsQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.2.0.tgz", + "integrity": "sha512-atih7IF/reUZe4LBLC5Izd44hth2tfDIG8LaPp4/cQXdHh9jabcZEvIeRPrpDq0i/Uu487Qu5gl5KwyAnWajnw==", "dev": true, - "requires": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - } + "optional": true }, - "@rollup/plugin-typescript": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.2.tgz", - "integrity": "sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==", + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.2.0.tgz", + "integrity": "sha512-vYxF3tKJeUE4ceYzpNe2p84RXk/fGK30I8frpRfv/MyPStej/mRlojztkN7Jtd1014HHVeq/tYaMBz/3IxkxZw==", "dev": true, - "requires": { - "@rollup/pluginutils": "^5.0.1", - "resolve": "^1.22.1" - } + "optional": true }, - "@rollup/plugin-virtual": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.1.tgz", - "integrity": "sha512-fK8O0IL5+q+GrsMLuACVNk2x21g3yaw+sG2qn16SnUd3IlBsQyvWxLMGHmCmXRMecPjGRSZ/1LmZB4rjQm68og==", + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.2.0.tgz", + "integrity": "sha512-1LZJ6zpl93SaPQvas618bMFarVwufWTaczH4ESAbFcwiC4OtznA6Ym+hFPyIGaJaGEB8uMWWac0uXGPXOg5FGA==", "dev": true, - "requires": {} + "optional": true }, - "@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "@rollup/rollup-win32-x64-msvc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.2.0.tgz", + "integrity": "sha512-dgQfFdHCNg08nM5zBmqxqc9vrm0DVzhWotpavbPa0j4//MAOKZEB75yGAfzQE9fUJ+4pvM1239Y4IhL8f6sSog==", "dev": true, - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - } + "optional": true }, "@sinonjs/commons": { "version": "1.8.3", @@ -7854,23 +8664,23 @@ } }, "@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "requires": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -7923,9 +8733,9 @@ } }, "@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.4.tgz", + "integrity": "sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA==", "dev": true, "requires": { "@types/node": "*" @@ -7989,24 +8799,40 @@ } }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", + "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "requires": { + "undici-types": "~5.26.4" + } }, "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==", "dev": true, "requires": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } } }, "@types/prettier": { @@ -8022,9 +8848,9 @@ "dev": true }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "@types/stack-utils": { @@ -8057,104 +8883,154 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" + } + }, + "@typescript-eslint/types": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" + } + } } }, "@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" } }, "@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -8379,6 +9255,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, "bigint-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", @@ -8415,6 +9297,15 @@ "text-encoding-utf-8": "^1.0.2" } }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8514,6 +9405,15 @@ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8727,6 +9627,101 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "dependencies": { + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -8861,12 +9856,6 @@ "source-map": "~0.6.1" }, "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -8909,27 +9898,28 @@ } }, "eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8939,7 +9929,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -8951,26 +9940,9 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9011,41 +9983,42 @@ } }, "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "requires": {} }, "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" } }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { "acorn": "^8.9.0", @@ -9066,14 +10039,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -9083,20 +10048,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "estree-walker": { @@ -9169,9 +10126,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -9298,9 +10255,9 @@ }, "dependencies": { "signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true } } @@ -9383,9 +10340,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -9411,12 +10368,6 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -9503,9 +10454,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -9574,6 +10525,12 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -9601,6 +10558,15 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -9646,6 +10612,23 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -9718,9 +10701,9 @@ } }, "jackspeak": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.0.tgz", - "integrity": "sha512-r5XBrqIJfwRIjRt/Xr5fv9Wh09qyhHfKnYddDlpM+ibRR20qrYActpCAgU6U+d53EOEjzkvxPMVHSlgR7leXrQ==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.5.tgz", + "integrity": "sha512-Ratx+B8WeXLAtRJn26hrhY8S1+Jz6pxPMrkrdkgb/NstTNiqMhX0/oFVu5wX+g5n6JlEu2LPsDJmY8nRP4+alw==", "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", @@ -10499,9 +11482,9 @@ } }, "minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true }, "ms": { @@ -10515,16 +11498,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "requires": { "whatwg-url": "^5.0.0" }, @@ -10607,6 +11584,18 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -10697,19 +11686,19 @@ "dev": true }, "path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "requires": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true } } @@ -10754,9 +11743,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "prettier-linter-helpers": { @@ -10809,6 +11798,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -10831,9 +11826,9 @@ "dev": true }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "require-directory": { "version": "2.1.1", @@ -10841,6 +11836,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -10888,12 +11889,12 @@ "dev": true }, "rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "requires": { - "glob": "^10.2.5" + "glob": "^10.3.7" }, "dependencies": { "brace-expansion": { @@ -10906,22 +11907,22 @@ } }, "glob": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.5.tgz", - "integrity": "sha512-Gj+dFYPZ5hc5dazjXzB0iHg2jKWJZYMjITXYPBRQ/xc2Buw7H0BINknRTwURJ6IC6MEFpYbLvtgVb3qD+DwyuA==", + "version": "10.3.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.9.tgz", + "integrity": "sha512-2tU/LKevAQvDVuVJ9pg9Yv9xcbSh+TqHuTaXTNbQwf+0kDl9Fm6bMovi4Nm5c8TVvfxo2LLcqCGtmO9KoJaGWg==", "dev": true, "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.0", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" } }, "minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -10930,31 +11931,49 @@ } }, "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "requires": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.2.0.tgz", + "integrity": "sha512-deaMa9Z+jPVeBD2dKXv+h7EbdKte9++V2potc/ADqvVgEr6DEJ3ia9u0joarjC2lX/ubaCRYz3QVx0TzuVqAJA==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.2.0", + "@rollup/rollup-android-arm64": "4.2.0", + "@rollup/rollup-darwin-arm64": "4.2.0", + "@rollup/rollup-darwin-x64": "4.2.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.2.0", + "@rollup/rollup-linux-arm64-gnu": "4.2.0", + "@rollup/rollup-linux-arm64-musl": "4.2.0", + "@rollup/rollup-linux-x64-gnu": "4.2.0", + "@rollup/rollup-linux-x64-musl": "4.2.0", + "@rollup/rollup-win32-arm64-msvc": "4.2.0", + "@rollup/rollup-win32-ia32-msvc": "4.2.0", + "@rollup/rollup-win32-x64-msvc": "4.2.0", "fsevents": "~2.3.2" } }, "rollup-plugin-dts": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.3.tgz", - "integrity": "sha512-jlcpItqM2efqfIiKzDB/IKOS9E9fDvbkJSGw5GtK/PqPGS9eC3R3JKyw2VvpTktZA+TNgJRMu1NTv244aTUzzQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", + "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "magic-string": "^0.26.6" + "@babel/code-frame": "^7.22.13", + "magic-string": "^0.30.4" }, "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "version": "0.30.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", + "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", "dev": true, "requires": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.15" } } } @@ -10987,31 +12006,6 @@ "rollup-plugin-inject": "^3.0.0" } }, - "rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "dependencies": { - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - } - } - }, "rollup-pluginutils": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", @@ -11050,6 +12044,15 @@ } } }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11080,23 +12083,14 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11130,6 +12124,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "smob": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", + "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11279,6 +12279,16 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -11290,13 +12300,13 @@ } }, "terser": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz", - "integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", + "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" } @@ -11334,6 +12344,12 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -11356,14 +12372,15 @@ } }, "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "requires": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" } }, "tr46": { @@ -11375,6 +12392,13 @@ "punycode": "^2.1.1" } }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, "ts-jest": { "version": "27.1.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", @@ -11395,26 +12419,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true, - "optional": true, - "peer": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "dev": true }, "type-check": { "version": "0.4.0", @@ -11452,10 +12457,21 @@ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, "uri-js": { @@ -11467,6 +12483,16 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "utf-8-validate": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", @@ -11569,9 +12595,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wrap-ansi": { diff --git a/stake-pool/js/package.json b/stake-pool/js/package.json index 85546a954f0..86391ce31aa 100644 --- a/stake-pool/js/package.json +++ b/stake-pool/js/package.json @@ -1,6 +1,6 @@ { "name": "@solana/spl-stake-pool", - "version": "0.6.5", + "version": "0.7.0", "description": "SPL Stake Pool Program JS API", "scripts": { "build": "npm run clean && tsc && cross-env NODE_ENV=production rollup -c", @@ -44,7 +44,7 @@ ], "license": "ISC", "dependencies": { - "@coral-xyz/borsh": "^0.28.0", + "@coral-xyz/borsh": "^0.29.0", "@solana/buffer-layout": "^4.0.1", "@solana/spl-token": "^0.3.7", "@solana/web3.js": "^1.76.0", @@ -57,24 +57,24 @@ "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-multi-entry": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.2", + "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.0", "@types/bn.js": "^5.1.0", "@types/jest": "^27.4.0", "@types/node": "^20.1.2", "@types/node-fetch": "^2.6.3", - "@typescript-eslint/eslint-plugin": "^5.59.5", - "@typescript-eslint/parser": "^5.59.5", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "cross-env": "^7.0.3", "eslint": "^8.40.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "jest": "^27.5.1", - "prettier": "^2.8.8", + "prettier": "^3.0.0", "rimraf": "^5.0.0", - "rollup": "^2.66.1", - "rollup-plugin-dts": "^4.2.3", + "rollup": "^4.0.2", + "rollup-plugin-dts": "^6.1.0", "rollup-plugin-node-polyfills": "^0.2.1", - "rollup-plugin-terser": "^7.0.2", "ts-jest": "^27.1.3", "typescript": "^4.5.4" }, diff --git a/stake-pool/js/rollup.config.js b/stake-pool/js/rollup.config.mjs similarity index 98% rename from stake-pool/js/rollup.config.js rename to stake-pool/js/rollup.config.mjs index eb4aaccb851..1742dcd152c 100644 --- a/stake-pool/js/rollup.config.js +++ b/stake-pool/js/rollup.config.mjs @@ -2,7 +2,7 @@ import typescript from '@rollup/plugin-typescript'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; -import { terser } from 'rollup-plugin-terser'; +import terser from '@rollup/plugin-terser'; const extensions = ['.js', '.ts']; diff --git a/stake-pool/js/src/constants.ts b/stake-pool/js/src/constants.ts index 9423f7cef6f..d0f24ffa078 100644 --- a/stake-pool/js/src/constants.ts +++ b/stake-pool/js/src/constants.ts @@ -1,6 +1,12 @@ import { Buffer } from 'buffer'; import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; +// Public key that identifies the metadata program. +export const METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'); +export const METADATA_MAX_NAME_LENGTH = 32; +export const METADATA_MAX_SYMBOL_LENGTH = 10; +export const METADATA_MAX_URI_LENGTH = 200; + // Public key that identifies the SPL Stake Pool program. export const STAKE_POOL_PROGRAM_ID = new PublicKey('SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy'); diff --git a/stake-pool/js/src/index.ts b/stake-pool/js/src/index.ts index 9b77e2a39d5..faf95a5e363 100644 --- a/stake-pool/js/src/index.ts +++ b/stake-pool/js/src/index.ts @@ -28,6 +28,7 @@ import { lamportsToSol, solToLamports, findEphemeralStakeProgramAddress, + findMetadataAddress, } from './utils'; import { StakePoolInstruction } from './instructions'; import { @@ -794,6 +795,7 @@ export async function decreaseValidatorStake( stakePool: stakePoolAddress, staker: stakePool.account.data.staker, validatorList: stakePool.account.data.validatorList, + reserveStake: stakePool.account.data.reserveStake, transientStakeSeed: transientStakeSeed.toNumber(), withdrawAuthority, validatorStake, @@ -805,10 +807,11 @@ export async function decreaseValidatorStake( ); } else { instructions.push( - StakePoolInstruction.decreaseValidatorStake({ + StakePoolInstruction.decreaseValidatorStakeWithReserve({ stakePool: stakePoolAddress, staker: stakePool.account.data.staker, validatorList: stakePool.account.data.validatorList, + reserveStake: stakePool.account.data.reserveStake, transientStakeSeed: transientStakeSeed.toNumber(), withdrawAuthority, validatorStake, @@ -934,8 +937,9 @@ export async function stakePoolInfo(connection: Connection, stakePoolAddress: Pu stakePoolAddress, ); - const minimumReserveStakeBalance = - (await connection.getMinimumBalanceForRentExemption(StakeProgram.space)) + 1; + const minimumReserveStakeBalance = await connection.getMinimumBalanceForRentExemption( + StakeProgram.space, + ); const stakeAccounts = await Promise.all( validatorList.account.data.validators.map(async (validator) => { @@ -1090,6 +1094,7 @@ export async function redelegate(props: RedelegateProps) { stakePool: stakePool.pubkey, staker: stakePool.account.data.staker, validatorList: stakePool.account.data.validatorList, + reserveStake: stakePool.account.data.reserveStake, stakePoolWithdrawAuthority, ephemeralStake, ephemeralStakeSeed, @@ -1108,3 +1113,80 @@ export async function redelegate(props: RedelegateProps) { instructions, }; } + +/** + * Creates instructions required to create pool token metadata. + */ +export async function createPoolTokenMetadata( + connection: Connection, + stakePoolAddress: PublicKey, + payer: PublicKey, + name: string, + symbol: string, + uri: string, +) { + const stakePool = await getStakePoolAccount(connection, stakePoolAddress); + + const withdrawAuthority = await findWithdrawAuthorityProgramAddress( + STAKE_POOL_PROGRAM_ID, + stakePoolAddress, + ); + const tokenMetadata = findMetadataAddress(stakePool.account.data.poolMint); + const manager = stakePool.account.data.manager; + + const instructions: TransactionInstruction[] = []; + instructions.push( + StakePoolInstruction.createTokenMetadata({ + stakePool: stakePoolAddress, + poolMint: stakePool.account.data.poolMint, + payer, + manager, + tokenMetadata, + withdrawAuthority, + name, + symbol, + uri, + }), + ); + + return { + instructions, + }; +} + +/** + * Creates instructions required to update pool token metadata. + */ +export async function updatePoolTokenMetadata( + connection: Connection, + stakePoolAddress: PublicKey, + name: string, + symbol: string, + uri: string, +) { + const stakePool = await getStakePoolAccount(connection, stakePoolAddress); + + const withdrawAuthority = await findWithdrawAuthorityProgramAddress( + STAKE_POOL_PROGRAM_ID, + stakePoolAddress, + ); + + const tokenMetadata = findMetadataAddress(stakePool.account.data.poolMint); + + const instructions: TransactionInstruction[] = []; + instructions.push( + StakePoolInstruction.updateTokenMetadata({ + stakePool: stakePoolAddress, + manager: stakePool.account.data.manager, + tokenMetadata, + withdrawAuthority, + name, + symbol, + uri, + }), + ); + + return { + instructions, + }; +} diff --git a/stake-pool/js/src/instructions.ts b/stake-pool/js/src/instructions.ts index bfc35f389c8..9891f17b6c7 100644 --- a/stake-pool/js/src/instructions.ts +++ b/stake-pool/js/src/instructions.ts @@ -10,9 +10,15 @@ import { } from '@solana/web3.js'; import * as BufferLayout from '@solana/buffer-layout'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import { STAKE_POOL_PROGRAM_ID } from './constants'; import { InstructionType, encodeData, decodeData } from './utils'; import BN from 'bn.js'; +import { + METADATA_MAX_NAME_LENGTH, + METADATA_MAX_SYMBOL_LENGTH, + METADATA_MAX_URI_LENGTH, + METADATA_PROGRAM_ID, + STAKE_POOL_PROGRAM_ID, +} from './constants'; /** * An enumeration of valid StakePoolInstructionType's @@ -29,8 +35,11 @@ export type StakePoolInstructionType = | 'WithdrawSol' | 'IncreaseAdditionalValidatorStake' | 'DecreaseAdditionalValidatorStake' + | 'DecreaseValidatorStakeWithReserve' | 'Redelegate'; +// 'UpdateTokenMetadata' and 'CreateTokenMetadata' have dynamic layouts + const MOVE_STAKE_LAYOUT = BufferLayout.struct([ BufferLayout.u8('instruction'), BufferLayout.ns64('lamports'), @@ -43,6 +52,38 @@ const UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT = BufferLayout.struct([ BufferLayout.u8('noMerge'), ]); +export function tokenMetadataLayout( + instruction: number, + nameLength: number, + symbolLength: number, + uriLength: number, +) { + if (nameLength > METADATA_MAX_NAME_LENGTH) { + throw 'maximum token name length is 32 characters'; + } + + if (symbolLength > METADATA_MAX_SYMBOL_LENGTH) { + throw 'maximum token symbol length is 10 characters'; + } + + if (uriLength > METADATA_MAX_URI_LENGTH) { + throw 'maximum token uri length is 200 characters'; + } + + return { + index: instruction, + layout: BufferLayout.struct([ + BufferLayout.u8('instruction'), + BufferLayout.u32('nameLen'), + BufferLayout.blob(nameLength, 'name'), + BufferLayout.u32('symbolLen'), + BufferLayout.blob(symbolLength, 'symbol'), + BufferLayout.u32('uriLen'), + BufferLayout.blob(uriLength, 'uri'), + ]), + }; +} + /** * An enumeration of valid stake InstructionType's * @internal @@ -118,8 +159,12 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: { BufferLayout.ns64('ephemeralStakeSeed'), ]), }, - Redelegate: { + DecreaseValidatorStakeWithReserve: { index: 21, + layout: MOVE_STAKE_LAYOUT, + }, + Redelegate: { + index: 22, layout: BufferLayout.struct([ BufferLayout.u8('instruction'), /// Amount of lamports to redelegate @@ -185,7 +230,12 @@ export type DecreaseValidatorStakeParams = { transientStakeSeed: number; }; +export interface DecreaseValidatorStakeWithReserveParams extends DecreaseValidatorStakeParams { + reserveStake: PublicKey; +} + export interface DecreaseAdditionalValidatorStakeParams extends DecreaseValidatorStakeParams { + reserveStake: PublicKey; ephemeralStake: PublicKey; ephemeralStakeSeed: number; } @@ -285,6 +335,7 @@ export type RedelegateParams = { staker: PublicKey; stakePoolWithdrawAuthority: PublicKey; validatorList: PublicKey; + reserveStake: PublicKey; sourceValidatorStake: PublicKey; sourceTransientStake: PublicKey; ephemeralStake: PublicKey; @@ -303,6 +354,28 @@ export type RedelegateParams = { destinationTransientStakeSeed: number | BN; }; +export type CreateTokenMetadataParams = { + stakePool: PublicKey; + manager: PublicKey; + tokenMetadata: PublicKey; + withdrawAuthority: PublicKey; + poolMint: PublicKey; + payer: PublicKey; + name: string; + symbol: string; + uri: string; +}; + +export type UpdateTokenMetadataParams = { + stakePool: PublicKey; + manager: PublicKey; + tokenMetadata: PublicKey; + withdrawAuthority: PublicKey; + name: string; + symbol: string; + uri: string; +}; + /** * Stake Pool Instruction class */ @@ -537,6 +610,49 @@ export class StakePoolInstruction { }); } + /** + * Creates `DecreaseValidatorStakeWithReserve` instruction (rebalance from + * validator account to transient account) + */ + static decreaseValidatorStakeWithReserve( + params: DecreaseValidatorStakeWithReserveParams, + ): TransactionInstruction { + const { + stakePool, + staker, + withdrawAuthority, + validatorList, + reserveStake, + validatorStake, + transientStake, + lamports, + transientStakeSeed, + } = params; + + const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStakeWithReserve; + const data = encodeData(type, { lamports, transientStakeSeed }); + + const keys = [ + { pubkey: stakePool, isSigner: false, isWritable: false }, + { pubkey: staker, isSigner: true, isWritable: false }, + { pubkey: withdrawAuthority, isSigner: false, isWritable: false }, + { pubkey: validatorList, isSigner: false, isWritable: true }, + { pubkey: reserveStake, isSigner: false, isWritable: true }, + { pubkey: validatorStake, isSigner: false, isWritable: true }, + { pubkey: transientStake, isSigner: false, isWritable: true }, + { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, + { pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + { pubkey: StakeProgram.programId, isSigner: false, isWritable: false }, + ]; + + return new TransactionInstruction({ + programId: STAKE_POOL_PROGRAM_ID, + keys, + data, + }); + } + /** * Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from * validator account to transient account) @@ -549,6 +665,7 @@ export class StakePoolInstruction { staker, withdrawAuthority, validatorList, + reserveStake, validatorStake, transientStake, lamports, @@ -565,6 +682,7 @@ export class StakePoolInstruction { { pubkey: staker, isSigner: true, isWritable: false }, { pubkey: withdrawAuthority, isSigner: false, isWritable: false }, { pubkey: validatorList, isSigner: false, isWritable: true }, + { pubkey: reserveStake, isSigner: false, isWritable: true }, { pubkey: validatorStake, isSigner: false, isWritable: true }, { pubkey: ephemeralStake, isSigner: false, isWritable: true }, { pubkey: transientStake, isSigner: false, isWritable: true }, @@ -779,6 +897,7 @@ export class StakePoolInstruction { staker, stakePoolWithdrawAuthority, validatorList, + reserveStake, sourceValidatorStake, sourceTransientStake, ephemeralStake, @@ -796,6 +915,7 @@ export class StakePoolInstruction { { pubkey: staker, isSigner: true, isWritable: false }, { pubkey: stakePoolWithdrawAuthority, isSigner: false, isWritable: false }, { pubkey: validatorList, isSigner: false, isWritable: true }, + { pubkey: reserveStake, isSigner: false, isWritable: true }, { pubkey: sourceValidatorStake, isSigner: false, isWritable: true }, { pubkey: sourceTransientStake, isSigner: false, isWritable: true }, { pubkey: ephemeralStake, isSigner: false, isWritable: true }, @@ -823,6 +943,84 @@ export class StakePoolInstruction { }); } + /** + * Creates an instruction to create metadata + * using the mpl token metadata program for the pool token + */ + static createTokenMetadata(params: CreateTokenMetadataParams): TransactionInstruction { + const { + stakePool, + withdrawAuthority, + tokenMetadata, + manager, + payer, + poolMint, + name, + symbol, + uri, + } = params; + + const keys = [ + { pubkey: stakePool, isSigner: false, isWritable: false }, + { pubkey: manager, isSigner: true, isWritable: false }, + { pubkey: withdrawAuthority, isSigner: false, isWritable: false }, + { pubkey: poolMint, isSigner: false, isWritable: false }, + { pubkey: payer, isSigner: true, isWritable: true }, + { pubkey: tokenMetadata, isSigner: false, isWritable: true }, + { pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false }, + ]; + + const type = tokenMetadataLayout(17, name.length, symbol.length, uri.length); + const data = encodeData(type, { + nameLen: name.length, + name: Buffer.from(name), + symbolLen: symbol.length, + symbol: Buffer.from(symbol), + uriLen: uri.length, + uri: Buffer.from(uri), + }); + + return new TransactionInstruction({ + programId: STAKE_POOL_PROGRAM_ID, + keys, + data, + }); + } + + /** + * Creates an instruction to update metadata + * in the mpl token metadata program account for the pool token + */ + static updateTokenMetadata(params: UpdateTokenMetadataParams): TransactionInstruction { + const { stakePool, withdrawAuthority, tokenMetadata, manager, name, symbol, uri } = params; + + const keys = [ + { pubkey: stakePool, isSigner: false, isWritable: false }, + { pubkey: manager, isSigner: true, isWritable: false }, + { pubkey: withdrawAuthority, isSigner: false, isWritable: false }, + { pubkey: tokenMetadata, isSigner: false, isWritable: true }, + { pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false }, + ]; + + const type = tokenMetadataLayout(18, name.length, symbol.length, uri.length); + const data = encodeData(type, { + nameLen: name.length, + name: Buffer.from(name), + symbolLen: symbol.length, + symbol: Buffer.from(symbol), + uriLen: uri.length, + uri: Buffer.from(uri), + }); + + return new TransactionInstruction({ + programId: STAKE_POOL_PROGRAM_ID, + keys, + data, + }); + } + /** * Decode a deposit stake pool instruction and retrieve the instruction params. */ diff --git a/stake-pool/js/src/utils/program-address.ts b/stake-pool/js/src/utils/program-address.ts index 7d1982356f3..49d106b1090 100644 --- a/stake-pool/js/src/utils/program-address.ts +++ b/stake-pool/js/src/utils/program-address.ts @@ -1,7 +1,11 @@ import { PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import { Buffer } from 'buffer'; -import { EPHEMERAL_STAKE_SEED_PREFIX, TRANSIENT_STAKE_SEED_PREFIX } from '../constants'; +import { + METADATA_PROGRAM_ID, + EPHEMERAL_STAKE_SEED_PREFIX, + TRANSIENT_STAKE_SEED_PREFIX, +} from '../constants'; /** * Generates the withdraw authority program address for the stake pool @@ -67,3 +71,14 @@ export async function findEphemeralStakeProgramAddress( ); return publicKey; } + +/** + * Generates the metadata program address for the stake pool + */ +export function findMetadataAddress(stakePoolMintAddress: PublicKey) { + const [publicKey] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), stakePoolMintAddress.toBuffer()], + METADATA_PROGRAM_ID, + ); + return publicKey; +} diff --git a/stake-pool/js/src/utils/stake.ts b/stake-pool/js/src/utils/stake.ts index 9eef2c6c52e..25535431823 100644 --- a/stake-pool/js/src/utils/stake.ts +++ b/stake-pool/js/src/utils/stake.ts @@ -115,7 +115,7 @@ export async function prepareWithdrawAccounts( accounts = accounts.sort(compareFn ? compareFn : (a, b) => b.lamports - a.lamports); const reserveStake = await connection.getAccountInfo(stakePool.reserveStake); - const reserveStakeBalance = (reserveStake?.lamports ?? 0) - minBalanceForRentExemption - 1; + const reserveStakeBalance = (reserveStake?.lamports ?? 0) - minBalanceForRentExemption; if (reserveStakeBalance > 0) { accounts.push({ type: 'reserve', diff --git a/stake-pool/js/test/instructions.test.ts b/stake-pool/js/test/instructions.test.ts index 6552d0c3ee7..1a39ddffb49 100644 --- a/stake-pool/js/test/instructions.test.ts +++ b/stake-pool/js/test/instructions.test.ts @@ -26,6 +26,9 @@ import { withdrawStake, redelegate, getStakeAccount, + createPoolTokenMetadata, + updatePoolTokenMetadata, + tokenMetadataLayout, } from '../src'; import { decodeData } from '../src/utils'; @@ -344,11 +347,52 @@ describe('StakePoolProgram', () => { res.instructions[0].data, ); - expect(decodedData.instruction).toBe(21); + expect(decodedData.instruction).toBe(22); expect(decodedData.lamports).toBe(data.lamports); expect(decodedData.sourceTransientStakeSeed).toBe(data.sourceTransientStakeSeed); expect(decodedData.destinationTransientStakeSeed).toBe(data.destinationTransientStakeSeed); expect(decodedData.ephemeralStakeSeed).toBe(data.ephemeralStakeSeed); }); }); + describe('createPoolTokenMetadata', () => { + it('should create pool token metadata', async () => { + connection.getAccountInfo = jest.fn(async (pubKey: PublicKey) => { + if (pubKey == stakePoolAddress) { + return stakePoolAccount; + } + return null; + }); + const name = 'test'; + const symbol = 'TEST'; + const uri = 'https://example.com'; + + const payer = new PublicKey(0); + const res = await createPoolTokenMetadata( + connection, + stakePoolAddress, + payer, + name, + symbol, + uri, + ); + + const type = tokenMetadataLayout(17, name.length, symbol.length, uri.length); + const data = decodeData(type, res.instructions[0].data); + expect(Buffer.from(data.name).toString()).toBe(name); + expect(Buffer.from(data.symbol).toString()).toBe(symbol); + expect(Buffer.from(data.uri).toString()).toBe(uri); + }); + + it('should update pool token metadata', async () => { + const name = 'test'; + const symbol = 'TEST'; + const uri = 'https://example.com'; + const res = await updatePoolTokenMetadata(connection, stakePoolAddress, name, symbol, uri); + const type = tokenMetadataLayout(18, name.length, symbol.length, uri.length); + const data = decodeData(type, res.instructions[0].data); + expect(Buffer.from(data.name).toString()).toBe(name); + expect(Buffer.from(data.symbol).toString()).toBe(symbol); + expect(Buffer.from(data.uri).toString()).toBe(uri); + }); + }); }); diff --git a/stake-pool/program/Cargo.toml b/stake-pool/program/Cargo.toml index f0e0ee54fdb..f2bdc7e588d 100644 --- a/stake-pool/program/Cargo.toml +++ b/stake-pool/program/Cargo.toml @@ -14,24 +14,27 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" borsh = "0.10" +bytemuck = "1.13" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" -serde = "1.0.164" +num_enum = "0.7.1" +serde = "1.0.190" serde_derive = "1.0.103" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-math = { version = "0.2", path = "../../libraries/math", features = [ "no-entrypoint" ] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = [ "no-entrypoint" ] } +spl-pod = { version = "0.1", path = "../../libraries/pod", features = ["borsh"] } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = [ "no-entrypoint" ] } thiserror = "1.0" bincode = "1.3.1" [dev-dependencies] -proptest = "1.2" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -solana-vote-program = "1.16.1" +assert_matches = "1.5.0" +proptest = "1.3" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +solana-vote-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } -test-case = "3.1" +test-case = "3.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/stake-pool/program/src/big_vec.rs b/stake-pool/program/src/big_vec.rs index 4085237799b..0d05f0aee11 100644 --- a/stake-pool/program/src/big_vec.rs +++ b/stake-pool/program/src/big_vec.rs @@ -1,13 +1,12 @@ //! Big vector type, used with vectors that can't be serde'd -#![allow(clippy::integer_arithmetic)] // checked math involves too many compute units +#![allow(clippy::arithmetic_side_effects)] // checked math involves too many compute units use { arrayref::array_ref, borsh::BorshDeserialize, - solana_program::{ - program_error::ProgramError, program_memory::sol_memmove, program_pack::Pack, - }, - std::marker::PhantomData, + bytemuck::Pod, + solana_program::{program_error::ProgramError, program_memory::sol_memmove}, + std::mem, }; /// Contains easy to use utilities for a big vector of Borsh-compatible types, @@ -32,19 +31,22 @@ impl<'data> BigVec<'data> { } /// Retain all elements that match the provided function, discard all others - pub fn retain(&mut self, predicate: fn(&[u8]) -> bool) -> Result<(), ProgramError> { + pub fn retain bool>( + &mut self, + predicate: F, + ) -> Result<(), ProgramError> { let mut vec_len = self.len(); let mut removals_found = 0; let mut dst_start_index = 0; let data_start_index = VEC_SIZE_BYTES; let data_end_index = - data_start_index.saturating_add((vec_len as usize).saturating_mul(T::LEN)); - for start_index in (data_start_index..data_end_index).step_by(T::LEN) { - let end_index = start_index + T::LEN; + data_start_index.saturating_add((vec_len as usize).saturating_mul(mem::size_of::())); + for start_index in (data_start_index..data_end_index).step_by(mem::size_of::()) { + let end_index = start_index + mem::size_of::(); let slice = &self.data[start_index..end_index]; if !predicate(slice) { - let gap = removals_found * T::LEN; + let gap = removals_found * mem::size_of::(); if removals_found > 0 { // In case the compute budget is ever bumped up, allowing us // to use this safe code instead: @@ -65,7 +67,7 @@ impl<'data> BigVec<'data> { // final memmove if removals_found > 0 { - let gap = removals_found * T::LEN; + let gap = removals_found * mem::size_of::(); // In case the compute budget is ever bumped up, allowing us // to use this safe code instead: //self.data.copy_within(dst_start_index + gap..data_end_index, dst_start_index); @@ -85,11 +87,11 @@ impl<'data> BigVec<'data> { } /// Extracts a slice of the data types - pub fn deserialize_mut_slice( + pub fn deserialize_mut_slice( &mut self, skip: usize, len: usize, - ) -> Result, ProgramError> { + ) -> Result<&mut [T], ProgramError> { let vec_len = self.len(); let last_item_index = skip .checked_add(len) @@ -98,22 +100,35 @@ impl<'data> BigVec<'data> { return Err(ProgramError::AccountDataTooSmall); } - let start_index = VEC_SIZE_BYTES.saturating_add(skip.saturating_mul(T::LEN)); - let end_index = start_index.saturating_add(len.saturating_mul(T::LEN)); - let mut deserialized = vec![]; - for slice in self.data[start_index..end_index].chunks_exact_mut(T::LEN) { - deserialized.push(unsafe { &mut *(slice.as_ptr() as *mut T) }); + let start_index = VEC_SIZE_BYTES.saturating_add(skip.saturating_mul(mem::size_of::())); + let end_index = start_index.saturating_add(len.saturating_mul(mem::size_of::())); + bytemuck::try_cast_slice_mut(&mut self.data[start_index..end_index]) + .map_err(|_| ProgramError::InvalidAccountData) + } + + /// Extracts a slice of the data types + pub fn deserialize_slice(&self, skip: usize, len: usize) -> Result<&[T], ProgramError> { + let vec_len = self.len(); + let last_item_index = skip + .checked_add(len) + .ok_or(ProgramError::AccountDataTooSmall)?; + if last_item_index > vec_len as usize { + return Err(ProgramError::AccountDataTooSmall); } - Ok(deserialized) + + let start_index = VEC_SIZE_BYTES.saturating_add(skip.saturating_mul(mem::size_of::())); + let end_index = start_index.saturating_add(len.saturating_mul(mem::size_of::())); + bytemuck::try_cast_slice(&self.data[start_index..end_index]) + .map_err(|_| ProgramError::InvalidAccountData) } /// Add new element to the end - pub fn push(&mut self, element: T) -> Result<(), ProgramError> { + pub fn push(&mut self, element: T) -> Result<(), ProgramError> { let mut vec_len_ref = &mut self.data[0..VEC_SIZE_BYTES]; let mut vec_len = u32::try_from_slice(vec_len_ref)?; - let start_index = VEC_SIZE_BYTES + vec_len as usize * T::LEN; - let end_index = start_index + T::LEN; + let start_index = VEC_SIZE_BYTES + vec_len as usize * mem::size_of::(); + let end_index = start_index + mem::size_of::(); vec_len += 1; borsh::to_writer(&mut vec_len_ref, &vec_len)?; @@ -121,43 +136,24 @@ impl<'data> BigVec<'data> { if self.data.len() < end_index { return Err(ProgramError::AccountDataTooSmall); } - let element_ref = &mut self.data[start_index..start_index + T::LEN]; - element.pack_into_slice(element_ref); + let element_ref = bytemuck::try_from_bytes_mut( + &mut self.data[start_index..start_index + mem::size_of::()], + ) + .map_err(|_| ProgramError::InvalidAccountData)?; + *element_ref = element; Ok(()) } - /// Get an iterator for the type provided - pub fn iter<'vec, T: Pack>(&'vec self) -> Iter<'data, 'vec, T> { - Iter { - len: self.len() as usize, - current: 0, - current_index: VEC_SIZE_BYTES, - inner: self, - phantom: PhantomData, - } - } - - /// Get a mutable iterator for the type provided - pub fn iter_mut<'vec, T: Pack>(&'vec mut self) -> IterMut<'data, 'vec, T> { - IterMut { - len: self.len() as usize, - current: 0, - current_index: VEC_SIZE_BYTES, - inner: self, - phantom: PhantomData, - } - } - /// Find matching data in the array - pub fn find bool>(&self, predicate: F) -> Option<&T> { + pub fn find bool>(&self, predicate: F) -> Option<&T> { let len = self.len() as usize; let mut current = 0; let mut current_index = VEC_SIZE_BYTES; while current != len { - let end_index = current_index + T::LEN; + let end_index = current_index + mem::size_of::(); let current_slice = &self.data[current_index..end_index]; if predicate(current_slice) { - return Some(unsafe { &*(current_slice.as_ptr() as *const T) }); + return Some(bytemuck::from_bytes(current_slice)); } current_index = end_index; current += 1; @@ -166,15 +162,17 @@ impl<'data> BigVec<'data> { } /// Find matching data in the array - pub fn find_mut bool>(&mut self, predicate: F) -> Option<&mut T> { + pub fn find_mut bool>(&mut self, predicate: F) -> Option<&mut T> { let len = self.len() as usize; let mut current = 0; let mut current_index = VEC_SIZE_BYTES; while current != len { - let end_index = current_index + T::LEN; + let end_index = current_index + mem::size_of::(); let current_slice = &self.data[current_index..end_index]; if predicate(current_slice) { - return Some(unsafe { &mut *(current_slice.as_ptr() as *mut T) }); + return Some(bytemuck::from_bytes_mut( + &mut self.data[current_index..end_index], + )); } current_index = end_index; current += 1; @@ -183,91 +181,24 @@ impl<'data> BigVec<'data> { } } -/// Iterator wrapper over a BigVec -pub struct Iter<'data, 'vec, T> { - len: usize, - current: usize, - current_index: usize, - inner: &'vec BigVec<'data>, - phantom: PhantomData, -} - -impl<'data, 'vec, T: Pack + 'data> Iterator for Iter<'data, 'vec, T> { - type Item = &'data T; - - fn next(&mut self) -> Option { - if self.current == self.len { - None - } else { - let end_index = self.current_index + T::LEN; - let value = Some(unsafe { - &*(self.inner.data[self.current_index..end_index].as_ptr() as *const T) - }); - self.current += 1; - self.current_index = end_index; - value - } - } -} - -/// Iterator wrapper over a BigVec -pub struct IterMut<'data, 'vec, T> { - len: usize, - current: usize, - current_index: usize, - inner: &'vec mut BigVec<'data>, - phantom: PhantomData, -} - -impl<'data, 'vec, T: Pack + 'data> Iterator for IterMut<'data, 'vec, T> { - type Item = &'data mut T; - - fn next(&mut self) -> Option { - if self.current == self.len { - None - } else { - let end_index = self.current_index + T::LEN; - let value = Some(unsafe { - &mut *(self.inner.data[self.current_index..end_index].as_ptr() as *mut T) - }); - self.current += 1; - self.current_index = end_index; - value - } - } -} - #[cfg(test)] mod tests { - use {super::*, solana_program::program_pack::Sealed}; + use {super::*, bytemuck::Zeroable}; - #[derive(Debug, PartialEq)] + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq, Pod, Zeroable)] struct TestStruct { - value: u64, - } - - impl Sealed for TestStruct {} - - impl Pack for TestStruct { - const LEN: usize = 8; - fn pack_into_slice(&self, data: &mut [u8]) { - let mut data = data; - borsh::to_writer(&mut data, &self.value).unwrap(); - } - fn unpack_from_slice(src: &[u8]) -> Result { - Ok(TestStruct { - value: u64::try_from_slice(src).unwrap(), - }) - } + value: [u8; 8], } impl TestStruct { - fn new(value: u64) -> Self { + fn new(value: u8) -> Self { + let value = [value, 0, 0, 0, 0, 0, 0, 0]; Self { value } } } - fn from_slice<'data>(data: &'data mut [u8], vec: &[u64]) -> BigVec<'data> { + fn from_slice<'data>(data: &'data mut [u8], vec: &[u8]) -> BigVec<'data> { let mut big_vec = BigVec { data }; for element in vec { big_vec.push(TestStruct::new(*element)).unwrap(); @@ -275,10 +206,12 @@ mod tests { big_vec } - fn check_big_vec_eq(big_vec: &BigVec, slice: &[u64]) { + fn check_big_vec_eq(big_vec: &BigVec, slice: &[u8]) { assert!(big_vec - .iter::() - .map(|x| &x.value) + .deserialize_slice::(0, big_vec.len() as usize) + .unwrap() + .iter() + .map(|x| &x.value[0]) .zip(slice.iter()) .all(|(a, b)| a == b)); } @@ -307,15 +240,15 @@ mod tests { let mut data = [0u8; 4 + 8 * 4]; let mut v = from_slice(&mut data, &[1, 2, 3, 4]); - v.retain::(mod_2_predicate).unwrap(); + v.retain::(mod_2_predicate).unwrap(); check_big_vec_eq(&v, &[2, 4]); } - fn find_predicate(a: &[u8], b: u64) -> bool { + fn find_predicate(a: &[u8], b: u8) -> bool { if a.len() != 8 { false } else { - u64::try_from_slice(&a[0..8]).unwrap() == b + a[0] == b } } @@ -338,10 +271,10 @@ mod tests { fn find_mut() { let mut data = [0u8; 4 + 8 * 4]; let mut v = from_slice(&mut data, &[1, 2, 3, 4]); - let mut test_struct = v + let test_struct = v .find_mut::(|x| find_predicate(x, 1)) .unwrap(); - test_struct.value = 0; + test_struct.value = [0; 8]; check_big_vec_eq(&v, &[0, 2, 3, 4]); assert_eq!(v.find_mut::(|x| find_predicate(x, 5)), None); } @@ -350,9 +283,9 @@ mod tests { fn deserialize_mut_slice() { let mut data = [0u8; 4 + 8 * 4]; let mut v = from_slice(&mut data, &[1, 2, 3, 4]); - let mut slice = v.deserialize_mut_slice::(1, 2).unwrap(); - slice[0].value = 10; - slice[1].value = 11; + let slice = v.deserialize_mut_slice::(1, 2).unwrap(); + slice[0].value[0] = 10; + slice[1].value[0] = 11; check_big_vec_eq(&v, &[1, 10, 11, 4]); assert_eq!( v.deserialize_mut_slice::(1, 4).unwrap_err(), diff --git a/stake-pool/program/src/error.rs b/stake-pool/program/src/error.rs index 39fae32961a..afeaeaf49df 100644 --- a/stake-pool/program/src/error.rs +++ b/stake-pool/program/src/error.rs @@ -48,8 +48,8 @@ pub enum StakePoolError { #[error("WrongPoolMint")] WrongPoolMint, /// Stake account is not in the state expected by the program. - #[error("WrongStakeState")] - WrongStakeState, + #[error("WrongStakeStake")] + WrongStakeStake, /// User stake is not active #[error("UserStakeNotActive")] UserStakeNotActive, @@ -144,6 +144,19 @@ pub enum StakePoolError { /// Instruction exceeds desired slippage limit #[error("Instruction exceeds desired slippage limit")] ExceededSlippage, + + // 40. + /// Provided mint does not have 9 decimals to match SOL + #[error("IncorrectMintDecimals")] + IncorrectMintDecimals, + /// Pool reserve does not have enough lamports to fund rent-exempt reserve in split + /// destination. Deposit more SOL in reserve, or pre-fund split destination with + /// the rent-exempt reserve for a stake account. + #[error("ReserveDepleted")] + ReserveDepleted, + /// Missing required sysvar account + #[error("Missing required sysvar account")] + MissingRequiredSysvar, } impl From for ProgramError { fn from(e: StakePoolError) -> Self { diff --git a/stake-pool/program/src/inline_mpl_token_metadata.rs b/stake-pool/program/src/inline_mpl_token_metadata.rs index ab42602ed75..4de593e3721 100644 --- a/stake-pool/program/src/inline_mpl_token_metadata.rs +++ b/stake-pool/program/src/inline_mpl_token_metadata.rs @@ -1,5 +1,5 @@ //! Inlined MPL metadata types to avoid a direct dependency on `mpl-token-metadata' -//! NOTE: this file is sym-linked in `spl-single-validator-pool`, so be careful +//! NOTE: this file is sym-linked in `spl-single-pool`, so be careful //! with changes! solana_program::declare_id!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"); diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 286a02a5a65..fb07373cd5f 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -112,11 +112,14 @@ pub enum StakePoolInstruction { /// 2. `[]` Stake pool withdraw authority /// 3. `[w]` Validator stake list storage account /// 4. `[w]` Stake account to remove from the pool - /// 5. `[]` Transient stake account, to check that that we're not trying to activate + /// 5. `[w]` Transient stake account, to deactivate if necessary /// 6. `[]` Sysvar clock /// 7. `[]` Stake program id, RemoveValidatorFromPool, + /// NOTE: This instruction has been deprecated since version 0.7.0. Please + /// use `DecreaseValidatorStakeWithReserve` instead. + /// /// (Staker only) Decrease active stake on a validator, eventually moving it to the reserve /// /// Internally, this instruction splits a validator stake account into its @@ -446,31 +449,69 @@ pub enum StakePoolInstruction { /// /// Works regardless if the transient stake account already exists. /// - /// Internally, this instruction splits a validator stake account into an - /// ephemeral stake account, deactivates it, then merges or splits it into - /// the transient stake account delegated to the appropriate validator. + /// Internally, this instruction: + /// * withdraws rent-exempt reserve lamports from the reserve into the ephemeral stake + /// * splits a validator stake account into an ephemeral stake account + /// * deactivates the ephemeral account + /// * merges or splits the ephemeral account into the transient stake account + /// delegated to the appropriate validator /// - /// The amount of lamports to move must be at least rent-exemption plus + /// The amount of lamports to move must be at least /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`. /// /// 0. `[]` Stake pool /// 1. `[s]` Stake pool staker /// 2. `[]` Stake pool withdraw authority /// 3. `[w]` Validator list - /// 4. `[w]` Canonical stake account to split from - /// 5. `[w]` Uninitialized ephemeral stake account to receive stake - /// 6. `[w]` Transient stake account + /// 4. `[w]` Reserve stake account, to fund rent exempt reserve + /// 5. `[w]` Canonical stake account to split from + /// 6. `[w]` Uninitialized ephemeral stake account to receive stake + /// 7. `[w]` Transient stake account + /// 8. `[]` Clock sysvar + /// 9. '[]' Stake history sysvar + /// 10. `[]` System program + /// 11. `[]` Stake program + DecreaseAdditionalValidatorStake { + /// amount of lamports to split into the transient stake account + lamports: u64, + /// seed used to create transient stake account + transient_stake_seed: u64, + /// seed used to create ephemeral account. + ephemeral_stake_seed: u64, + }, + + /// (Staker only) Decrease active stake on a validator, eventually moving it to the reserve + /// + /// Internally, this instruction: + /// * withdraws enough lamports to make the transient account rent-exempt + /// * splits from a validator stake account into a transient stake account + /// * deactivates the transient stake account + /// + /// In order to rebalance the pool without taking custody, the staker needs + /// a way of reducing the stake on a stake account. This instruction splits + /// some amount of stake, up to the total activated stake, from the canonical + /// validator stake account, into its "transient" stake account. + /// + /// The instruction only succeeds if the transient stake account does not + /// exist. The amount of lamports to move must be at least rent-exemption plus + /// `max(crate::MINIMUM_ACTIVE_STAKE, solana_program::stake::tools::get_minimum_delegation())`. + /// + /// 0. `[]` Stake pool + /// 1. `[s]` Stake pool staker + /// 2. `[]` Stake pool withdraw authority + /// 3. `[w]` Validator list + /// 4. `[w]` Reserve stake account, to fund rent exempt reserve + /// 5. `[w]` Canonical stake account to split from + /// 6. `[w]` Transient stake account to receive split /// 7. `[]` Clock sysvar /// 8. '[]' Stake history sysvar /// 9. `[]` System program /// 10. `[]` Stake program - DecreaseAdditionalValidatorStake { + DecreaseValidatorStakeWithReserve { /// amount of lamports to split into the transient stake account lamports: u64, /// seed used to create transient stake account transient_stake_seed: u64, - /// seed used to create ephemeral account. - ephemeral_stake_seed: u64, }, /// (Staker only) Redelegate active stake on a validator, eventually moving it to another @@ -487,34 +528,36 @@ pub enum StakePoolInstruction { /// The instruction only succeeds if the source transient stake account and /// ephemeral stake account do not exist. /// - /// The amount of lamports to move must be at least twice rent-exemption - /// plus the minimum delegation amount. Rent-exemption is required for the - /// source transient stake account, and rent-exemption plus minimum delegation + /// The amount of lamports to move must be at least rent-exemption plus the + /// minimum delegation amount. Rent-exemption plus minimum delegation /// is required for the destination ephemeral stake account. /// + /// The rent-exemption for the source transient account comes from the stake + /// pool reserve, if needed. + /// /// The amount that arrives at the destination validator in the end is - /// `redelegate_lamports - 2 * rent_exemption` if the destination transient - /// account does *not* exist, and `redelegate_lamports - rent_exemption` if - /// the destination transient account already exists. One `rent_exemption` - /// is deactivated with the source transient account during redelegation, - /// and another `rent_exemption` is deactivated when creating the destination - /// transient stake account. + /// `redelegate_lamports - rent_exemption` if the destination transient + /// account does *not* exist, and `redelegate_lamports` if the destination + /// transient account already exists. The `rent_exemption` is not activated + /// when creating the destination transient stake account, but if it already + /// exists, then the full amount is delegated. /// /// 0. `[]` Stake pool /// 1. `[s]` Stake pool staker /// 2. `[]` Stake pool withdraw authority /// 3. `[w]` Validator list - /// 4. `[w]` Source canonical stake account to split from - /// 5. `[w]` Source transient stake account to receive split and be redelegated - /// 6. `[w]` Uninitialized ephemeral stake account to receive redelegation - /// 7. `[w]` Destination transient stake account to receive ephemeral stake by merge - /// 8. `[]` Destination stake account to receive transient stake after activation - /// 9. `[]` Destination validator vote account - /// 10. `[]` Clock sysvar - /// 11. `[]` Stake History sysvar - /// 12. `[]` Stake Config sysvar - /// 13. `[]` System program - /// 14. `[]` Stake program + /// 4. `[w]` Reserve stake account, to withdraw rent exempt reserve + /// 5. `[w]` Source canonical stake account to split from + /// 6. `[w]` Source transient stake account to receive split and be redelegated + /// 7. `[w]` Uninitialized ephemeral stake account to receive redelegation + /// 8. `[w]` Destination transient stake account to receive ephemeral stake by merge + /// 9. `[]` Destination stake account to receive transient stake after activation + /// 10. `[]` Destination validator vote account + /// 11. `[]` Clock sysvar + /// 12. `[]` Stake History sysvar + /// 13. `[]` Stake Config sysvar + /// 14. `[]` System program + /// 15. `[]` Stake program Redelegate { /// Amount of lamports to redelegate #[allow(dead_code)] // but it's not @@ -703,6 +746,7 @@ pub fn add_validator_to_pool( AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(stake::program::id(), false), @@ -733,7 +777,7 @@ pub fn remove_validator_from_pool( AccountMeta::new_readonly(*stake_pool_withdraw, false), AccountMeta::new(*validator_list, false), AccountMeta::new(*stake_account, false), - AccountMeta::new_readonly(*transient_stake_account, false), + AccountMeta::new(*transient_stake_account, false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(stake::program::id(), false), ]; @@ -748,6 +792,10 @@ pub fn remove_validator_from_pool( /// Creates `DecreaseValidatorStake` instruction (rebalance from validator account to /// transient account) +#[deprecated( + since = "0.7.0", + note = "please use `decrease_validator_stake_with_reserve`" +)] pub fn decrease_validator_stake( program_id: &Pubkey, stake_pool: &Pubkey, @@ -791,6 +839,7 @@ pub fn decrease_additional_validator_stake( staker: &Pubkey, stake_pool_withdraw_authority: &Pubkey, validator_list: &Pubkey, + reserve_stake: &Pubkey, validator_stake: &Pubkey, ephemeral_stake: &Pubkey, transient_stake: &Pubkey, @@ -803,6 +852,7 @@ pub fn decrease_additional_validator_stake( AccountMeta::new_readonly(*staker, true), AccountMeta::new_readonly(*stake_pool_withdraw_authority, false), AccountMeta::new(*validator_list, false), + AccountMeta::new(*reserve_stake, false), AccountMeta::new(*validator_stake, false), AccountMeta::new(*ephemeral_stake, false), AccountMeta::new(*transient_stake, false), @@ -824,6 +874,45 @@ pub fn decrease_additional_validator_stake( } } +/// Creates `DecreaseValidatorStakeWithReserve` instruction (rebalance from +/// validator account to transient account) +pub fn decrease_validator_stake_with_reserve( + program_id: &Pubkey, + stake_pool: &Pubkey, + staker: &Pubkey, + stake_pool_withdraw_authority: &Pubkey, + validator_list: &Pubkey, + reserve_stake: &Pubkey, + validator_stake: &Pubkey, + transient_stake: &Pubkey, + lamports: u64, + transient_stake_seed: u64, +) -> Instruction { + let accounts = vec![ + AccountMeta::new_readonly(*stake_pool, false), + AccountMeta::new_readonly(*staker, true), + AccountMeta::new_readonly(*stake_pool_withdraw_authority, false), + AccountMeta::new(*validator_list, false), + AccountMeta::new(*reserve_stake, false), + AccountMeta::new(*validator_stake, false), + AccountMeta::new(*transient_stake, false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + AccountMeta::new_readonly(sysvar::stake_history::id(), false), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new_readonly(stake::program::id(), false), + ]; + Instruction { + program_id: *program_id, + accounts, + data: StakePoolInstruction::DecreaseValidatorStakeWithReserve { + lamports, + transient_stake_seed, + } + .try_to_vec() + .unwrap(), + } +} + /// Creates `IncreaseValidatorStake` instruction (rebalance from reserve account to /// transient account) pub fn increase_validator_stake( @@ -851,6 +940,7 @@ pub fn increase_validator_stake( AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(stake::program::id(), false), @@ -896,6 +986,7 @@ pub fn increase_additional_validator_stake( AccountMeta::new_readonly(*validator, false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(stake::program::id(), false), @@ -920,6 +1011,7 @@ pub fn redelegate( staker: &Pubkey, stake_pool_withdraw_authority: &Pubkey, validator_list: &Pubkey, + reserve_stake: &Pubkey, source_validator_stake: &Pubkey, source_transient_stake: &Pubkey, ephemeral_stake: &Pubkey, @@ -936,6 +1028,7 @@ pub fn redelegate( AccountMeta::new_readonly(*staker, true), AccountMeta::new_readonly(*stake_pool_withdraw_authority, false), AccountMeta::new(*validator_list, false), + AccountMeta::new(*reserve_stake, false), AccountMeta::new(*source_validator_stake, false), AccountMeta::new(*source_transient_stake, false), AccountMeta::new(*ephemeral_stake, false), @@ -944,6 +1037,7 @@ pub fn redelegate( AccountMeta::new_readonly(*validator, false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(stake::program::id(), false), @@ -1160,12 +1254,13 @@ pub fn decrease_validator_stake_with_vote( stake_pool_address, transient_stake_seed, ); - decrease_validator_stake( + decrease_validator_stake_with_reserve( program_id, stake_pool_address, &stake_pool.staker, &pool_withdraw_authority, &stake_pool.validator_list, + &stake_pool.reserve_stake, &validator_stake_address, &transient_stake_address, lamports, @@ -1207,6 +1302,7 @@ pub fn decrease_additional_validator_stake_with_vote( &stake_pool.staker, &pool_withdraw_authority, &stake_pool.validator_list, + &stake_pool.reserve_stake, &validator_stake_address, &ephemeral_stake_address, &transient_stake_address, @@ -1247,13 +1343,13 @@ pub fn update_validator_list_balance( program_id, vote_account_address, stake_pool, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), ); let (transient_stake_account, _) = find_transient_stake_program_address( program_id, vote_account_address, stake_pool, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), ); vec![ AccountMeta::new(validator_stake_account, false), diff --git a/stake-pool/program/src/lib.rs b/stake-pool/program/src/lib.rs index ab5bef67919..8ef0193f968 100644 --- a/stake-pool/program/src/lib.rs +++ b/stake-pool/program/src/lib.rs @@ -37,9 +37,7 @@ const EPHEMERAL_STAKE_SEED_PREFIX: &[u8] = b"ephemeral"; pub const MINIMUM_ACTIVE_STAKE: u64 = 1_000_000; /// Minimum amount of lamports in the reserve -/// NOTE: This can be changed to 0 once the `stake_allow_zero_undelegated_amount` -/// feature is enabled on all clusters -pub const MINIMUM_RESERVE_LAMPORTS: u64 = 1; +pub const MINIMUM_RESERVE_LAMPORTS: u64 = 0; /// Maximum amount of validator stake accounts to update per /// `UpdateValidatorListBalance` instruction, based on compute limits diff --git a/stake-pool/program/src/processor.rs b/stake-pool/program/src/processor.rs index 2ecf789cf70..b25ec780531 100644 --- a/stake-pool/program/src/processor.rs +++ b/stake-pool/program/src/processor.rs @@ -24,7 +24,7 @@ use { num_traits::FromPrimitive, solana_program::{ account_info::{next_account_info, AccountInfo}, - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, clock::{Clock, Epoch}, decode_error::DecodeError, entrypoint::ProgramResult, @@ -39,6 +39,7 @@ use { spl_token_2022::{ check_spl_token_program_account, extension::{BaseStateWithExtensions, StateWithExtensions}, + native_mint, state::Mint, }, std::num::NonZeroU32, @@ -49,10 +50,10 @@ fn get_stake_state( stake_account_info: &AccountInfo, ) -> Result<(stake::state::Meta, stake::state::Stake), ProgramError> { let stake_state = - try_from_slice_unchecked::(&stake_account_info.data.borrow())?; + try_from_slice_unchecked::(&stake_account_info.data.borrow())?; match stake_state { - stake::state::StakeState::Stake(meta, stake) => Ok((meta, stake)), - _ => Err(StakePoolError::WrongStakeState.into()), + stake::state::StakeStateV2::Stake(meta, stake, _) => Ok((meta, stake)), + _ => Err(StakePoolError::WrongStakeStake.into()), } } @@ -223,7 +224,7 @@ fn check_if_stake_deactivating( vote_account_address, epoch, ); - Err(StakePoolError::WrongStakeState.into()) + Err(StakePoolError::WrongStakeStake.into()) } else { Ok(()) } @@ -245,7 +246,7 @@ fn check_if_stake_activating( vote_account_address, epoch, ); - Err(StakePoolError::WrongStakeState.into()) + Err(StakePoolError::WrongStakeStake.into()) } else { Ok(()) } @@ -265,7 +266,7 @@ fn check_stake_state( "Validator stake for {} not usable by pool, must be owned by withdraw authority", vote_account_address ); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); } if stake.delegation.voter_pubkey != *vote_account_address { msg!( @@ -273,7 +274,7 @@ fn check_stake_state( stake_account_info.key, vote_account_address ); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); } Ok(()) } @@ -738,6 +739,8 @@ impl Processor { return Err(StakePoolError::AlreadyInUse.into()); } + // This check is unnecessary since the runtime will check the ownership, + // but provides clarity that the parameter is in fact checked. check_account_owner(stake_pool_info, program_id)?; let mut stake_pool = try_from_slice_unchecked::(&stake_pool_info.data.borrow())?; if !stake_pool.is_uninitialized() { @@ -745,6 +748,8 @@ impl Processor { return Err(StakePoolError::AlreadyInUse.into()); } + // This check is unnecessary since the runtime will check the ownership, + // but provides clarity that the parameter is in fact checked. check_account_owner(validator_list_info, program_id)?; let mut validator_list = try_from_slice_unchecked::(&validator_list_info.data.borrow())?; @@ -828,6 +833,10 @@ impl Processor { return Err(StakePoolError::NonZeroPoolTokenSupply.into()); } + if pool_mint.base.decimals != native_mint::DECIMALS { + return Err(StakePoolError::IncorrectMintDecimals.into()); + } + if !pool_mint .base .mint_authority @@ -854,13 +863,13 @@ impl Processor { msg!("Reserve stake account not owned by stake program"); return Err(ProgramError::IncorrectProgramId); } - let stake_state = try_from_slice_unchecked::( + let stake_state = try_from_slice_unchecked::( &reserve_stake_info.data.borrow(), )?; - let total_lamports = if let stake::state::StakeState::Initialized(meta) = stake_state { + let total_lamports = if let stake::state::StakeStateV2::Initialized(meta) = stake_state { if meta.lockup != stake::state::Lockup::default() { msg!("Reserve stake account has some lockup"); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); } if meta.authorized.staker != withdraw_authority_key { @@ -869,7 +878,7 @@ impl Processor { meta.authorized.staker, withdraw_authority_key ); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); } if meta.authorized.withdrawer != withdraw_authority_key { @@ -878,7 +887,7 @@ impl Processor { meta.authorized.staker, withdraw_authority_key ); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); } reserve_stake_info .lamports() @@ -886,7 +895,7 @@ impl Processor { .ok_or(StakePoolError::CalculationFailure)? } else { msg!("Reserve stake account not in intialized state"); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); }; if total_lamports > 0 { @@ -1027,18 +1036,18 @@ impl Processor { ]; // Fund the stake account with the minimum + rent-exempt balance - let stake_space = std::mem::size_of::(); + let stake_space = std::mem::size_of::(); let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; let required_lamports = minimum_delegation(stake_minimum_delegation) .saturating_add(rent.minimum_balance(stake_space)); // Check that we're not draining the reserve totally - let reserve_stake = try_from_slice_unchecked::( + let reserve_stake = try_from_slice_unchecked::( &reserve_stake_info.data.borrow(), )?; let reserve_meta = reserve_stake .meta() - .ok_or(StakePoolError::WrongStakeState)?; + .ok_or(StakePoolError::WrongStakeStake)?; let minimum_lamports = minimum_reserve_lamports(&reserve_meta); let reserve_lamports = reserve_stake_info.lamports(); if reserve_lamports.saturating_sub(required_lamports) < minimum_lamports { @@ -1076,14 +1085,14 @@ impl Processor { )?; validator_list.push(ValidatorStakeInfo { - status: StakeStatus::Active, + status: StakeStatus::Active.into(), vote_account_address: *validator_vote_info.key, - active_stake_lamports: required_lamports, - transient_stake_lamports: 0, - last_update_epoch: clock.epoch, - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: raw_validator_seed, + active_stake_lamports: required_lamports.into(), + transient_stake_lamports: 0.into(), + last_update_epoch: clock.epoch.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: raw_validator_seed.into(), })?; Ok(()) @@ -1152,27 +1161,27 @@ impl Processor { ); return Err(StakePoolError::ValidatorNotFound.into()); } - let mut validator_stake_info = maybe_validator_stake_info.unwrap(); + let validator_stake_info = maybe_validator_stake_info.unwrap(); check_validator_stake_address( program_id, stake_pool_info.key, stake_account_info.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!("Validator is already marked for removal"); return Err(StakePoolError::ValidatorNotFound.into()); } - let new_status = if validator_stake_info.transient_stake_lamports > 0 { + let new_status = if u64::from(validator_stake_info.transient_stake_lamports) > 0 { check_transient_stake_address( program_id, stake_pool_info.key, transient_stake_account_info.key, &vote_account_address, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), )?; match get_stake_state(transient_stake_account_info) { @@ -1184,16 +1193,16 @@ impl Processor { ) => { if stake.delegation.deactivation_epoch == Epoch::MAX { - msg!( - "Transient stake {} activating, can't remove stake {} on validator {}", - transient_stake_account_info.key, - stake_account_info.key, - vote_account_address - ); - return Err(StakePoolError::WrongStakeState.into()); - } else { - StakeStatus::DeactivatingAll + Self::stake_deactivate( + transient_stake_account_info.clone(), + clock_info.clone(), + withdraw_authority_info.clone(), + stake_pool_info.key, + AUTHORITY_WITHDRAW, + stake_pool.stake_withdraw_bump_seed, + )?; } + StakeStatus::DeactivatingAll } _ => StakeStatus::DeactivatingValidator, } @@ -1201,17 +1210,20 @@ impl Processor { StakeStatus::DeactivatingValidator }; - // deactivate stake - Self::stake_deactivate( - stake_account_info.clone(), - clock_info.clone(), - withdraw_authority_info.clone(), - stake_pool_info.key, - AUTHORITY_WITHDRAW, - stake_pool.stake_withdraw_bump_seed, - )?; + // If the stake was force-deactivated through deactivate-delinquent or + // some other means, we *do not* need to deactivate it again + if stake.delegation.deactivation_epoch == Epoch::MAX { + Self::stake_deactivate( + stake_account_info.clone(), + clock_info.clone(), + withdraw_authority_info.clone(), + stake_pool_info.key, + AUTHORITY_WITHDRAW, + stake_pool.stake_withdraw_bump_seed, + )?; + } - validator_stake_info.status = new_status; + validator_stake_info.status = new_status.into(); if stake_pool.preferred_deposit_validator_vote_address == Some(vote_account_address) { stake_pool.preferred_deposit_validator_vote_address = None; @@ -1232,12 +1244,16 @@ impl Processor { lamports: u64, transient_stake_seed: u64, maybe_ephemeral_stake_seed: Option, + fund_rent_exempt_reserve: bool, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let stake_pool_info = next_account_info(account_info_iter)?; let staker_info = next_account_info(account_info_iter)?; let withdraw_authority_info = next_account_info(account_info_iter)?; let validator_list_info = next_account_info(account_info_iter)?; + let maybe_reserve_stake_info = fund_rent_exempt_reserve + .then(|| next_account_info(account_info_iter)) + .transpose()?; let validator_stake_account_info = next_account_info(account_info_iter)?; let maybe_ephemeral_stake_account_info = maybe_ephemeral_stake_seed .map(|_| next_account_info(account_info_iter)) @@ -1245,17 +1261,14 @@ impl Processor { let transient_stake_account_info = next_account_info(account_info_iter)?; let clock_info = next_account_info(account_info_iter)?; let clock = &Clock::from_account_info(clock_info)?; - let rent = if maybe_ephemeral_stake_seed.is_some() { - // instruction with ephemeral account doesn't take the rent account - Rent::get()? - } else { - // legacy instruction takes the rent account - let rent_info = next_account_info(account_info_iter)?; - Rent::from_account_info(rent_info)? - }; - let maybe_stake_history_info = maybe_ephemeral_stake_seed - .map(|_| next_account_info(account_info_iter)) - .transpose()?; + let (rent, maybe_stake_history_info) = + if maybe_ephemeral_stake_seed.is_some() || fund_rent_exempt_reserve { + (Rent::get()?, Some(next_account_info(account_info_iter)?)) + } else { + // legacy instruction takes the rent account + let rent_info = next_account_info(account_info_iter)?; + (Rent::from_account_info(rent_info)?, None) + }; let system_program_info = next_account_info(account_info_iter)?; let stake_program_info = next_account_info(account_info_iter)?; @@ -1289,6 +1302,10 @@ impl Processor { return Err(StakePoolError::InvalidState.into()); } + if let Some(reserve_stake_info) = maybe_reserve_stake_info { + stake_pool.check_reserve_stake(reserve_stake_info)?; + } + let (meta, stake) = get_stake_state(validator_stake_account_info)?; let vote_account_address = stake.delegation.voter_pubkey; @@ -1302,23 +1319,23 @@ impl Processor { ); return Err(StakePoolError::ValidatorNotFound.into()); } - let mut validator_stake_info = maybe_validator_stake_info.unwrap(); + let validator_stake_info = maybe_validator_stake_info.unwrap(); check_validator_stake_address( program_id, stake_pool_info.key, validator_stake_account_info.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; - if validator_stake_info.transient_stake_lamports > 0 { + if u64::from(validator_stake_info.transient_stake_lamports) > 0 { if maybe_ephemeral_stake_seed.is_none() { msg!("Attempting to decrease stake on a validator with pending transient stake, use DecreaseAdditionalValidatorStake with the existing seed"); return Err(StakePoolError::TransientAccountInUse.into()); } - if transient_stake_seed != validator_stake_info.transient_seed_suffix { + if transient_stake_seed != u64::from(validator_stake_info.transient_seed_suffix) { msg!( "Transient stake already exists with seed {}, you must use that one", - validator_stake_info.transient_seed_suffix + u64::from(validator_stake_info.transient_seed_suffix) ); return Err(ProgramError::InvalidSeeds); } @@ -1329,11 +1346,11 @@ impl Processor { )?; } - let stake_space = std::mem::size_of::(); - let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; + let stake_space = std::mem::size_of::(); let stake_rent = rent.minimum_balance(stake_space); - let current_minimum_lamports = - stake_rent.saturating_add(minimum_delegation(stake_minimum_delegation)); + + let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; + let current_minimum_lamports = minimum_delegation(stake_minimum_delegation); if lamports < current_minimum_lamports { msg!( "Need at least {} lamports for transient stake to meet minimum delegation and rent-exempt requirements, {} provided", @@ -1357,7 +1374,7 @@ impl Processor { return Err(ProgramError::InsufficientFunds); } - let source_stake_account_info = + let (source_stake_account_info, split_lamports) = if let Some((ephemeral_stake_seed, ephemeral_stake_account_info)) = maybe_ephemeral_stake_seed.zip(maybe_ephemeral_stake_account_info) { @@ -1379,6 +1396,30 @@ impl Processor { stake_space, )?; + // if needed, withdraw rent-exempt reserve for ephemeral account + if let Some(reserve_stake_info) = maybe_reserve_stake_info { + let required_lamports_for_rent_exemption = + stake_rent.saturating_sub(ephemeral_stake_account_info.lamports()); + if required_lamports_for_rent_exemption > 0 { + if required_lamports_for_rent_exemption >= reserve_stake_info.lamports() { + return Err(StakePoolError::ReserveDepleted.into()); + } + let stake_history_info = maybe_stake_history_info + .ok_or(StakePoolError::MissingRequiredSysvar)?; + Self::stake_withdraw( + stake_pool_info.key, + reserve_stake_info.clone(), + withdraw_authority_info.clone(), + AUTHORITY_WITHDRAW, + stake_pool.stake_withdraw_bump_seed, + ephemeral_stake_account_info.clone(), + clock_info.clone(), + stake_history_info.clone(), + required_lamports_for_rent_exemption, + )?; + } + } + // split into ephemeral stake account Self::stake_split( stake_pool_info.key, @@ -1399,11 +1440,14 @@ impl Processor { stake_pool.stake_withdraw_bump_seed, )?; - ephemeral_stake_account_info + ( + ephemeral_stake_account_info, + ephemeral_stake_account_info.lamports(), + ) } else { // if no ephemeral account is provided, split everything from the // validator stake account, into the transient stake account - validator_stake_account_info + (validator_stake_account_info, lamports) }; let transient_stake_bump_seed = check_transient_stake_address( @@ -1414,7 +1458,7 @@ impl Processor { transient_stake_seed, )?; - if validator_stake_info.transient_stake_lamports > 0 { + if u64::from(validator_stake_info.transient_stake_lamports) > 0 { let stake_history_info = maybe_stake_history_info.unwrap(); // transient stake exists, try to merge from the source account, // which is always an ephemeral account @@ -1443,6 +1487,35 @@ impl Processor { stake_space, )?; + // if needed, withdraw rent-exempt reserve for transient account + if let Some(reserve_stake_info) = maybe_reserve_stake_info { + let required_lamports = + stake_rent.saturating_sub(transient_stake_account_info.lamports()); + // in the case of doing a full split from an ephemeral account, + // the rent-exempt reserve moves over, so no need to fund it from + // the pool reserve + if source_stake_account_info.lamports() != split_lamports { + let stake_history_info = + maybe_stake_history_info.ok_or(StakePoolError::MissingRequiredSysvar)?; + if required_lamports >= reserve_stake_info.lamports() { + return Err(StakePoolError::ReserveDepleted.into()); + } + if required_lamports > 0 { + Self::stake_withdraw( + stake_pool_info.key, + reserve_stake_info.clone(), + withdraw_authority_info.clone(), + AUTHORITY_WITHDRAW, + stake_pool.stake_withdraw_bump_seed, + transient_stake_account_info.clone(), + clock_info.clone(), + stake_history_info.clone(), + required_lamports, + )?; + } + } + } + // split into transient stake account Self::stake_split( stake_pool_info.key, @@ -1450,7 +1523,7 @@ impl Processor { withdraw_authority_info.clone(), AUTHORITY_WITHDRAW, stake_pool.stake_withdraw_bump_seed, - lamports, + split_lamports, transient_stake_account_info.clone(), )?; @@ -1468,15 +1541,14 @@ impl Processor { } } - validator_stake_info.active_stake_lamports = validator_stake_info - .active_stake_lamports - .checked_sub(lamports) - .ok_or(StakePoolError::CalculationFailure)?; - validator_stake_info.transient_stake_lamports = validator_stake_info - .transient_stake_lamports - .checked_add(lamports) - .ok_or(StakePoolError::CalculationFailure)?; - validator_stake_info.transient_seed_suffix = transient_stake_seed; + validator_stake_info.active_stake_lamports = + u64::from(validator_stake_info.active_stake_lamports) + .checked_sub(lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into(); + validator_stake_info.transient_stake_lamports = + transient_stake_account_info.lamports().into(); + validator_stake_info.transient_seed_suffix = transient_stake_seed.into(); Ok(()) } @@ -1561,16 +1633,16 @@ impl Processor { ); return Err(StakePoolError::ValidatorNotFound.into()); } - let mut validator_stake_info = maybe_validator_stake_info.unwrap(); - if validator_stake_info.transient_stake_lamports > 0 { + let validator_stake_info = maybe_validator_stake_info.unwrap(); + if u64::from(validator_stake_info.transient_stake_lamports) > 0 { if maybe_ephemeral_stake_seed.is_none() { msg!("Attempting to increase stake on a validator with pending transient stake, use IncreaseAdditionalValidatorStake with the existing seed"); return Err(StakePoolError::TransientAccountInUse.into()); } - if transient_stake_seed != validator_stake_info.transient_seed_suffix { + if transient_stake_seed != u64::from(validator_stake_info.transient_seed_suffix) { msg!( "Transient stake already exists with seed {}, you must use that one", - validator_stake_info.transient_seed_suffix + u64::from(validator_stake_info.transient_seed_suffix) ); return Err(ProgramError::InvalidSeeds); } @@ -1587,16 +1659,16 @@ impl Processor { stake_pool_info.key, withdraw_authority_info.key, vote_account_address, - validator_stake_info.validator_seed_suffix, + validator_stake_info.validator_seed_suffix.into(), &stake_pool.lockup, )?; - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!("Validator is marked for removal and no longer allows increases"); return Err(StakePoolError::ValidatorNotFound.into()); } - let stake_space = std::mem::size_of::(); + let stake_space = std::mem::size_of::(); let stake_rent = rent.minimum_balance(stake_space); let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; let current_minimum_delegation = minimum_delegation(stake_minimum_delegation); @@ -1619,13 +1691,13 @@ impl Processor { if reserve_stake_account_info .lamports() .saturating_sub(total_lamports) - <= stake_rent + < stake_rent { let max_split_amount = reserve_stake_account_info .lamports() .saturating_sub(stake_rent.saturating_mul(2)); msg!( - "Reserve stake does not have enough lamports for increase, must be less than {}, {} requested", + "Reserve stake does not have enough lamports for increase, maximum amount {}, {} requested", max_split_amount, lamports ); @@ -1692,7 +1764,7 @@ impl Processor { transient_stake_seed, )?; - if validator_stake_info.transient_stake_lamports > 0 { + if u64::from(validator_stake_info.transient_stake_lamports) > 0 { // transient stake exists, try to merge from the source account, // which is always an ephemeral account Self::stake_merge( @@ -1733,12 +1805,12 @@ impl Processor { )?; // Activate transient stake to validator if necessary - let stake_state = try_from_slice_unchecked::( + let stake_state = try_from_slice_unchecked::( &transient_stake_account_info.data.borrow(), )?; match stake_state { // if it was delegated on or before this epoch, we're good - stake::state::StakeState::Stake(_, stake) + stake::state::StakeStateV2::Stake(_, stake, _) if stake.delegation.activation_epoch <= clock.epoch => {} // all other situations, delegate! _ => { @@ -1757,11 +1829,12 @@ impl Processor { } } - validator_stake_info.transient_stake_lamports = validator_stake_info - .transient_stake_lamports - .checked_add(total_lamports) - .ok_or(StakePoolError::CalculationFailure)?; - validator_stake_info.transient_seed_suffix = transient_stake_seed; + validator_stake_info.transient_stake_lamports = + u64::from(validator_stake_info.transient_stake_lamports) + .checked_add(total_lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into(); + validator_stake_info.transient_seed_suffix = transient_stake_seed.into(); Ok(()) } @@ -1781,6 +1854,7 @@ impl Processor { let staker_info = next_account_info(account_info_iter)?; let withdraw_authority_info = next_account_info(account_info_iter)?; let validator_list_info = next_account_info(account_info_iter)?; + let reserve_stake_info = next_account_info(account_info_iter)?; let source_validator_stake_account_info = next_account_info(account_info_iter)?; let source_transient_stake_account_info = next_account_info(account_info_iter)?; let ephemeral_stake_account_info = next_account_info(account_info_iter)?; @@ -1817,6 +1891,7 @@ impl Processor { stake_pool.check_validator_list(validator_list_info)?; check_account_owner(validator_list_info, program_id)?; + stake_pool.check_reserve_stake(reserve_stake_info)?; let mut validator_list_data = validator_list_info.data.borrow_mut(); let (header, mut validator_list) = @@ -1826,17 +1901,17 @@ impl Processor { } let rent = Rent::get()?; - let stake_space = std::mem::size_of::(); + let stake_space = std::mem::size_of::(); let stake_rent = rent.minimum_balance(stake_space); let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; let current_minimum_delegation = minimum_delegation(stake_minimum_delegation); // check that we're redelegating enough - let destination_transient_lamports = { + { // redelegation requires that the source account maintains rent exemption and that // the destination account has rent-exemption and minimum delegation let minimum_redelegation_lamports = - current_minimum_delegation.saturating_add(stake_rent.saturating_mul(2)); + current_minimum_delegation.saturating_add(stake_rent); if lamports < minimum_redelegation_lamports { msg!( "Need more than {} lamports for redelegated stake and transient stake to meet minimum delegation requirement, {} provided", @@ -1865,10 +1940,7 @@ impl Processor { ); return Err(ProgramError::InsufficientFunds); } - lamports - .checked_sub(stake_rent) - .ok_or(StakePoolError::CalculationFailure)? - }; + } // check source account state let (_, stake) = get_stake_state(source_validator_stake_account_info)?; @@ -1885,27 +1957,28 @@ impl Processor { ); return Err(StakePoolError::ValidatorNotFound.into()); } - let mut validator_stake_info = maybe_validator_stake_info.unwrap(); + let validator_stake_info = maybe_validator_stake_info.unwrap(); check_validator_stake_address( program_id, stake_pool_info.key, source_validator_stake_account_info.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; - if validator_stake_info.transient_stake_lamports > 0 { + if u64::from(validator_stake_info.transient_stake_lamports) > 0 { return Err(StakePoolError::TransientAccountInUse.into()); } - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!("Validator is marked for removal and no longer allows redelegation"); return Err(StakePoolError::ValidatorNotFound.into()); } - validator_stake_info.active_stake_lamports = validator_stake_info - .active_stake_lamports - .checked_sub(lamports) - .ok_or(StakePoolError::CalculationFailure)?; - validator_stake_info.transient_stake_lamports = stake_rent; - validator_stake_info.transient_seed_suffix = source_transient_stake_seed; + validator_stake_info.active_stake_lamports = + u64::from(validator_stake_info.active_stake_lamports) + .checked_sub(lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into(); + validator_stake_info.transient_stake_lamports = stake_rent.into(); + validator_stake_info.transient_seed_suffix = source_transient_stake_seed.into(); } // split from source, into source transient @@ -1932,6 +2005,25 @@ impl Processor { stake_space, )?; + // if needed, pre-fund the rent-exempt reserve from the reserve stake + let required_lamports_for_rent_exemption = + stake_rent.saturating_sub(source_transient_stake_account_info.lamports()); + if required_lamports_for_rent_exemption > 0 { + if required_lamports_for_rent_exemption >= reserve_stake_info.lamports() { + return Err(StakePoolError::ReserveDepleted.into()); + } + Self::stake_withdraw( + stake_pool_info.key, + reserve_stake_info.clone(), + withdraw_authority_info.clone(), + AUTHORITY_WITHDRAW, + stake_pool.stake_withdraw_bump_seed, + source_transient_stake_account_info.clone(), + clock_info.clone(), + stake_history_info.clone(), + required_lamports_for_rent_exemption, + )?; + } Self::stake_split( stake_pool_info.key, source_validator_stake_account_info.clone(), @@ -1988,35 +2080,39 @@ impl Processor { ); return Err(StakePoolError::ValidatorNotFound.into()); } - let mut validator_stake_info = maybe_validator_stake_info.unwrap(); + let validator_stake_info = maybe_validator_stake_info.unwrap(); check_validator_stake_account( destination_validator_stake_account_info, program_id, stake_pool_info.key, withdraw_authority_info.key, vote_account_address, - validator_stake_info.validator_seed_suffix, + validator_stake_info.validator_seed_suffix.into(), &stake_pool.lockup, )?; - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!( "Destination validator is marked for removal and no longer allows redelegation" ); return Err(StakePoolError::ValidatorNotFound.into()); } - let transient_account_exists = validator_stake_info.transient_stake_lamports > 0; - validator_stake_info.transient_stake_lamports = validator_stake_info - .transient_stake_lamports - .checked_add(destination_transient_lamports) - .ok_or(StakePoolError::CalculationFailure)?; + let transient_account_exists = + u64::from(validator_stake_info.transient_stake_lamports) > 0; + validator_stake_info.transient_stake_lamports = + u64::from(validator_stake_info.transient_stake_lamports) + .checked_add(lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into(); if transient_account_exists { // if transient stake exists, make sure it's the right one and that it's // usable by the pool - if validator_stake_info.transient_seed_suffix != destination_transient_stake_seed { + if u64::from(validator_stake_info.transient_seed_suffix) + != destination_transient_stake_seed + { msg!("Provided seed {} does not match current seed {} for transient stake account", destination_transient_stake_seed, - validator_stake_info.transient_seed_suffix + u64::from(validator_stake_info.transient_seed_suffix) ); return Err(StakePoolError::InvalidStakeAccountAddress.into()); } @@ -2071,10 +2167,11 @@ impl Processor { withdraw_authority_info.clone(), AUTHORITY_WITHDRAW, stake_pool.stake_withdraw_bump_seed, - destination_transient_lamports, + lamports, destination_transient_stake_account_info.clone(), )?; - validator_stake_info.transient_seed_suffix = destination_transient_stake_seed; + validator_stake_info.transient_seed_suffix = + destination_transient_stake_seed.into(); } } @@ -2119,7 +2216,7 @@ impl Processor { }); match maybe_validator_stake_info { Some(vsi) => { - if vsi.status != StakeStatus::Active { + if vsi.status != StakeStatus::Active.into() { msg!("Validator for {:?} about to be removed, cannot set as preferred deposit account", validator_type); return Err(StakePoolError::InvalidPreferredValidator.into()); } @@ -2188,12 +2285,13 @@ impl Processor { check_account_owner(validator_list_info, program_id)?; let mut validator_list_data = validator_list_info.data.borrow_mut(); - let (validator_list_header, mut validator_slice) = - ValidatorListHeader::deserialize_mut_slice( - &mut validator_list_data, - start_index as usize, - validator_stake_accounts.len() / 2, - )?; + let (validator_list_header, mut big_vec) = + ValidatorListHeader::deserialize_vec(&mut validator_list_data)?; + let validator_slice = ValidatorListHeader::deserialize_mut_slice( + &mut big_vec, + start_index as usize, + validator_stake_accounts.len() / 2, + )?; if !validator_list_header.is_valid() { return Err(StakePoolError::InvalidState.into()); @@ -2215,7 +2313,7 @@ impl Processor { stake_pool_info.key, validator_stake_info.key, &validator_stake_record.vote_account_address, - NonZeroU32::new(validator_stake_record.validator_seed_suffix), + NonZeroU32::new(validator_stake_record.validator_seed_suffix.into()), ) .is_err() { @@ -2226,7 +2324,7 @@ impl Processor { stake_pool_info.key, transient_stake_info.key, &validator_stake_record.vote_account_address, - validator_stake_record.transient_seed_suffix, + validator_stake_record.transient_seed_suffix.into(), ) .is_err() { @@ -2235,11 +2333,11 @@ impl Processor { let mut active_stake_lamports = 0; let mut transient_stake_lamports = 0; - let validator_stake_state = try_from_slice_unchecked::( + let validator_stake_state = try_from_slice_unchecked::( &validator_stake_info.data.borrow(), ) .ok(); - let transient_stake_state = try_from_slice_unchecked::( + let transient_stake_state = try_from_slice_unchecked::( &transient_stake_info.data.borrow(), ) .ok(); @@ -2251,7 +2349,7 @@ impl Processor { // * inactive -> merge into reserve stake // * not a stake -> ignore match transient_stake_state { - Some(stake::state::StakeState::Initialized(meta)) => { + Some(stake::state::StakeStateV2::Initialized(meta)) => { if stake_is_usable_by_pool( &meta, withdraw_authority_info.key, @@ -2271,11 +2369,11 @@ impl Processor { clock_info.clone(), stake_history_info.clone(), )?; - validator_stake_record.status.remove_transient_stake(); + validator_stake_record.status.remove_transient_stake()?; } } } - Some(stake::state::StakeState::Stake(meta, stake)) => { + Some(stake::state::StakeStateV2::Stake(meta, stake, _)) => { if stake_is_usable_by_pool( &meta, withdraw_authority_info.key, @@ -2295,9 +2393,9 @@ impl Processor { clock_info.clone(), stake_history_info.clone(), )?; - validator_stake_record.status.remove_transient_stake(); + validator_stake_record.status.remove_transient_stake()?; } else if stake.delegation.activation_epoch < clock.epoch { - if let Some(stake::state::StakeState::Stake(_, validator_stake)) = + if let Some(stake::state::StakeStateV2::Stake(_, validator_stake, _)) = validator_stake_state { if validator_stake.delegation.activation_epoch < clock.epoch { @@ -2326,19 +2424,19 @@ impl Processor { } } None - | Some(stake::state::StakeState::Uninitialized) - | Some(stake::state::StakeState::RewardsPool) => {} // do nothing + | Some(stake::state::StakeStateV2::Uninitialized) + | Some(stake::state::StakeStateV2::RewardsPool) => {} // do nothing } // Status for validator stake // * active -> do everything // * any other state / not a stake -> error state, but account for transient stake - let validator_stake_state = try_from_slice_unchecked::( + let validator_stake_state = try_from_slice_unchecked::( &validator_stake_info.data.borrow(), ) .ok(); match validator_stake_state { - Some(stake::state::StakeState::Stake(meta, stake)) => { + Some(stake::state::StakeStateV2::Stake(meta, stake, _)) => { let additional_lamports = validator_stake_info .lamports() .saturating_sub(stake.delegation.stake) @@ -2363,7 +2461,7 @@ impl Processor { additional_lamports, )?; } - match validator_stake_record.status { + match validator_stake_record.status.try_into()? { StakeStatus::Active => { active_stake_lamports = validator_stake_info.lamports(); } @@ -2388,7 +2486,7 @@ impl Processor { clock_info.clone(), stake_history_info.clone(), )?; - validator_stake_record.status.remove_validator_stake(); + validator_stake_record.status.remove_validator_stake()?; } } StakeStatus::DeactivatingTransient | StakeStatus::ReadyForRemoval => { @@ -2396,7 +2494,7 @@ impl Processor { } } } - Some(stake::state::StakeState::Initialized(meta)) + Some(stake::state::StakeStateV2::Initialized(meta)) if stake_is_usable_by_pool( &meta, withdraw_authority_info.key, @@ -2417,19 +2515,19 @@ impl Processor { clock_info.clone(), stake_history_info.clone(), )?; - validator_stake_record.status.remove_validator_stake(); + validator_stake_record.status.remove_validator_stake()?; } - Some(stake::state::StakeState::Initialized(_)) - | Some(stake::state::StakeState::Uninitialized) - | Some(stake::state::StakeState::RewardsPool) + Some(stake::state::StakeStateV2::Initialized(_)) + | Some(stake::state::StakeStateV2::Uninitialized) + | Some(stake::state::StakeStateV2::RewardsPool) | None => { msg!("Validator stake account no longer part of the pool, ignoring"); } } - validator_stake_record.last_update_epoch = clock.epoch; - validator_stake_record.active_stake_lamports = active_stake_lamports; - validator_stake_record.transient_stake_lamports = transient_stake_lamports; + validator_stake_record.last_update_epoch = clock.epoch.into(); + validator_stake_record.active_stake_lamports = active_stake_lamports.into(); + validator_stake_record.transient_stake_lamports = transient_stake_lamports.into(); } Ok(()) @@ -2480,21 +2578,23 @@ impl Processor { let previous_lamports = stake_pool.total_lamports; let previous_pool_token_supply = stake_pool.pool_token_supply; - let reserve_stake = try_from_slice_unchecked::( + let reserve_stake = try_from_slice_unchecked::( &reserve_stake_info.data.borrow(), )?; - let mut total_lamports = if let stake::state::StakeState::Initialized(meta) = reserve_stake + let mut total_lamports = + if let stake::state::StakeStateV2::Initialized(meta) = reserve_stake { + reserve_stake_info + .lamports() + .checked_sub(minimum_reserve_lamports(&meta)) + .ok_or(StakePoolError::CalculationFailure)? + } else { + msg!("Reserve stake account in unknown state, aborting"); + return Err(StakePoolError::WrongStakeStake.into()); + }; + for validator_stake_record in validator_list + .deserialize_slice::(0, validator_list.len() as usize)? { - reserve_stake_info - .lamports() - .checked_sub(minimum_reserve_lamports(&meta)) - .ok_or(StakePoolError::CalculationFailure)? - } else { - msg!("Reserve stake account in unknown state, aborting"); - return Err(StakePoolError::WrongStakeState.into()); - }; - for validator_stake_record in validator_list.iter::() { - if validator_stake_record.last_update_epoch < clock.epoch { + if u64::from(validator_stake_record.last_update_epoch) < clock.epoch { return Err(StakePoolError::StakeListOutOfDate.into()); } total_lamports = total_lamports @@ -2582,7 +2682,7 @@ impl Processor { return Err(StakePoolError::InvalidState.into()); } - validator_list.retain::(ValidatorStakeInfo::is_not_removed)?; + validator_list.retain::(ValidatorStakeInfo::is_not_removed)?; Ok(()) } @@ -2667,7 +2767,7 @@ impl Processor { } } - let mut validator_stake_info = validator_list + let validator_stake_info = validator_list .find_mut::(|x| { ValidatorStakeInfo::memcmp_pubkey(x, &vote_account_address) }) @@ -2677,10 +2777,10 @@ impl Processor { stake_pool_info.key, validator_stake_account_info.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!("Validator is marked for removal and no longer accepting deposits"); return Err(StakePoolError::ValidatorNotFound.into()); } @@ -2847,7 +2947,7 @@ impl Processor { .ok_or(StakePoolError::CalculationFailure)?; borsh::to_writer(&mut stake_pool_info.data.borrow_mut()[..], &stake_pool)?; - validator_stake_info.active_stake_lamports = validator_stake_account_info.lamports(); + validator_stake_info.active_stake_lamports = validator_stake_account_info.lamports().into(); Ok(()) } @@ -3088,9 +3188,10 @@ impl Processor { } let stake_minimum_delegation = stake::tools::get_minimum_delegation()?; - let stake_state = - try_from_slice_unchecked::(&stake_split_from.data.borrow())?; - let meta = stake_state.meta().ok_or(StakePoolError::WrongStakeState)?; + let stake_state = try_from_slice_unchecked::( + &stake_split_from.data.borrow(), + )?; + let meta = stake_state.meta().ok_or(StakePoolError::WrongStakeStake)?; let required_lamports = minimum_stake_lamports(&meta, stake_minimum_delegation); let lamports_per_pool_token = stake_pool @@ -3132,7 +3233,7 @@ impl Processor { } else { let delegation = stake_state .delegation() - .ok_or(StakePoolError::WrongStakeState)?; + .ok_or(StakePoolError::WrongStakeStake)?; let vote_account_address = delegation.voter_pubkey; if let Some(preferred_withdraw_validator) = @@ -3143,11 +3244,10 @@ impl Processor { ValidatorStakeInfo::memcmp_pubkey(x, &preferred_withdraw_validator) }) .ok_or(StakePoolError::ValidatorNotFound)?; - let available_lamports = preferred_validator_info - .active_stake_lamports + let available_lamports = u64::from(preferred_validator_info.active_stake_lamports) .saturating_sub(minimum_lamports_with_tolerance); if preferred_withdraw_validator != vote_account_address && available_lamports > 0 { - msg!("Validator vote address {} is preferred for withdrawals, it currently has {} lamports available. Please withdraw those before using other validator stake accounts.", preferred_withdraw_validator, preferred_validator_info.active_stake_lamports); + msg!("Validator vote address {} is preferred for withdrawals, it currently has {} lamports available. Please withdraw those before using other validator stake accounts.", preferred_withdraw_validator, u64::from(preferred_validator_info.active_stake_lamports)); return Err(StakePoolError::IncorrectWithdrawVoteAddress.into()); } } @@ -3166,7 +3266,7 @@ impl Processor { stake_pool_info.key, stake_split_from.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; StakeWithdrawSource::Active } else if has_transient_stake { @@ -3176,7 +3276,7 @@ impl Processor { stake_pool_info.key, stake_split_from.key, &vote_account_address, - validator_stake_info.transient_seed_suffix, + validator_stake_info.transient_seed_suffix.into(), )?; StakeWithdrawSource::Transient } else { @@ -3186,12 +3286,12 @@ impl Processor { stake_pool_info.key, stake_split_from.key, &vote_account_address, - NonZeroU32::new(validator_stake_info.validator_seed_suffix), + NonZeroU32::new(validator_stake_info.validator_seed_suffix.into()), )?; StakeWithdrawSource::ValidatorRemoval }; - if validator_stake_info.status != StakeStatus::Active { + if validator_stake_info.status != StakeStatus::Active.into() { msg!("Validator is marked for removal and no longer allowing withdrawals"); return Err(StakePoolError::ValidatorNotFound.into()); } @@ -3280,30 +3380,33 @@ impl Processor { if let Some((validator_list_item, withdraw_source)) = validator_list_item_info { match withdraw_source { StakeWithdrawSource::Active => { - validator_list_item.active_stake_lamports = validator_list_item - .active_stake_lamports - .checked_sub(withdraw_lamports) - .ok_or(StakePoolError::CalculationFailure)? + validator_list_item.active_stake_lamports = + u64::from(validator_list_item.active_stake_lamports) + .checked_sub(withdraw_lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into() } StakeWithdrawSource::Transient => { - validator_list_item.transient_stake_lamports = validator_list_item - .transient_stake_lamports - .checked_sub(withdraw_lamports) - .ok_or(StakePoolError::CalculationFailure)? + validator_list_item.transient_stake_lamports = + u64::from(validator_list_item.transient_stake_lamports) + .checked_sub(withdraw_lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into() } StakeWithdrawSource::ValidatorRemoval => { - validator_list_item.active_stake_lamports = validator_list_item - .active_stake_lamports - .checked_sub(withdraw_lamports) - .ok_or(StakePoolError::CalculationFailure)?; - if validator_list_item.active_stake_lamports != 0 { + validator_list_item.active_stake_lamports = + u64::from(validator_list_item.active_stake_lamports) + .checked_sub(withdraw_lamports) + .ok_or(StakePoolError::CalculationFailure)? + .into(); + if u64::from(validator_list_item.active_stake_lamports) != 0 { msg!("Attempting to remove a validator from the pool, but withdrawal leaves {} lamports, update the pool to merge any unaccounted lamports", - validator_list_item.active_stake_lamports); + u64::from(validator_list_item.active_stake_lamports)); return Err(StakePoolError::StakeListAndPoolOutOfDate.into()); } // since we already checked that there's no transient stake, // we can immediately set this as ready for removal - validator_list_item.status = StakeStatus::ReadyForRemoval; + validator_list_item.status = StakeStatus::ReadyForRemoval.into(); } } } @@ -3396,10 +3499,10 @@ impl Processor { let new_reserve_lamports = reserve_stake_info .lamports() .saturating_sub(withdraw_lamports); - let stake_state = try_from_slice_unchecked::( + let stake_state = try_from_slice_unchecked::( &reserve_stake_info.data.borrow(), )?; - if let stake::state::StakeState::Initialized(meta) = stake_state { + if let stake::state::StakeStateV2::Initialized(meta) = stake_state { let minimum_reserve_lamports = minimum_reserve_lamports(&meta); if new_reserve_lamports < minimum_reserve_lamports { msg!("Attempting to withdraw {} lamports, maximum possible SOL withdrawal is {} lamports", @@ -3410,7 +3513,7 @@ impl Processor { } } else { msg!("Reserve stake account not in intialized state"); - return Err(StakePoolError::WrongStakeState.into()); + return Err(StakePoolError::WrongStakeStake.into()); }; Self::token_burn( @@ -3764,12 +3867,28 @@ impl Processor { transient_stake_seed, } => { msg!("Instruction: DecreaseValidatorStake"); + msg!("NOTE: This instruction is deprecated, please use `DecreaseValidatorStakeWithReserve`"); + Self::process_decrease_validator_stake( + program_id, + accounts, + lamports, + transient_stake_seed, + None, + false, + ) + } + StakePoolInstruction::DecreaseValidatorStakeWithReserve { + lamports, + transient_stake_seed, + } => { + msg!("Instruction: DecreaseValidatorStakeWithReserve"); Self::process_decrease_validator_stake( program_id, accounts, lamports, transient_stake_seed, None, + true, ) } StakePoolInstruction::DecreaseAdditionalValidatorStake { @@ -3784,6 +3903,7 @@ impl Processor { lamports, transient_stake_seed, Some(ephemeral_stake_seed), + true, ) } StakePoolInstruction::IncreaseValidatorStake { @@ -3964,7 +4084,7 @@ impl PrintProgramError for StakePoolError { StakePoolError::InvalidValidatorStakeList => msg!("Error: Invalid validator stake list account"), StakePoolError::InvalidFeeAccount => msg!("Error: Invalid manager fee account"), StakePoolError::WrongPoolMint => msg!("Error: Specified pool mint account is wrong"), - StakePoolError::WrongStakeState => msg!("Error: Stake account is not in the state expected by the program"), + StakePoolError::WrongStakeStake => msg!("Error: Stake account is not in the state expected by the program"), StakePoolError::UserStakeNotActive => msg!("Error: User stake is not active"), StakePoolError::ValidatorAlreadyAdded => msg!("Error: Stake account voting for this validator already exists in the pool"), StakePoolError::ValidatorNotFound => msg!("Error: Stake account for this validator not found in the pool"), @@ -3995,6 +4115,9 @@ impl PrintProgramError for StakePoolError { StakePoolError::UnsupportedMintExtension => msg!("Error: mint has an unsupported extension"), StakePoolError::UnsupportedFeeAccountExtension => msg!("Error: fee account has an unsupported extension"), StakePoolError::ExceededSlippage => msg!("Error: instruction exceeds desired slippage limit"), + StakePoolError::IncorrectMintDecimals => msg!("Error: Provided mint does not have 9 decimals to match SOL"), + StakePoolError::ReserveDepleted => msg!("Error: Pool reserve does not have enough lamports to fund rent-exempt reserve in split destination. Deposit more SOL in reserve, or pre-fund split destination with the rent-exempt reserve for a stake account."), + StakePoolError::MissingRequiredSysvar => msg!("Missing required sysvar account"), } } } diff --git a/stake-pool/program/src/state.rs b/stake-pool/program/src/state.rs index b8ea873b567..b218cecd40b 100644 --- a/stake-pool/program/src/state.rs +++ b/stake-pool/program/src/state.rs @@ -6,11 +6,12 @@ use { WITHDRAWAL_BASELINE_FEE, }, borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, - num_derive::FromPrimitive, - num_traits::FromPrimitive, + bytemuck::{Pod, Zeroable}, + num_derive::{FromPrimitive, ToPrimitive}, + num_traits::{FromPrimitive, ToPrimitive}, solana_program::{ account_info::AccountInfo, - borsh::get_instance_packed_len, + borsh0_10::get_instance_packed_len, msg, program_error::ProgramError, program_memory::sol_memcmp, @@ -18,6 +19,7 @@ use { pubkey::{Pubkey, PUBKEY_BYTES}, stake::state::Lockup, }, + spl_pod::primitives::{PodU32, PodU64}, spl_token_2022::{ extension::{BaseStateWithExtensions, ExtensionType, StateWithExtensions}, state::{Account, AccountState, Mint}, @@ -492,12 +494,14 @@ impl StakePool { /// Checks if the given extension is supported for the stake pool mint pub fn is_extension_supported_for_mint(extension_type: &ExtensionType) -> bool { - const SUPPORTED_EXTENSIONS: [ExtensionType; 5] = [ + const SUPPORTED_EXTENSIONS: [ExtensionType; 7] = [ ExtensionType::Uninitialized, ExtensionType::TransferFeeConfig, ExtensionType::ConfidentialTransferMint, ExtensionType::DefaultAccountState, // ok, but a freeze authority is not ExtensionType::InterestBearingConfig, + ExtensionType::MetadataPointer, + ExtensionType::TokenMetadata, ]; if !SUPPORTED_EXTENSIONS.contains(extension_type) { msg!( @@ -553,7 +557,15 @@ pub struct ValidatorListHeader { /// Status of the stake account in the validator list, for accounting #[derive( - FromPrimitive, Copy, Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema, + ToPrimitive, + FromPrimitive, + Copy, + Clone, + Debug, + PartialEq, + BorshDeserialize, + BorshSerialize, + BorshSchema, )] pub enum StakeStatus { /// Stake account is active, there may be a transient stake as well @@ -571,29 +583,67 @@ pub enum StakeStatus { /// a validator is removed with a transient stake active DeactivatingAll, } -impl StakeStatus { +impl Default for StakeStatus { + fn default() -> Self { + Self::Active + } +} + +/// Wrapper struct that can be `Pod`, containing a byte that *should* be a valid +/// `StakeStatus` underneath. +#[repr(transparent)] +#[derive( + Clone, + Copy, + Debug, + Default, + PartialEq, + Pod, + Zeroable, + BorshDeserialize, + BorshSerialize, + BorshSchema, +)] +pub struct PodStakeStatus(u8); +impl PodStakeStatus { /// Downgrade the status towards ready for removal by removing the validator stake - pub fn remove_validator_stake(&mut self) { - let new_self = match self { - Self::Active | Self::DeactivatingTransient | Self::ReadyForRemoval => *self, - Self::DeactivatingAll => Self::DeactivatingTransient, - Self::DeactivatingValidator => Self::ReadyForRemoval, + pub fn remove_validator_stake(&mut self) -> Result<(), ProgramError> { + let status = StakeStatus::try_from(*self)?; + let new_self = match status { + StakeStatus::Active + | StakeStatus::DeactivatingTransient + | StakeStatus::ReadyForRemoval => status, + StakeStatus::DeactivatingAll => StakeStatus::DeactivatingTransient, + StakeStatus::DeactivatingValidator => StakeStatus::ReadyForRemoval, }; - *self = new_self; + *self = new_self.into(); + Ok(()) } /// Downgrade the status towards ready for removal by removing the transient stake - pub fn remove_transient_stake(&mut self) { - let new_self = match self { - Self::Active | Self::DeactivatingValidator | Self::ReadyForRemoval => *self, - Self::DeactivatingAll => Self::DeactivatingValidator, - Self::DeactivatingTransient => Self::ReadyForRemoval, + pub fn remove_transient_stake(&mut self) -> Result<(), ProgramError> { + let status = StakeStatus::try_from(*self)?; + let new_self = match status { + StakeStatus::Active + | StakeStatus::DeactivatingValidator + | StakeStatus::ReadyForRemoval => status, + StakeStatus::DeactivatingAll => StakeStatus::DeactivatingValidator, + StakeStatus::DeactivatingTransient => StakeStatus::ReadyForRemoval, }; - *self = new_self; + *self = new_self.into(); + Ok(()) } } -impl Default for StakeStatus { - fn default() -> Self { - Self::Active +impl TryFrom for StakeStatus { + type Error = ProgramError; + fn try_from(pod: PodStakeStatus) -> Result { + FromPrimitive::from_u8(pod.0).ok_or(ProgramError::InvalidAccountData) + } +} +impl From for PodStakeStatus { + fn from(status: StakeStatus) -> Self { + // unwrap is safe here because the variants of `StakeStatus` fit very + // comfortably within a `u8` + PodStakeStatus(status.to_u8().unwrap()) } } @@ -613,38 +663,49 @@ pub(crate) enum StakeWithdrawSource { /// NOTE: ORDER IS VERY IMPORTANT HERE, PLEASE DO NOT RE-ORDER THE FIELDS UNLESS /// THERE'S AN EXTREMELY GOOD REASON. /// -/// To save on BPF instructions, the serialized bytes are reinterpreted with an -/// unsafe pointer cast, which means that this structure cannot have any +/// To save on BPF instructions, the serialized bytes are reinterpreted with a +/// bytemuck transmute, which means that this structure cannot have any /// undeclared alignment-padding in its representation. #[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive( + Clone, + Copy, + Debug, + Default, + PartialEq, + Pod, + Zeroable, + BorshDeserialize, + BorshSerialize, + BorshSchema, +)] pub struct ValidatorStakeInfo { /// Amount of lamports on the validator stake account, including rent /// /// Note that if `last_update_epoch` does not match the current epoch then /// this field may not be accurate - pub active_stake_lamports: u64, + pub active_stake_lamports: PodU64, /// Amount of transient stake delegated to this validator /// /// Note that if `last_update_epoch` does not match the current epoch then /// this field may not be accurate - pub transient_stake_lamports: u64, + pub transient_stake_lamports: PodU64, /// Last epoch the active and transient stake lamports fields were updated - pub last_update_epoch: u64, + pub last_update_epoch: PodU64, /// Transient account seed suffix, used to derive the transient stake account address - pub transient_seed_suffix: u64, + pub transient_seed_suffix: PodU64, /// Unused space, initially meant to specify the end of seed suffixes - pub unused: u32, + pub unused: PodU32, /// Validator account seed suffix - pub validator_seed_suffix: u32, // really `Option` so 0 is `None` + pub validator_seed_suffix: PodU32, // really `Option` so 0 is `None` /// Status of the validator stake account - pub status: StakeStatus, + pub status: PodStakeStatus, /// Validator vote account address pub vote_account_address: Pubkey, @@ -653,8 +714,8 @@ pub struct ValidatorStakeInfo { impl ValidatorStakeInfo { /// Get the total lamports on this validator (active and transient) pub fn stake_lamports(&self) -> Result { - self.active_stake_lamports - .checked_add(self.transient_stake_lamports) + u64::from(self.active_stake_lamports) + .checked_add(self.transient_stake_lamports.into()) .ok_or(StakePoolError::CalculationFailure) } @@ -746,7 +807,9 @@ impl ValidatorList { /// Check if the list has any active stake pub fn has_active_stake(&self) -> bool { - self.validators.iter().any(|x| x.active_stake_lamports > 0) + self.validators + .iter() + .any(|x| u64::from(x.active_stake_lamports) > 0) } } @@ -765,14 +828,12 @@ impl ValidatorListHeader { /// Extracts a slice of ValidatorStakeInfo types from the vec part /// of the ValidatorList - pub fn deserialize_mut_slice( - data: &mut [u8], + pub fn deserialize_mut_slice<'a>( + big_vec: &'a mut BigVec, skip: usize, len: usize, - ) -> Result<(Self, Vec<&mut ValidatorStakeInfo>), ProgramError> { - let (header, mut big_vec) = Self::deserialize_vec(data)?; - let validator_list = big_vec.deserialize_mut_slice::(skip, len)?; - Ok((header, validator_list)) + ) -> Result<&'a mut [ValidatorStakeInfo], ProgramError> { + big_vec.deserialize_mut_slice::(skip, len) } /// Extracts the validator list into its header and internal BigVec @@ -969,12 +1030,12 @@ impl FeeType { #[cfg(test)] mod test { - #![allow(clippy::integer_arithmetic)] + #![allow(clippy::arithmetic_side_effects)] use { super::*, proptest::prelude::*, solana_program::{ - borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked}, + borsh0_10::{get_packed_len, try_from_slice_unchecked}, clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_S_PER_SLOT, SECONDS_PER_DAY}, native_token::LAMPORTS_PER_SOL, }, @@ -998,34 +1059,34 @@ mod test { }, validators: vec![ ValidatorStakeInfo { - status: StakeStatus::Active, + status: StakeStatus::Active.into(), vote_account_address: Pubkey::new_from_array([1; 32]), - active_stake_lamports: u64::from_le_bytes([255; 8]), - transient_stake_lamports: u64::from_le_bytes([128; 8]), - last_update_epoch: u64::from_le_bytes([64; 8]), - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: 0, + active_stake_lamports: u64::from_le_bytes([255; 8]).into(), + transient_stake_lamports: u64::from_le_bytes([128; 8]).into(), + last_update_epoch: u64::from_le_bytes([64; 8]).into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: 0.into(), }, ValidatorStakeInfo { - status: StakeStatus::DeactivatingTransient, + status: StakeStatus::DeactivatingTransient.into(), vote_account_address: Pubkey::new_from_array([2; 32]), - active_stake_lamports: 998877665544, - transient_stake_lamports: 222222222, - last_update_epoch: 11223445566, - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: 0, + active_stake_lamports: 998877665544.into(), + transient_stake_lamports: 222222222.into(), + last_update_epoch: 11223445566.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: 0.into(), }, ValidatorStakeInfo { - status: StakeStatus::ReadyForRemoval, + status: StakeStatus::ReadyForRemoval.into(), vote_account_address: Pubkey::new_from_array([3; 32]), - active_stake_lamports: 0, - transient_stake_lamports: 0, - last_update_epoch: 999999999999999, - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: 0, + active_stake_lamports: 0.into(), + transient_stake_lamports: 0.into(), + last_update_epoch: 999999999999999.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: 0.into(), }, ], } @@ -1071,7 +1132,7 @@ mod test { let mut validator_list = test_validator_list(max_validators); assert!(validator_list.has_active_stake()); for validator in validator_list.validators.iter_mut() { - validator.active_stake_lamports = 0; + validator.active_stake_lamports = 0.into(); } assert!(!validator_list.has_active_stake()); } @@ -1081,8 +1142,9 @@ mod test { let max_validators = 10; let stake_list = test_validator_list(max_validators); let mut serialized = stake_list.try_to_vec().unwrap(); - let (header, list) = ValidatorListHeader::deserialize_mut_slice( - &mut serialized, + let (header, mut big_vec) = ValidatorListHeader::deserialize_vec(&mut serialized).unwrap(); + let list = ValidatorListHeader::deserialize_mut_slice( + &mut big_vec, 0, stake_list.validators.len(), ) @@ -1092,30 +1154,30 @@ mod test { assert!(list .iter() .zip(stake_list.validators.iter()) - .all(|(a, b)| *a == b)); + .all(|(a, b)| a == b)); - let (_, list) = ValidatorListHeader::deserialize_mut_slice(&mut serialized, 1, 2).unwrap(); + let list = ValidatorListHeader::deserialize_mut_slice(&mut big_vec, 1, 2).unwrap(); assert!(list .iter() .zip(stake_list.validators[1..].iter()) - .all(|(a, b)| *a == b)); - let (_, list) = ValidatorListHeader::deserialize_mut_slice(&mut serialized, 2, 1).unwrap(); + .all(|(a, b)| a == b)); + let list = ValidatorListHeader::deserialize_mut_slice(&mut big_vec, 2, 1).unwrap(); assert!(list .iter() .zip(stake_list.validators[2..].iter()) - .all(|(a, b)| *a == b)); - let (_, list) = ValidatorListHeader::deserialize_mut_slice(&mut serialized, 0, 2).unwrap(); + .all(|(a, b)| a == b)); + let list = ValidatorListHeader::deserialize_mut_slice(&mut big_vec, 0, 2).unwrap(); assert!(list .iter() .zip(stake_list.validators[..2].iter()) - .all(|(a, b)| *a == b)); + .all(|(a, b)| a == b)); assert_eq!( - ValidatorListHeader::deserialize_mut_slice(&mut serialized, 0, 4).unwrap_err(), + ValidatorListHeader::deserialize_mut_slice(&mut big_vec, 0, 4).unwrap_err(), ProgramError::AccountDataTooSmall ); assert_eq!( - ValidatorListHeader::deserialize_mut_slice(&mut serialized, 1, 3).unwrap_err(), + ValidatorListHeader::deserialize_mut_slice(&mut big_vec, 1, 3).unwrap_err(), ProgramError::AccountDataTooSmall ); } @@ -1127,7 +1189,9 @@ mod test { let mut serialized = stake_list.try_to_vec().unwrap(); let (_, big_vec) = ValidatorListHeader::deserialize_vec(&mut serialized).unwrap(); for (a, b) in big_vec - .iter::() + .deserialize_slice::(0, big_vec.len() as usize) + .unwrap() + .iter() .zip(stake_list.validators.iter()) { assert_eq!(a, b); diff --git a/stake-pool/program/tests/create_pool_token_metadata.rs b/stake-pool/program/tests/create_pool_token_metadata.rs index 7734c785d5c..147e3928a07 100644 --- a/stake-pool/program/tests/create_pool_token_metadata.rs +++ b/stake-pool/program/tests/create_pool_token_metadata.rs @@ -1,4 +1,5 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/stake-pool/program/tests/decrease.rs b/stake-pool/program/tests/decrease.rs index 1293fd7806e..fe6398ae7b6 100644 --- a/stake-pool/program/tests/decrease.rs +++ b/stake-pool/program/tests/decrease.rs @@ -1,9 +1,11 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; use { + assert_matches::assert_matches, bincode::deserialize, helpers::*, solana_program::{clock::Epoch, instruction::InstructionError, pubkey::Pubkey, stake}, @@ -25,10 +27,11 @@ async fn setup() -> ( ValidatorStakeAccount, DepositStakeAccount, u64, + u64, ) { let mut context = program_test().start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -37,12 +40,13 @@ async fn setup() -> ( .await; let stake_pool_accounts = StakePoolAccounts::default(); + let reserve_lamports = MINIMUM_RESERVE_LAMPORTS + stake_rent + current_minimum_delegation; stake_pool_accounts .initialize_stake_pool( &mut context.banks_client, &context.payer, &context.last_blockhash, - MINIMUM_RESERVE_LAMPORTS + stake_rent + current_minimum_delegation, + reserve_lamports, ) .await .unwrap(); @@ -74,15 +78,23 @@ async fn setup() -> ( validator_stake_account, deposit_info, decrease_lamports, + reserve_lamports + stake_rent, ) } -#[test_case(true; "additional")] -#[test_case(false; "no-additional")] +#[test_case(DecreaseInstruction::Additional; "additional")] +#[test_case(DecreaseInstruction::Reserve; "reserve")] +#[test_case(DecreaseInstruction::Deprecated; "deprecated")] #[tokio::test] -async fn success(use_additional_instruction: bool) { - let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports) = - setup().await; +async fn success(instruction_type: DecreaseInstruction) { + let ( + mut context, + stake_pool_accounts, + validator_stake, + _deposit_info, + decrease_lamports, + reserve_lamports, + ) = setup().await; // Save validator stake let pre_validator_stake_account = @@ -105,16 +117,16 @@ async fn success(use_additional_instruction: bool) { &validator_stake.transient_stake_account, decrease_lamports, validator_stake.transient_stake_seed, - use_additional_instruction, + instruction_type, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check validator stake account balance let validator_stake_account = get_account(&mut context.banks_client, &validator_stake.stake_account).await; let validator_stake_state = - deserialize::(&validator_stake_account.data).unwrap(); + deserialize::(&validator_stake_account.data).unwrap(); assert_eq!( pre_validator_stake_account.lamports - decrease_lamports, validator_stake_account.lamports @@ -128,14 +140,29 @@ async fn success(use_additional_instruction: bool) { ); // Check transient stake account state and balance + let rent = context.banks_client.get_rent().await.unwrap(); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let transient_stake_account = get_account( &mut context.banks_client, &validator_stake.transient_stake_account, ) .await; let transient_stake_state = - deserialize::(&transient_stake_account.data).unwrap(); - assert_eq!(transient_stake_account.lamports, decrease_lamports); + deserialize::(&transient_stake_account.data).unwrap(); + let transient_lamports = decrease_lamports + stake_rent; + assert_eq!(transient_stake_account.lamports, transient_lamports); + let reserve_lamports = if instruction_type == DecreaseInstruction::Deprecated { + reserve_lamports + } else { + reserve_lamports - stake_rent + }; + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!(reserve_stake_account.lamports, reserve_lamports); assert_ne!( transient_stake_state .delegation() @@ -147,18 +174,19 @@ async fn success(use_additional_instruction: bool) { #[tokio::test] async fn fail_with_wrong_withdraw_authority() { - let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports) = + let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports, _) = setup().await; let wrong_authority = Pubkey::new_unique(); let transaction = Transaction::new_signed_with_payer( - &[instruction::decrease_validator_stake( + &[instruction::decrease_validator_stake_with_reserve( &id(), &stake_pool_accounts.stake_pool.pubkey(), &stake_pool_accounts.staker.pubkey(), &wrong_authority, &stake_pool_accounts.validator_list.pubkey(), + &stake_pool_accounts.reserve_stake.pubkey(), &validator_stake.stake_account, &validator_stake.transient_stake_account, decrease_lamports, @@ -176,29 +204,36 @@ async fn fail_with_wrong_withdraw_authority() { .unwrap() .unwrap(); - match error { - TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => { - let program_error = StakePoolError::InvalidProgramAddress as u32; - assert_eq!(error_index, program_error); - } - _ => panic!("Wrong error occurs while decreasing with wrong withdraw authority"), - } + assert_eq!( + error, + TransactionError::InstructionError( + 0, + InstructionError::Custom(StakePoolError::InvalidProgramAddress as u32) + ) + ); } #[tokio::test] async fn fail_with_wrong_validator_list() { - let (mut context, mut stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports) = - setup().await; + let ( + mut context, + mut stake_pool_accounts, + validator_stake, + _deposit_info, + decrease_lamports, + _, + ) = setup().await; stake_pool_accounts.validator_list = Keypair::new(); let transaction = Transaction::new_signed_with_payer( - &[instruction::decrease_validator_stake( + &[instruction::decrease_validator_stake_with_reserve( &id(), &stake_pool_accounts.stake_pool.pubkey(), &stake_pool_accounts.staker.pubkey(), &stake_pool_accounts.withdraw_authority, &stake_pool_accounts.validator_list.pubkey(), + &stake_pool_accounts.reserve_stake.pubkey(), &validator_stake.stake_account, &validator_stake.transient_stake_account, decrease_lamports, @@ -216,18 +251,18 @@ async fn fail_with_wrong_validator_list() { .unwrap() .unwrap(); - match error { - TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => { - let program_error = StakePoolError::InvalidValidatorStakeList as u32; - assert_eq!(error_index, program_error); - } - _ => panic!("Wrong error occurs while decreasing with wrong validator stake list account"), - } + assert_eq!( + error, + TransactionError::InstructionError( + 0, + InstructionError::Custom(StakePoolError::InvalidValidatorStakeList as u32) + ) + ); } #[tokio::test] async fn fail_with_unknown_validator() { - let (mut context, stake_pool_accounts, _validator_stake, _deposit_info, decrease_lamports) = + let (mut context, stake_pool_accounts, _validator_stake, _deposit_info, decrease_lamports, _) = setup().await; let unknown_stake = create_unknown_validator_stake( @@ -240,12 +275,13 @@ async fn fail_with_unknown_validator() { .await; let transaction = Transaction::new_signed_with_payer( - &[instruction::decrease_validator_stake( + &[instruction::decrease_validator_stake_with_reserve( &id(), &stake_pool_accounts.stake_pool.pubkey(), &stake_pool_accounts.staker.pubkey(), &stake_pool_accounts.withdraw_authority, &stake_pool_accounts.validator_list.pubkey(), + &stake_pool_accounts.reserve_stake.pubkey(), &unknown_stake.stake_account, &unknown_stake.transient_stake_account, decrease_lamports, @@ -272,11 +308,12 @@ async fn fail_with_unknown_validator() { ); } -#[test_case(true; "additional")] -#[test_case(false; "no-additional")] +#[test_case(DecreaseInstruction::Additional; "additional")] +#[test_case(DecreaseInstruction::Reserve; "reserve")] +#[test_case(DecreaseInstruction::Deprecated; "deprecated")] #[tokio::test] -async fn fail_twice_diff_seed(use_additional_instruction: bool) { - let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports) = +async fn fail_twice_diff_seed(instruction_type: DecreaseInstruction) { + let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports, _) = setup().await; let error = stake_pool_accounts @@ -288,10 +325,10 @@ async fn fail_twice_diff_seed(use_additional_instruction: bool) { &validator_stake.transient_stake_account, decrease_lamports / 3, validator_stake.transient_stake_seed, - use_additional_instruction, + instruction_type, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let transient_stake_seed = validator_stake.transient_stake_seed * 100; let transient_stake_address = find_transient_stake_program_address( @@ -310,39 +347,52 @@ async fn fail_twice_diff_seed(use_additional_instruction: bool) { &transient_stake_address, decrease_lamports / 2, transient_stake_seed, - use_additional_instruction, + instruction_type, ) .await .unwrap() .unwrap(); - if use_additional_instruction { + if instruction_type == DecreaseInstruction::Additional { assert_eq!( error, TransactionError::InstructionError(0, InstructionError::InvalidSeeds) ); } else { - assert_eq!( + assert_matches!( error, TransactionError::InstructionError( - 0, - InstructionError::Custom(StakePoolError::TransientAccountInUse as u32) - ) + _, + InstructionError::Custom(code) + ) if code == StakePoolError::TransientAccountInUse as u32 ); } } -#[test_case(true, true, true; "success-all-additional")] -#[test_case(true, false, true; "success-with-additional")] -#[test_case(false, true, false; "fail-without-additional")] -#[test_case(false, false, false; "fail-no-additional")] +#[test_case(true, DecreaseInstruction::Additional, DecreaseInstruction::Additional; "success-all-additional")] +#[test_case(true, DecreaseInstruction::Reserve, DecreaseInstruction::Additional; "success-with-additional")] +#[test_case(false, DecreaseInstruction::Additional, DecreaseInstruction::Reserve; "fail-without-additional")] +#[test_case(false, DecreaseInstruction::Reserve, DecreaseInstruction::Reserve; "fail-no-additional")] #[tokio::test] -async fn twice(success: bool, use_additional_first_time: bool, use_additional_second_time: bool) { - let (mut context, stake_pool_accounts, validator_stake, _deposit_info, decrease_lamports) = - setup().await; +async fn twice( + success: bool, + first_instruction: DecreaseInstruction, + second_instruction: DecreaseInstruction, +) { + let ( + mut context, + stake_pool_accounts, + validator_stake, + _deposit_info, + decrease_lamports, + reserve_lamports, + ) = setup().await; let pre_stake_account = get_account(&mut context.banks_client, &validator_stake.stake_account).await; + let rent = context.banks_client.get_rent().await.unwrap(); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let first_decrease = decrease_lamports / 3; let second_decrease = decrease_lamports / 2; let total_decrease = first_decrease + second_decrease; @@ -355,10 +405,10 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se &validator_stake.transient_stake_account, first_decrease, validator_stake.transient_stake_seed, - use_additional_first_time, + first_instruction, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .decrease_validator_stake_either( @@ -369,12 +419,12 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se &validator_stake.transient_stake_account, second_decrease, validator_stake.transient_stake_seed, - use_additional_second_time, + second_instruction, ) .await; if success { - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // no ephemeral account let ephemeral_stake = find_ephemeral_stake_program_address( &id(), @@ -392,7 +442,7 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se // Check validator stake account balance let stake_account = get_account(&mut context.banks_client, &validator_stake.stake_account).await; - let stake_state = deserialize::(&stake_account.data).unwrap(); + let stake_state = deserialize::(&stake_account.data).unwrap(); assert_eq!( pre_stake_account.lamports - total_decrease, stake_account.lamports @@ -409,8 +459,12 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se ) .await; let transient_stake_state = - deserialize::(&transient_stake_account.data).unwrap(); - assert_eq!(transient_stake_account.lamports, total_decrease); + deserialize::(&transient_stake_account.data).unwrap(); + let mut transient_lamports = total_decrease + stake_rent; + if second_instruction == DecreaseInstruction::Additional { + transient_lamports += stake_rent; + } + assert_eq!(transient_stake_account.lamports, transient_lamports); assert_ne!( transient_stake_state .delegation() @@ -424,28 +478,44 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se .get_validator_list(&mut context.banks_client) .await; let entry = validator_list.find(&validator_stake.vote.pubkey()).unwrap(); - assert_eq!(entry.transient_stake_lamports, total_decrease); + assert_eq!( + u64::from(entry.transient_stake_lamports), + transient_lamports + ); + + // reserve deducted properly + let mut reserve_lamports = reserve_lamports - stake_rent; + if second_instruction == DecreaseInstruction::Additional { + reserve_lamports -= stake_rent; + } + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!(reserve_stake_account.lamports, reserve_lamports); } else { let error = error.unwrap().unwrap(); - assert_eq!( + assert_matches!( error, TransactionError::InstructionError( - 0, - InstructionError::Custom(StakePoolError::TransientAccountInUse as u32) - ) + _, + InstructionError::Custom(code) + ) if code == StakePoolError::TransientAccountInUse as u32 ); } } -#[test_case(true; "additional")] -#[test_case(false; "no-additional")] +#[test_case(DecreaseInstruction::Additional; "additional")] +#[test_case(DecreaseInstruction::Reserve; "reserve")] +#[test_case(DecreaseInstruction::Deprecated; "deprecated")] #[tokio::test] -async fn fail_with_small_lamport_amount(use_additional_instruction: bool) { - let (mut context, stake_pool_accounts, validator_stake, _deposit_info, _decrease_lamports) = +async fn fail_with_small_lamport_amount(instruction_type: DecreaseInstruction) { + let (mut context, stake_pool_accounts, validator_stake, _deposit_info, _decrease_lamports, _) = setup().await; let rent = context.banks_client.get_rent().await.unwrap(); - let lamports = rent.minimum_balance(std::mem::size_of::()); + let lamports = rent.minimum_balance(std::mem::size_of::()); let error = stake_pool_accounts .decrease_validator_stake_either( @@ -456,25 +526,28 @@ async fn fail_with_small_lamport_amount(use_additional_instruction: bool) { &validator_stake.transient_stake_account, lamports, validator_stake.transient_stake_seed, - use_additional_instruction, + instruction_type, ) .await .unwrap() .unwrap(); - match error { - TransactionError::InstructionError(_, InstructionError::AccountNotRentExempt) => {} - _ => panic!("Wrong error occurs while try to decrease small stake"), - } + assert_matches!( + error, + TransactionError::InstructionError(_, InstructionError::AccountNotRentExempt) + ); } +#[test_case(DecreaseInstruction::Additional; "additional")] +#[test_case(DecreaseInstruction::Reserve; "reserve")] +#[test_case(DecreaseInstruction::Deprecated; "deprecated")] #[tokio::test] -async fn fail_big_overdraw() { - let (mut context, stake_pool_accounts, validator_stake, deposit_info, _decrease_lamports) = +async fn fail_big_overdraw(instruction_type: DecreaseInstruction) { + let (mut context, stake_pool_accounts, validator_stake, deposit_info, _decrease_lamports, _) = setup().await; let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -482,26 +555,28 @@ async fn fail_big_overdraw() { &validator_stake.transient_stake_account, deposit_info.stake_lamports * 1_000_000, validator_stake.transient_stake_seed, + instruction_type, ) .await .unwrap() .unwrap(); - assert_eq!( + assert_matches!( error, - TransactionError::InstructionError(0, InstructionError::InsufficientFunds) + TransactionError::InstructionError(_, InstructionError::InsufficientFunds) ); } -#[test_case(true; "additional")] -#[test_case(false; "no-additional")] +#[test_case(DecreaseInstruction::Additional; "additional")] +#[test_case(DecreaseInstruction::Reserve; "reserve")] +#[test_case(DecreaseInstruction::Deprecated; "deprecated")] #[tokio::test] -async fn fail_overdraw(use_additional_instruction: bool) { - let (mut context, stake_pool_accounts, validator_stake, deposit_info, _decrease_lamports) = +async fn fail_overdraw(instruction_type: DecreaseInstruction) { + let (mut context, stake_pool_accounts, validator_stake, deposit_info, _decrease_lamports, _) = setup().await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let error = stake_pool_accounts .decrease_validator_stake_either( @@ -512,21 +587,22 @@ async fn fail_overdraw(use_additional_instruction: bool) { &validator_stake.transient_stake_account, deposit_info.stake_lamports + stake_rent + 1, validator_stake.transient_stake_seed, - use_additional_instruction, + instruction_type, ) .await .unwrap() .unwrap(); - assert_eq!( + assert_matches!( error, - TransactionError::InstructionError(0, InstructionError::InsufficientFunds) + TransactionError::InstructionError(_, InstructionError::InsufficientFunds) ); } #[tokio::test] async fn fail_additional_with_increasing() { - let (mut context, stake_pool_accounts, validator_stake, _, decrease_lamports) = setup().await; + let (mut context, stake_pool_accounts, validator_stake, _, decrease_lamports, _) = + setup().await; let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, @@ -537,7 +613,7 @@ async fn fail_additional_with_increasing() { // warp forward to activation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); let last_blockhash = context .banks_client .get_new_latest_blockhash(&context.last_blockhash) @@ -565,7 +641,7 @@ async fn fail_additional_with_increasing() { validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .decrease_validator_stake_either( @@ -576,17 +652,17 @@ async fn fail_additional_with_increasing() { &validator_stake.transient_stake_account, decrease_lamports / 2, validator_stake.transient_stake_seed, - true, + DecreaseInstruction::Additional, ) .await .unwrap() .unwrap(); - assert_eq!( + assert_matches!( error, TransactionError::InstructionError( - 0, - InstructionError::Custom(StakePoolError::WrongStakeState as u32) - ) + _, + InstructionError::Custom(code) + ) if code == StakePoolError::WrongStakeStake as u32 ); } diff --git a/stake-pool/program/tests/deposit.rs b/stake-pool/program/tests/deposit.rs index 7736918c215..74e96194fa1 100644 --- a/stake-pool/program/tests/deposit.rs +++ b/stake-pool/program/tests/deposit.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, stake, sysvar, @@ -88,7 +88,7 @@ async fn setup( .await; let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -140,7 +140,7 @@ async fn success(token_program_id: Pubkey) { ) = setup(token_program_id).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // Save stake pool state before depositing let pre_stake_pool = get_account( @@ -182,7 +182,7 @@ async fn success(token_program_id: Pubkey) { &user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Original stake account should be drained assert!(context @@ -249,7 +249,10 @@ async fn success(token_program_id: Pubkey) { validator_stake_account.lamports, post_validator_stake_item.stake_lamports().unwrap() ); - assert_eq!(post_validator_stake_item.transient_stake_lamports, 0); + assert_eq!( + u64::from(post_validator_stake_item.transient_stake_lamports), + 0 + ); // Check reserve let post_reserve_lamports = get_account( @@ -309,7 +312,7 @@ async fn success_with_extra_stake_lamports() { .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // Save stake pool state before depositing let pre_stake_pool = get_account( @@ -352,7 +355,7 @@ async fn success_with_extra_stake_lamports() { &referrer_token_account.pubkey(), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Original stake account should be drained assert!(context @@ -443,7 +446,10 @@ async fn success_with_extra_stake_lamports() { validator_stake_account.lamports, post_validator_stake_item.stake_lamports().unwrap() ); - assert_eq!(post_validator_stake_item.transient_stake_lamports, 0); + assert_eq!( + u64::from(post_validator_stake_item.transient_stake_lamports), + 0 + ); // Check reserve let post_reserve_lamports = get_account( @@ -815,7 +821,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { ) = setup(token_program_id).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // Save stake pool state before depositing let pre_stake_pool = get_account( @@ -869,7 +875,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { tokens_issued_user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Original stake account should be drained assert!(context diff --git a/stake-pool/program/tests/deposit_authority.rs b/stake-pool/program/tests/deposit_authority.rs index 8e44178a465..9d64f6da046 100644 --- a/stake-pool/program/tests/deposit_authority.rs +++ b/stake-pool/program/tests/deposit_authority.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -8,7 +8,7 @@ use { solana_program::{instruction::InstructionError, stake}, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, signature::{Keypair, Signer}, transaction::TransactionError, }, @@ -120,7 +120,7 @@ async fn success_deposit() { &user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] diff --git a/stake-pool/program/tests/deposit_edge_cases.rs b/stake-pool/program/tests/deposit_edge_cases.rs index 13482fd5be1..86ae9c27d56 100644 --- a/stake-pool/program/tests/deposit_edge_cases.rs +++ b/stake-pool/program/tests/deposit_edge_cases.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ mod helpers; use { helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, }, solana_program_test::*, solana_sdk::{ @@ -81,7 +81,7 @@ async fn setup( .await; let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -151,7 +151,7 @@ async fn success_with_preferred_deposit() { &user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -274,7 +274,7 @@ async fn success_with_referral_fee() { let stake_pool = try_from_slice_unchecked::(stake_pool.data.as_slice()).unwrap(); let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let fee_tokens = stake_pool .calc_pool_tokens_sol_deposit_fee(stake_rent) .unwrap() diff --git a/stake-pool/program/tests/deposit_sol.rs b/stake-pool/program/tests/deposit_sol.rs index 5d390f0025e..bc983520700 100644 --- a/stake-pool/program/tests/deposit_sol.rs +++ b/stake-pool/program/tests/deposit_sol.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ mod helpers; use { helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, }, solana_program_test::*, solana_sdk::{ @@ -98,7 +98,7 @@ async fn success(token_program_id: Pubkey) { None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let tokens_issued = TEST_STAKE_AMOUNT; // For now tokens are 1:1 to stake @@ -309,7 +309,7 @@ async fn success_with_sol_deposit_authority() { None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let sol_deposit_authority = Keypair::new(); @@ -336,7 +336,7 @@ async fn success_with_sol_deposit_authority() { Some(&sol_deposit_authority), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -569,7 +569,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { tokens_issued, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Stake pool should add its balance to the pool balance let post_stake_pool = get_account( diff --git a/stake-pool/program/tests/force_destake.rs b/stake-pool/program/tests/force_destake.rs index 1aedc5e8468..f54bd02db35 100644 --- a/stake-pool/program/tests/force_destake.rs +++ b/stake-pool/program/tests/force_destake.rs @@ -1,11 +1,20 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; use { helpers::*, - solana_program::{instruction::InstructionError, pubkey::Pubkey, stake}, + solana_program::{ + borsh0_10::try_from_slice_unchecked, + instruction::InstructionError, + pubkey::Pubkey, + stake::{ + self, + stake_flags::StakeFlags, + state::{Authorized, Delegation, Lockup, Meta, Stake, StakeStateV2}, + }, + }, solana_program_test::*, solana_sdk::{ account::{Account, WritableAccount}, @@ -16,40 +25,29 @@ use { spl_stake_pool::{ error::StakePoolError, find_stake_program_address, find_transient_stake_program_address, id, - state::{StakeStatus, ValidatorStakeInfo}, + state::{AccountType, StakeStatus, ValidatorList, ValidatorListHeader, ValidatorStakeInfo}, MINIMUM_ACTIVE_STAKE, }, std::num::NonZeroU32, }; -async fn setup() -> ( - ProgramTestContext, - StakePoolAccounts, - Pubkey, - Option, -) { +async fn setup( + stake_pool_accounts: &StakePoolAccounts, + forced_stake: &StakeStateV2, + voter_pubkey: &Pubkey, +) -> (ProgramTestContext, Option) { let mut program_test = program_test(); - let stake_pool_accounts = StakePoolAccounts::default(); let stake_pool_pubkey = stake_pool_accounts.stake_pool.pubkey(); let (mut stake_pool, mut validator_list) = stake_pool_accounts.state(); - let voter_pubkey = add_vote_account(&mut program_test); - let meta = stake::state::Meta { - rent_exempt_reserve: STAKE_ACCOUNT_RENT_EXEMPTION, - authorized: stake::state::Authorized { - staker: stake_pool_accounts.withdraw_authority, - withdrawer: stake_pool_accounts.withdraw_authority, - }, - lockup: stake_pool.lockup, - }; + let _ = add_vote_account_with_pubkey(voter_pubkey, &mut program_test); + let mut data = vec![0; std::mem::size_of::()]; + bincode::serialize_into(&mut data[..], forced_stake).unwrap(); let stake_account = Account::create( TEST_STAKE_AMOUNT + STAKE_ACCOUNT_RENT_EXEMPTION, - bincode::serialize::(&stake::state::StakeState::Initialized( - meta, - )) - .unwrap(), + data, stake::program::id(), false, Epoch::default(), @@ -58,19 +56,19 @@ async fn setup() -> ( let raw_validator_seed = 42; let validator_seed = NonZeroU32::new(raw_validator_seed); let (stake_address, _) = - find_stake_program_address(&id(), &voter_pubkey, &stake_pool_pubkey, validator_seed); + find_stake_program_address(&id(), voter_pubkey, &stake_pool_pubkey, validator_seed); program_test.add_account(stake_address, stake_account); let active_stake_lamports = TEST_STAKE_AMOUNT - MINIMUM_ACTIVE_STAKE; // add to validator list validator_list.validators.push(ValidatorStakeInfo { - status: StakeStatus::Active, - vote_account_address: voter_pubkey, - active_stake_lamports, - transient_stake_lamports: 0, - last_update_epoch: 0, - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: raw_validator_seed, + status: StakeStatus::Active.into(), + vote_account_address: *voter_pubkey, + active_stake_lamports: active_stake_lamports.into(), + transient_stake_lamports: 0.into(), + last_update_epoch: 0.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: raw_validator_seed.into(), }); stake_pool.total_lamports += active_stake_lamports; @@ -110,12 +108,27 @@ async fn setup() -> ( ); let context = program_test.start_with_context().await; - (context, stake_pool_accounts, voter_pubkey, validator_seed) + (context, validator_seed) } #[tokio::test] async fn success_update() { - let (mut context, stake_pool_accounts, voter_pubkey, validator_seed) = setup().await; + let stake_pool_accounts = StakePoolAccounts::default(); + let meta = Meta { + rent_exempt_reserve: STAKE_ACCOUNT_RENT_EXEMPTION, + authorized: Authorized { + staker: stake_pool_accounts.withdraw_authority, + withdrawer: stake_pool_accounts.withdraw_authority, + }, + lockup: Lockup::default(), + }; + let voter_pubkey = Pubkey::new_unique(); + let (mut context, validator_seed) = setup( + &stake_pool_accounts, + &StakeStateV2::Initialized(meta), + &voter_pubkey, + ) + .await; let pre_reserve_lamports = context .banks_client .get_account(stake_pool_accounts.reserve_stake.pubkey()) @@ -146,7 +159,7 @@ async fn success_update() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let post_reserve_lamports = context .banks_client .get_account(stake_pool_accounts.reserve_stake.pubkey()) @@ -169,7 +182,22 @@ async fn success_update() { #[tokio::test] async fn fail_increase() { - let (mut context, stake_pool_accounts, voter_pubkey, validator_seed) = setup().await; + let stake_pool_accounts = StakePoolAccounts::default(); + let meta = Meta { + rent_exempt_reserve: STAKE_ACCOUNT_RENT_EXEMPTION, + authorized: Authorized { + staker: stake_pool_accounts.withdraw_authority, + withdrawer: stake_pool_accounts.withdraw_authority, + }, + lockup: Lockup::default(), + }; + let voter_pubkey = Pubkey::new_unique(); + let (mut context, validator_seed) = setup( + &stake_pool_accounts, + &StakeStateV2::Initialized(meta), + &voter_pubkey, + ) + .await; let (stake_address, _) = find_stake_program_address( &id(), &voter_pubkey, @@ -202,7 +230,117 @@ async fn fail_increase() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(StakePoolError::WrongStakeState as u32) + InstructionError::Custom(StakePoolError::WrongStakeStake as u32) ) ); } + +#[tokio::test] +async fn success_remove_validator() { + let stake_pool_accounts = StakePoolAccounts::default(); + let meta = Meta { + rent_exempt_reserve: STAKE_ACCOUNT_RENT_EXEMPTION, + authorized: Authorized { + staker: stake_pool_accounts.withdraw_authority, + withdrawer: stake_pool_accounts.withdraw_authority, + }, + lockup: Lockup::default(), + }; + let voter_pubkey = Pubkey::new_unique(); + let stake = Stake { + delegation: Delegation { + voter_pubkey, + stake: TEST_STAKE_AMOUNT, + activation_epoch: 0, + deactivation_epoch: 0, + ..Delegation::default() + }, + credits_observed: 1, + }; + let (mut context, validator_seed) = setup( + &stake_pool_accounts, + &StakeStateV2::Stake(meta, stake, StakeFlags::empty()), + &voter_pubkey, + ) + .await; + + // move forward to after deactivation + let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; + context.warp_to_slot(first_normal_slot + 1).unwrap(); + stake_pool_accounts + .update_all( + &mut context.banks_client, + &context.payer, + &context.last_blockhash, + &[voter_pubkey], + false, + ) + .await; + + let (stake_address, _) = find_stake_program_address( + &id(), + &voter_pubkey, + &stake_pool_accounts.stake_pool.pubkey(), + validator_seed, + ); + let transient_stake_seed = 0; + let transient_stake_address = find_transient_stake_program_address( + &id(), + &voter_pubkey, + &stake_pool_accounts.stake_pool.pubkey(), + transient_stake_seed, + ) + .0; + + let error = stake_pool_accounts + .remove_validator_from_pool( + &mut context.banks_client, + &context.payer, + &context.last_blockhash, + &stake_address, + &transient_stake_address, + ) + .await; + assert!(error.is_none(), "{:?}", error); + + // Get a new blockhash for the next update to work + context.get_new_latest_blockhash().await.unwrap(); + + let error = stake_pool_accounts + .update_all( + &mut context.banks_client, + &context.payer, + &context.last_blockhash, + &[voter_pubkey], + false, + ) + .await; + assert!(error.is_none(), "{:?}", error); + + // Check if account was removed from the list of stake accounts + let validator_list = get_account( + &mut context.banks_client, + &stake_pool_accounts.validator_list.pubkey(), + ) + .await; + let validator_list = + try_from_slice_unchecked::(validator_list.data.as_slice()).unwrap(); + assert_eq!( + validator_list, + ValidatorList { + header: ValidatorListHeader { + account_type: AccountType::ValidatorList, + max_validators: stake_pool_accounts.max_validators, + }, + validators: vec![] + } + ); + + // Check stake account no longer exists + let account = context + .banks_client + .get_account(stake_address) + .await + .unwrap(); + assert!(account.is_none()); +} diff --git a/stake-pool/program/tests/helpers/mod.rs b/stake-pool/program/tests/helpers/mod.rs index d9145708d90..0bf6c1e18bb 100644 --- a/stake-pool/program/tests/helpers/mod.rs +++ b/stake-pool/program/tests/helpers/mod.rs @@ -3,7 +3,7 @@ use { borsh::{BorshDeserialize, BorshSerialize}, solana_program::{ - borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked}, + borsh0_10::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked}, hash::Hash, instruction::Instruction, program_option::COption, @@ -36,6 +36,7 @@ use { }, spl_token_2022::{ extension::{ExtensionType, StateWithExtensionsOwned}, + native_mint, state::{Account, Mint}, }, std::{convert::TryInto, num::NonZeroU32}, @@ -94,7 +95,7 @@ pub async fn create_mint( ) -> Result<(), TransportError> { assert!(extension_types.is_empty() || program_id != &spl_token::id()); let rent = banks_client.get_rent().await.unwrap(); - let space = ExtensionType::try_get_account_len::(extension_types).unwrap(); + let space = ExtensionType::try_calculate_account_len::(extension_types).unwrap(); let mint_rent = rent.minimum_balance(space); let mint_pubkey = pool_mint.pubkey(); @@ -225,7 +226,7 @@ pub async fn create_token_account( extensions: &[ExtensionType], ) -> Result<(), TransportError> { let rent = banks_client.get_rent().await.unwrap(); - let space = ExtensionType::try_get_account_len::(extensions).unwrap(); + let space = ExtensionType::try_calculate_account_len::(extensions).unwrap(); let account_rent = rent.minimum_balance(space); let mut instructions = vec![system_instruction::create_account( @@ -650,7 +651,7 @@ pub async fn create_independent_stake_account( ) -> u64 { let rent = banks_client.get_rent().await.unwrap(); let lamports = - rent.minimum_balance(std::mem::size_of::()) + stake_amount; + rent.minimum_balance(std::mem::size_of::()) + stake_amount; let transaction = Transaction::new_signed_with_payer( &stake::instruction::create_account( @@ -676,14 +677,14 @@ pub async fn create_blank_stake_account( stake: &Keypair, ) -> u64 { let rent = banks_client.get_rent().await.unwrap(); - let lamports = rent.minimum_balance(std::mem::size_of::()) + 1; + let lamports = rent.minimum_balance(std::mem::size_of::()); let transaction = Transaction::new_signed_with_payer( &[system_instruction::create_account( &payer.pubkey(), &stake.pubkey(), lamports, - std::mem::size_of::() as u64, + std::mem::size_of::() as u64, &stake::program::id(), )], Some(&payer.pubkey()), @@ -1613,7 +1614,7 @@ impl StakePoolAccounts { } #[allow(clippy::too_many_arguments)] - pub async fn decrease_validator_stake( + pub async fn decrease_validator_stake_deprecated( &self, banks_client: &mut BanksClient, payer: &Keypair, @@ -1623,12 +1624,57 @@ impl StakePoolAccounts { lamports: u64, transient_stake_seed: u64, ) -> Option { - let mut instructions = vec![instruction::decrease_validator_stake( + #[allow(deprecated)] + let mut instructions = vec![ + system_instruction::transfer( + &payer.pubkey(), + transient_stake, + STAKE_ACCOUNT_RENT_EXEMPTION, + ), + instruction::decrease_validator_stake( + &id(), + &self.stake_pool.pubkey(), + &self.staker.pubkey(), + &self.withdraw_authority, + &self.validator_list.pubkey(), + validator_stake, + transient_stake, + lamports, + transient_stake_seed, + ), + ]; + self.maybe_add_compute_budget_instruction(&mut instructions); + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &self.staker], + *recent_blockhash, + ); + banks_client + .process_transaction(transaction) + .await + .map_err(|e| e.into()) + .err() + } + + #[allow(clippy::too_many_arguments)] + pub async fn decrease_validator_stake_with_reserve( + &self, + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: &Hash, + validator_stake: &Pubkey, + transient_stake: &Pubkey, + lamports: u64, + transient_stake_seed: u64, + ) -> Option { + let mut instructions = vec![instruction::decrease_validator_stake_with_reserve( &id(), &self.stake_pool.pubkey(), &self.staker.pubkey(), &self.withdraw_authority, &self.validator_list.pubkey(), + &self.reserve_stake.pubkey(), validator_stake, transient_stake, lamports, @@ -1667,6 +1713,7 @@ impl StakePoolAccounts { &self.staker.pubkey(), &self.withdraw_authority, &self.validator_list.pubkey(), + &self.reserve_stake.pubkey(), validator_stake, ephemeral_stake, transient_stake, @@ -1698,39 +1745,56 @@ impl StakePoolAccounts { transient_stake: &Pubkey, lamports: u64, transient_stake_seed: u64, - use_additional_instruction: bool, + instruction_type: DecreaseInstruction, ) -> Option { - if use_additional_instruction { - let ephemeral_stake_seed = 0; - let ephemeral_stake = find_ephemeral_stake_program_address( - &id(), - &self.stake_pool.pubkey(), - ephemeral_stake_seed, - ) - .0; - self.decrease_additional_validator_stake( - banks_client, - payer, - recent_blockhash, - validator_stake, - &ephemeral_stake, - transient_stake, - lamports, - transient_stake_seed, - ephemeral_stake_seed, - ) - .await - } else { - self.decrease_validator_stake( - banks_client, - payer, - recent_blockhash, - validator_stake, - transient_stake, - lamports, - transient_stake_seed, - ) - .await + match instruction_type { + DecreaseInstruction::Additional => { + let ephemeral_stake_seed = 0; + let ephemeral_stake = find_ephemeral_stake_program_address( + &id(), + &self.stake_pool.pubkey(), + ephemeral_stake_seed, + ) + .0; + self.decrease_additional_validator_stake( + banks_client, + payer, + recent_blockhash, + validator_stake, + &ephemeral_stake, + transient_stake, + lamports, + transient_stake_seed, + ephemeral_stake_seed, + ) + .await + } + DecreaseInstruction::Reserve => { + self.decrease_validator_stake_with_reserve( + banks_client, + payer, + recent_blockhash, + validator_stake, + transient_stake, + lamports, + transient_stake_seed, + ) + .await + } + DecreaseInstruction::Deprecated => + { + #[allow(deprecated)] + self.decrease_validator_stake_deprecated( + banks_client, + payer, + recent_blockhash, + validator_stake, + transient_stake, + lamports, + transient_stake_seed, + ) + .await + } } } @@ -1889,6 +1953,7 @@ impl StakePoolAccounts { &self.staker.pubkey(), &self.withdraw_authority, &self.validator_list.pubkey(), + &self.reserve_stake.pubkey(), source_validator_stake, source_transient_stake, ephemeral_stake, @@ -2013,7 +2078,7 @@ impl Default for StakePoolAccounts { token_program_id: spl_token::id(), pool_mint, pool_fee_account, - pool_decimals: 0, + pool_decimals: native_mint::DECIMALS, manager, staker, withdraw_authority, @@ -2057,7 +2122,7 @@ pub async fn simple_add_validator_to_pool( ); let rent = banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation(banks_client, payer, recent_blockhash).await; @@ -2084,7 +2149,7 @@ pub async fn simple_add_validator_to_pool( sol_deposit_authority, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); create_vote( banks_client, @@ -2105,7 +2170,7 @@ pub async fn simple_add_validator_to_pool( validator_stake.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); validator_stake } @@ -2206,7 +2271,7 @@ impl DepositStakeAccount { ) .await; self.pool_tokens = get_token_balance(banks_client, &self.pool_account.pubkey()).await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } } @@ -2315,17 +2380,19 @@ pub async fn get_validator_list_sum( .map(|info| info.stake_lamports().unwrap()) .sum(); let rent = banks_client.get_rent().await.unwrap(); - let rent = rent.minimum_balance(std::mem::size_of::()); + let rent = rent.minimum_balance(std::mem::size_of::()); validator_sum + reserve_stake.lamports - rent - MINIMUM_RESERVE_LAMPORTS } -pub fn add_vote_account(program_test: &mut ProgramTest) -> Pubkey { +pub fn add_vote_account_with_pubkey( + voter_pubkey: &Pubkey, + program_test: &mut ProgramTest, +) -> Pubkey { let authorized_voter = Pubkey::new_unique(); let authorized_withdrawer = Pubkey::new_unique(); let commission = 1; // create vote account - let vote_pubkey = Pubkey::new_unique(); let node_pubkey = Pubkey::new_unique(); let vote_state = VoteStateVersions::new_current(VoteState::new( &VoteInit { @@ -2343,8 +2410,13 @@ pub fn add_vote_account(program_test: &mut ProgramTest) -> Pubkey { false, Epoch::default(), ); - program_test.add_account(vote_pubkey, vote_account); - vote_pubkey + program_test.add_account(*voter_pubkey, vote_account); + *voter_pubkey +} + +pub fn add_vote_account(program_test: &mut ProgramTest) -> Pubkey { + let voter_pubkey = Pubkey::new_unique(); + add_vote_account_with_pubkey(&voter_pubkey, program_test) } #[allow(clippy::too_many_arguments)] @@ -2374,17 +2446,22 @@ pub fn add_validator_stake_account( stake: stake_amount, activation_epoch: FIRST_NORMAL_EPOCH, deactivation_epoch: u64::MAX, - warmup_cooldown_rate: 0.25, // default + ..Default::default() }, credits_observed: 0, }; + let mut data = vec![0u8; std::mem::size_of::()]; + let stake_data = bincode::serialize(&stake::state::StakeStateV2::Stake( + meta, + stake, + stake::stake_flags::StakeFlags::empty(), + )) + .unwrap(); + data[..stake_data.len()].copy_from_slice(&stake_data); let stake_account = SolanaAccount::create( stake_amount + STAKE_ACCOUNT_RENT_EXEMPTION, - bincode::serialize::(&stake::state::StakeState::Stake( - meta, stake, - )) - .unwrap(), + data, stake::program::id(), false, Epoch::default(), @@ -2403,14 +2480,14 @@ pub fn add_validator_stake_account( let active_stake_lamports = stake_amount + STAKE_ACCOUNT_RENT_EXEMPTION; validator_list.validators.push(state::ValidatorStakeInfo { - status, + status: status.into(), vote_account_address: *voter_pubkey, - active_stake_lamports, - transient_stake_lamports: 0, - last_update_epoch: FIRST_NORMAL_EPOCH, - transient_seed_suffix: 0, - unused: 0, - validator_seed_suffix: raw_suffix, + active_stake_lamports: active_stake_lamports.into(), + transient_stake_lamports: 0.into(), + last_update_epoch: FIRST_NORMAL_EPOCH.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), + validator_seed_suffix: raw_suffix.into(), }); stake_pool.total_lamports += active_stake_lamports; @@ -2433,7 +2510,7 @@ pub fn add_reserve_stake_account( }; let reserve_stake_account = SolanaAccount::create( stake_amount + STAKE_ACCOUNT_RENT_EXEMPTION, - bincode::serialize::(&stake::state::StakeState::Initialized( + bincode::serialize::(&stake::state::StakeStateV2::Initialized( meta, )) .unwrap(), @@ -2542,6 +2619,7 @@ pub fn add_token_account( pub async fn setup_for_withdraw( token_program_id: Pubkey, + reserve_lamports: u64, ) -> ( ProgramTestContext, StakePoolAccounts, @@ -2558,7 +2636,7 @@ pub async fn setup_for_withdraw( &mut context.banks_client, &context.payer, &context.last_blockhash, - MINIMUM_RESERVE_LAMPORTS, + reserve_lamports, ) .await .unwrap(); @@ -2626,3 +2704,10 @@ pub async fn setup_for_withdraw( tokens_to_withdraw, ) } + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum DecreaseInstruction { + Additional, + Reserve, + Deprecated, +} diff --git a/stake-pool/program/tests/huge_pool.rs b/stake-pool/program/tests/huge_pool.rs index ad1ac200c0b..ccfa5370b62 100644 --- a/stake-pool/program/tests/huge_pool.rs +++ b/stake-pool/program/tests/huge_pool.rs @@ -1,11 +1,11 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; use { helpers::*, - solana_program::{borsh::try_from_slice_unchecked, pubkey::Pubkey, stake}, + solana_program::{borsh0_10::try_from_slice_unchecked, pubkey::Pubkey, stake}, solana_program_test::*, solana_sdk::{ native_token::LAMPORTS_PER_SOL, @@ -106,8 +106,8 @@ async fn setup( ); let mut context = program_test.start_with_context().await; - let epoch_schedule = context.genesis_config().epoch_schedule; - let slot = epoch_schedule.first_normal_slot + epoch_schedule.slots_per_epoch; + let epoch_schedule = &context.genesis_config().epoch_schedule; + let slot = epoch_schedule.first_normal_slot + epoch_schedule.slots_per_epoch + 1; context.warp_to_slot(slot).unwrap(); let vote_pubkey = vote_account_pubkeys[max_validators as usize - 1]; @@ -184,7 +184,7 @@ async fn update(max_validators: u32) { false, /* no_merge */ ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .update_stake_pool_balance( @@ -193,7 +193,7 @@ async fn update(max_validators: u32) { &context.last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .cleanup_removed_validator_entries( @@ -202,7 +202,7 @@ async fn update(max_validators: u32) { &context.last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } //#[test_case(MAX_POOL_SIZE_WITH_REQUESTED_COMPUTE_UNITS; "compute-budget")] @@ -236,7 +236,7 @@ async fn remove_validator_from_pool(max_validators: u32) { &transient_stake_address, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let middle_index = max_validators as usize / 2; let middle_vote = vote_account_pubkeys[middle_index]; @@ -262,7 +262,7 @@ async fn remove_validator_from_pool(max_validators: u32) { &transient_stake_address, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let last_index = max_validators as usize - 1; let last_vote = vote_account_pubkeys[last_index]; @@ -288,7 +288,7 @@ async fn remove_validator_from_pool(max_validators: u32) { &transient_stake_address, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, @@ -298,28 +298,37 @@ async fn remove_validator_from_pool(max_validators: u32) { let validator_list = try_from_slice_unchecked::(validator_list.data.as_slice()).unwrap(); let first_element = &validator_list.validators[0]; - assert_eq!(first_element.status, StakeStatus::DeactivatingValidator); assert_eq!( - first_element.active_stake_lamports, + first_element.status, + StakeStatus::DeactivatingValidator.into() + ); + assert_eq!( + u64::from(first_element.active_stake_lamports), LAMPORTS_PER_SOL + STAKE_ACCOUNT_RENT_EXEMPTION ); - assert_eq!(first_element.transient_stake_lamports, 0); + assert_eq!(u64::from(first_element.transient_stake_lamports), 0); let middle_element = &validator_list.validators[middle_index]; - assert_eq!(middle_element.status, StakeStatus::DeactivatingValidator); assert_eq!( - middle_element.active_stake_lamports, + middle_element.status, + StakeStatus::DeactivatingValidator.into() + ); + assert_eq!( + u64::from(middle_element.active_stake_lamports), LAMPORTS_PER_SOL + STAKE_ACCOUNT_RENT_EXEMPTION ); - assert_eq!(middle_element.transient_stake_lamports, 0); + assert_eq!(u64::from(middle_element.transient_stake_lamports), 0); let last_element = &validator_list.validators[last_index]; - assert_eq!(last_element.status, StakeStatus::DeactivatingValidator); assert_eq!( - last_element.active_stake_lamports, + last_element.status, + StakeStatus::DeactivatingValidator.into() + ); + assert_eq!( + u64::from(last_element.active_stake_lamports), LAMPORTS_PER_SOL + STAKE_ACCOUNT_RENT_EXEMPTION ); - assert_eq!(last_element.transient_stake_lamports, 0); + assert_eq!(u64::from(last_element.transient_stake_lamports), 0); let error = stake_pool_accounts .update_validator_list_balance( @@ -330,7 +339,7 @@ async fn remove_validator_from_pool(max_validators: u32) { false, /* no_merge */ ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let mut instructions = vec![instruction::update_validator_list_balance( &id(), @@ -355,7 +364,7 @@ async fn remove_validator_from_pool(max_validators: u32) { .process_transaction(transaction) .await .err(); - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let mut instructions = vec![instruction::update_validator_list_balance( &id(), @@ -380,7 +389,7 @@ async fn remove_validator_from_pool(max_validators: u32) { .process_transaction(transaction) .await .err(); - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .cleanup_removed_validator_entries( @@ -389,7 +398,7 @@ async fn remove_validator_from_pool(max_validators: u32) { &context.last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, @@ -454,7 +463,7 @@ async fn add_validator_to_pool(max_validators: u32) { None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, @@ -465,12 +474,12 @@ async fn add_validator_to_pool(max_validators: u32) { try_from_slice_unchecked::(validator_list.data.as_slice()).unwrap(); assert_eq!(validator_list.validators.len(), last_index + 1); let last_element = validator_list.validators[last_index]; - assert_eq!(last_element.status, StakeStatus::Active); + assert_eq!(last_element.status, StakeStatus::Active.into()); assert_eq!( - last_element.active_stake_lamports, + u64::from(last_element.active_stake_lamports), LAMPORTS_PER_SOL + STAKE_ACCOUNT_RENT_EXEMPTION ); - assert_eq!(last_element.transient_stake_lamports, 0); + assert_eq!(u64::from(last_element.transient_stake_lamports), 0); assert_eq!(last_element.vote_account_address, test_vote_address); let transient_stake_seed = u64::MAX; @@ -503,13 +512,13 @@ async fn add_validator_to_pool(max_validators: u32) { let validator_list = try_from_slice_unchecked::(validator_list.data.as_slice()).unwrap(); let last_element = validator_list.validators[last_index]; - assert_eq!(last_element.status, StakeStatus::Active); + assert_eq!(last_element.status, StakeStatus::Active.into()); assert_eq!( - last_element.active_stake_lamports, + u64::from(last_element.active_stake_lamports), LAMPORTS_PER_SOL + STAKE_ACCOUNT_RENT_EXEMPTION ); assert_eq!( - last_element.transient_stake_lamports, + u64::from(last_element.transient_stake_lamports), increase_amount + STAKE_ACCOUNT_RENT_EXEMPTION ); assert_eq!(last_element.vote_account_address, test_vote_address); @@ -531,7 +540,7 @@ async fn set_preferred(max_validators: u32) { Some(vote_account_address), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .set_preferred_validator( &mut context.banks_client, @@ -541,7 +550,7 @@ async fn set_preferred(max_validators: u32) { Some(vote_account_address), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool = get_account( &mut context.banks_client, @@ -585,7 +594,7 @@ async fn deposit_stake(max_validators: u32) { &user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[test_case(MAX_POOL_SIZE_WITH_REQUESTED_COMPUTE_UNITS; "compute-budget")] @@ -613,7 +622,7 @@ async fn withdraw(max_validators: u32) { &user, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Create stake account to withdraw to let user_stake_recipient = Keypair::new(); @@ -635,7 +644,7 @@ async fn withdraw(max_validators: u32) { &pool_account_pubkey, &stake_address, &user.pubkey(), - STAKE_AMOUNT, + TEST_STAKE_AMOUNT, ) .await; assert!(error.is_none(), "{:?}", error); @@ -695,5 +704,5 @@ async fn cleanup_all(max_validators: u32) { &context.last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } diff --git a/stake-pool/program/tests/increase.rs b/stake-pool/program/tests/increase.rs index 4cb8f128f28..bd83885e496 100644 --- a/stake-pool/program/tests/increase.rs +++ b/stake-pool/program/tests/increase.rs @@ -1,4 +1,5 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; @@ -55,7 +56,7 @@ async fn setup() -> ( ) .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let _deposit_info = simple_deposit_stake( &mut context.banks_client, @@ -98,7 +99,7 @@ async fn success(use_additional_instruction: bool) { assert!(transient_account.is_none()); let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let increase_amount = reserve_lamports - stake_rent - MINIMUM_RESERVE_LAMPORTS; let error = stake_pool_accounts .increase_validator_stake_either( @@ -113,7 +114,7 @@ async fn success(use_additional_instruction: bool) { use_additional_instruction, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check reserve stake account balance let reserve_stake_account = get_account( @@ -122,7 +123,7 @@ async fn success(use_additional_instruction: bool) { ) .await; let reserve_stake_state = - deserialize::(&reserve_stake_account.data).unwrap(); + deserialize::(&reserve_stake_account.data).unwrap(); assert_eq!( pre_reserve_stake_account.lamports - increase_amount - stake_rent, reserve_stake_account.lamports @@ -136,7 +137,7 @@ async fn success(use_additional_instruction: bool) { ) .await; let transient_stake_state = - deserialize::(&transient_stake_account.data).unwrap(); + deserialize::(&transient_stake_account.data).unwrap(); assert_eq!( transient_stake_account.lamports, increase_amount + stake_rent @@ -298,7 +299,7 @@ async fn fail_twice_diff_seed(use_additional_instruction: bool) { use_additional_instruction, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let transient_stake_seed = validator_stake.transient_stake_seed * 100; let transient_stake_address = find_transient_stake_program_address( @@ -370,7 +371,7 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se use_additional_first_time, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .increase_validator_stake_either( @@ -387,9 +388,9 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se .await; if success { - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // no ephemeral account let ephemeral_stake = find_ephemeral_stake_program_address( &id(), @@ -410,7 +411,7 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se ) .await; let reserve_stake_state = - deserialize::(&reserve_stake_account.data).unwrap(); + deserialize::(&reserve_stake_account.data).unwrap(); assert_eq!( pre_reserve_stake_account.lamports - total_increase - stake_rent * 2, reserve_stake_account.lamports @@ -424,7 +425,7 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se ) .await; let transient_stake_state = - deserialize::(&transient_stake_account.data).unwrap(); + deserialize::(&transient_stake_account.data).unwrap(); assert_eq!( transient_stake_account.lamports, total_increase + stake_rent * 2 @@ -440,7 +441,7 @@ async fn twice(success: bool, use_additional_first_time: bool, use_additional_se .await; let entry = validator_list.find(&validator_stake.vote.pubkey()).unwrap(); assert_eq!( - entry.transient_stake_lamports, + u64::from(entry.transient_stake_lamports), total_increase + stake_rent * 2 ); } else { @@ -532,11 +533,11 @@ async fn fail_additional_with_decreasing() { ) .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // warp forward to activation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - context.warp_to_slot(first_normal_slot).unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); let last_blockhash = context .banks_client .get_new_latest_blockhash(&context.last_blockhash) @@ -553,7 +554,7 @@ async fn fail_additional_with_decreasing() { .await; let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -561,9 +562,10 @@ async fn fail_additional_with_decreasing() { &validator_stake.transient_stake_account, current_minimum_delegation + stake_rent, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .increase_validator_stake_either( @@ -585,7 +587,7 @@ async fn fail_additional_with_decreasing() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(StakePoolError::WrongStakeState as u32) + InstructionError::Custom(StakePoolError::WrongStakeStake as u32) ) ); } diff --git a/stake-pool/program/tests/initialize.rs b/stake-pool/program/tests/initialize.rs index 750ab08bf11..109d52737bc 100644 --- a/stake-pool/program/tests/initialize.rs +++ b/stake-pool/program/tests/initialize.rs @@ -1,4 +1,5 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +8,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked}, + borsh0_10::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked}, hash::Hash, instruction::{AccountMeta, Instruction}, program_pack::Pack, @@ -448,7 +449,7 @@ async fn fail_with_freeze_authority() { &wrong_mint.pubkey(), &stake_pool_accounts.withdraw_authority, Some(&stake_pool_accounts.withdraw_authority), - 0, + stake_pool_accounts.pool_decimals, ) .unwrap(), ], @@ -1412,7 +1413,7 @@ async fn fail_with_bad_reserve() { error, TransactionError::InstructionError( 2, - InstructionError::Custom(error::StakePoolError::WrongStakeState as u32), + InstructionError::Custom(error::StakePoolError::WrongStakeStake as u32), ) ); } @@ -1464,7 +1465,7 @@ async fn fail_with_bad_reserve() { error, TransactionError::InstructionError( 2, - InstructionError::Custom(error::StakePoolError::WrongStakeState as u32), + InstructionError::Custom(error::StakePoolError::WrongStakeStake as u32), ) ); } @@ -1519,7 +1520,7 @@ async fn fail_with_bad_reserve() { error, TransactionError::InstructionError( 2, - InstructionError::Custom(error::StakePoolError::WrongStakeState as u32), + InstructionError::Custom(error::StakePoolError::WrongStakeStake as u32), ) ); } @@ -1527,7 +1528,7 @@ async fn fail_with_bad_reserve() { { let bad_stake = Keypair::new(); let rent = banks_client.get_rent().await.unwrap(); - let lamports = rent.minimum_balance(std::mem::size_of::()) + let lamports = rent.minimum_balance(std::mem::size_of::()) + MINIMUM_RESERVE_LAMPORTS; let transaction = Transaction::new_signed_with_payer( @@ -1535,7 +1536,7 @@ async fn fail_with_bad_reserve() { &payer.pubkey(), &bad_stake.pubkey(), lamports, - std::mem::size_of::() as u64, + std::mem::size_of::() as u64, &stake::program::id(), )], Some(&payer.pubkey()), @@ -1575,7 +1576,7 @@ async fn fail_with_bad_reserve() { error, TransactionError::InstructionError( 2, - InstructionError::Custom(error::StakePoolError::WrongStakeState as u32), + InstructionError::Custom(error::StakePoolError::WrongStakeStake as u32), ) ); } @@ -1603,3 +1604,30 @@ async fn success_with_extra_reserve_lamports() { .await; assert_eq!(init_pool_tokens, init_lamports); } + +#[tokio::test] +async fn fail_with_incorrect_mint_decimals() { + let (mut banks_client, payer, recent_blockhash) = program_test().start().await; + let stake_pool_accounts = StakePoolAccounts { + pool_decimals: 8, + ..Default::default() + }; + let error = stake_pool_accounts + .initialize_stake_pool( + &mut banks_client, + &payer, + &recent_blockhash, + MINIMUM_RESERVE_LAMPORTS, + ) + .await + .unwrap_err() + .unwrap(); + + assert_eq!( + error, + TransactionError::InstructionError( + 2, + InstructionError::Custom(error::StakePoolError::IncorrectMintDecimals as u32), + ) + ); +} diff --git a/stake-pool/program/tests/redelegate.rs b/stake-pool/program/tests/redelegate.rs index 241918425f2..47dc08a438a 100644 --- a/stake-pool/program/tests/redelegate.rs +++ b/stake-pool/program/tests/redelegate.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -34,7 +34,7 @@ async fn setup( ) { let mut context = program_test().start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -48,7 +48,7 @@ async fn setup( &mut context.banks_client, &context.payer, &context.last_blockhash, - MINIMUM_RESERVE_LAMPORTS + current_minimum_delegation + stake_rent, + MINIMUM_RESERVE_LAMPORTS + current_minimum_delegation + stake_rent * 2, ) .await .unwrap(); @@ -71,7 +71,7 @@ async fn setup( ) .await; - let minimum_redelegate_lamports = current_minimum_delegation + stake_rent * 2; + let minimum_redelegate_lamports = current_minimum_delegation + stake_rent; simple_deposit_stake( &mut context.banks_client, &context.payer, @@ -83,9 +83,9 @@ async fn setup( .await .unwrap(); - let mut slot = 0; + let mut slot = 1; if do_warp { - slot = context.genesis_config().epoch_schedule.first_normal_slot; + slot += context.genesis_config().epoch_schedule.first_normal_slot; context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( @@ -176,7 +176,7 @@ async fn success() { destination_validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check validator stake account balance let validator_stake_account = get_account( @@ -185,7 +185,7 @@ async fn success() { ) .await; let validator_stake_state = - deserialize::(&validator_stake_account.data).unwrap(); + deserialize::(&validator_stake_account.data).unwrap(); assert_eq!( pre_validator_stake_account.lamports - redelegate_lamports, validator_stake_account.lamports @@ -200,7 +200,7 @@ async fn success() { // Check source transient stake account state and balance let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let source_transient_stake_account = get_account( &mut context.banks_client, @@ -208,11 +208,26 @@ async fn success() { ) .await; let transient_stake_state = - deserialize::(&source_transient_stake_account.data).unwrap(); + deserialize::(&source_transient_stake_account.data).unwrap(); assert_eq!(source_transient_stake_account.lamports, stake_rent); let transient_delegation = transient_stake_state.delegation().unwrap(); assert_ne!(transient_delegation.deactivation_epoch, Epoch::MAX); - assert_eq!(transient_delegation.stake, redelegate_lamports - stake_rent); + assert_eq!(transient_delegation.stake, redelegate_lamports); + + // Check reserve stake + let current_minimum_delegation = stake_pool_get_minimum_delegation( + &mut context.banks_client, + &context.payer, + &last_blockhash, + ) + .await; + let reserve_lamports = MINIMUM_RESERVE_LAMPORTS + current_minimum_delegation + stake_rent * 2; + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!(reserve_stake_account.lamports, reserve_lamports); // Check ephemeral account doesn't exist let maybe_account = context @@ -229,18 +244,16 @@ async fn success() { ) .await; let transient_stake_state = - deserialize::(&destination_transient_stake_account.data).unwrap(); + deserialize::(&destination_transient_stake_account.data) + .unwrap(); assert_eq!( destination_transient_stake_account.lamports, - redelegate_lamports - stake_rent + redelegate_lamports ); let transient_delegation = transient_stake_state.delegation().unwrap(); assert_eq!(transient_delegation.deactivation_epoch, Epoch::MAX); assert_ne!(transient_delegation.activation_epoch, Epoch::MAX); - assert_eq!( - transient_delegation.stake, - redelegate_lamports - stake_rent * 2 - ); + assert_eq!(transient_delegation.stake, redelegate_lamports - stake_rent); // Check validator list let validator_list = stake_pool_accounts @@ -250,15 +263,15 @@ async fn success() { .find(&source_validator_stake.vote.pubkey()) .unwrap(); assert_eq!( - source_item.active_stake_lamports, + u64::from(source_item.active_stake_lamports), validator_stake_account.lamports ); assert_eq!( - source_item.transient_stake_lamports, + u64::from(source_item.transient_stake_lamports), source_transient_stake_account.lamports ); assert_eq!( - source_item.transient_seed_suffix, + u64::from(source_item.transient_seed_suffix), source_validator_stake.transient_stake_seed ); @@ -266,11 +279,11 @@ async fn success() { .find(&destination_validator_stake.vote.pubkey()) .unwrap(); assert_eq!( - destination_item.transient_stake_lamports, + u64::from(destination_item.transient_stake_lamports), destination_transient_stake_account.lamports ); assert_eq!( - destination_item.transient_seed_suffix, + u64::from(destination_item.transient_seed_suffix), destination_validator_stake.transient_stake_seed ); @@ -313,18 +326,18 @@ async fn success() { .find(&source_validator_stake.vote.pubkey()) .unwrap(); assert_eq!( - source_item.active_stake_lamports, + u64::from(source_item.active_stake_lamports), validator_stake_account.lamports ); - assert_eq!(source_item.transient_stake_lamports, 0); + assert_eq!(u64::from(source_item.transient_stake_lamports), 0); let destination_item = validator_list .find(&destination_validator_stake.vote.pubkey()) .unwrap(); - assert_eq!(destination_item.transient_stake_lamports, 0); + assert_eq!(u64::from(destination_item.transient_stake_lamports), 0); assert_eq!( - destination_item.active_stake_lamports, - pre_destination_validator_stake_account.lamports + redelegate_lamports - stake_rent * 2 + u64::from(destination_item.active_stake_lamports), + pre_destination_validator_stake_account.lamports + redelegate_lamports - stake_rent ); let post_destination_validator_stake_account = get_account( &mut context.banks_client, @@ -333,7 +346,18 @@ async fn success() { .await; assert_eq!( post_destination_validator_stake_account.lamports, - pre_destination_validator_stake_account.lamports + redelegate_lamports - stake_rent * 2 + pre_destination_validator_stake_account.lamports + redelegate_lamports - stake_rent + ); + + // Check reserve stake, which has claimed back all rent-exempt reserves + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!( + reserve_stake_account.lamports, + reserve_lamports + stake_rent * 2 ); } @@ -363,7 +387,7 @@ async fn success_with_increasing_stake() { ) .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let error = stake_pool_accounts .increase_validator_stake( @@ -377,7 +401,7 @@ async fn success_with_increasing_stake() { destination_validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = stake_pool_accounts .get_validator_list(&mut context.banks_client) @@ -386,7 +410,7 @@ async fn success_with_increasing_stake() { .find(&destination_validator_stake.vote.pubkey()) .unwrap(); assert_eq!( - destination_item.transient_stake_lamports, + u64::from(destination_item.transient_stake_lamports), current_minimum_delegation + stake_rent ); let pre_transient_stake_account = get_account( @@ -467,7 +491,7 @@ async fn success_with_increasing_stake() { destination_validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check destination transient stake account let destination_transient_stake_account = get_account( @@ -476,11 +500,11 @@ async fn success_with_increasing_stake() { ) .await; let transient_stake_state = - deserialize::(&destination_transient_stake_account.data).unwrap(); - // stake rent cancels out + deserialize::(&destination_transient_stake_account.data) + .unwrap(); assert_eq!( destination_transient_stake_account.lamports, - redelegate_lamports + current_minimum_delegation + redelegate_lamports + current_minimum_delegation + stake_rent ); let transient_delegation = transient_stake_state.delegation().unwrap(); @@ -488,9 +512,17 @@ async fn success_with_increasing_stake() { assert_ne!(transient_delegation.activation_epoch, Epoch::MAX); assert_eq!( transient_delegation.stake, - redelegate_lamports + current_minimum_delegation - stake_rent + redelegate_lamports + current_minimum_delegation ); + // Check reserve stake + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!(reserve_stake_account.lamports, stake_rent); + // Check validator list let validator_list = stake_pool_accounts .get_validator_list(&mut context.banks_client) @@ -499,11 +531,11 @@ async fn success_with_increasing_stake() { .find(&destination_validator_stake.vote.pubkey()) .unwrap(); assert_eq!( - destination_item.transient_stake_lamports, + u64::from(destination_item.transient_stake_lamports), destination_transient_stake_account.lamports ); assert_eq!( - destination_item.transient_seed_suffix, + u64::from(destination_item.transient_seed_suffix), destination_validator_stake.transient_stake_seed ); @@ -539,13 +571,12 @@ async fn success_with_increasing_stake() { let destination_item = validator_list .find(&destination_validator_stake.vote.pubkey()) .unwrap(); - assert_eq!(destination_item.transient_stake_lamports, 0); - // redelegate is smart enough to activate *everything*, so there's only one rent-exemption + assert_eq!(u64::from(destination_item.transient_stake_lamports), 0); + // redelegate is smart enough to activate *everything*, so there's no rent-exemption // worth of inactive stake! assert_eq!( - destination_item.active_stake_lamports, + u64::from(destination_item.active_stake_lamports), pre_validator_stake_account.lamports + redelegate_lamports + current_minimum_delegation - - stake_rent ); let post_validator_stake_account = get_account( &mut context.banks_client, @@ -555,8 +586,16 @@ async fn success_with_increasing_stake() { assert_eq!( post_validator_stake_account.lamports, pre_validator_stake_account.lamports + redelegate_lamports + current_minimum_delegation - - stake_rent ); + + // Check reserve has claimed back rent-exempt reserve from both transient + // accounts + let reserve_stake_account = get_account( + &mut context.banks_client, + &stake_pool_accounts.reserve_stake.pubkey(), + ) + .await; + assert_eq!(reserve_stake_account.lamports, stake_rent * 3); } #[tokio::test] @@ -578,7 +617,7 @@ async fn fail_with_decreasing_stake() { ) .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let minimum_decrease_lamports = current_minimum_delegation + stake_rent; simple_deposit_stake( @@ -614,7 +653,7 @@ async fn fail_with_decreasing_stake() { .unwrap(); let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -622,9 +661,10 @@ async fn fail_with_decreasing_stake() { &destination_validator_stake.transient_stake_account, minimum_decrease_lamports, destination_validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let ephemeral_stake_seed = 20; let ephemeral_stake = find_ephemeral_stake_program_address( @@ -657,7 +697,7 @@ async fn fail_with_decreasing_stake() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(StakePoolError::WrongStakeState as u32) + InstructionError::Custom(StakePoolError::WrongStakeStake as u32) ) ); } @@ -690,6 +730,7 @@ async fn fail_with_wrong_withdraw_authority() { &stake_pool_accounts.staker.pubkey(), &wrong_withdraw_authority, &stake_pool_accounts.validator_list.pubkey(), + &stake_pool_accounts.reserve_stake.pubkey(), &source_validator_stake.stake_account, &source_validator_stake.transient_stake_account, &ephemeral_stake, @@ -750,6 +791,7 @@ async fn fail_with_wrong_validator_list() { &stake_pool_accounts.staker.pubkey(), &stake_pool_accounts.withdraw_authority, &wrong_validator_list, + &stake_pool_accounts.reserve_stake.pubkey(), &source_validator_stake.stake_account, &source_validator_stake.transient_stake_account, &ephemeral_stake, @@ -782,6 +824,67 @@ async fn fail_with_wrong_validator_list() { ); } +#[tokio::test] +async fn fail_with_wrong_reserve() { + let ( + mut context, + last_blockhash, + stake_pool_accounts, + source_validator_stake, + destination_validator_stake, + redelegate_lamports, + _, + ) = setup(true).await; + + let ephemeral_stake_seed = 2; + let ephemeral_stake = find_ephemeral_stake_program_address( + &id(), + &stake_pool_accounts.stake_pool.pubkey(), + ephemeral_stake_seed, + ) + .0; + + let wrong_reserve = Keypair::new(); + let transaction = Transaction::new_signed_with_payer( + &[instruction::redelegate( + &id(), + &stake_pool_accounts.stake_pool.pubkey(), + &stake_pool_accounts.staker.pubkey(), + &stake_pool_accounts.withdraw_authority, + &stake_pool_accounts.validator_list.pubkey(), + &wrong_reserve.pubkey(), + &source_validator_stake.stake_account, + &source_validator_stake.transient_stake_account, + &ephemeral_stake, + &destination_validator_stake.transient_stake_account, + &destination_validator_stake.stake_account, + &destination_validator_stake.vote.pubkey(), + redelegate_lamports, + source_validator_stake.transient_stake_seed, + ephemeral_stake_seed, + destination_validator_stake.transient_stake_seed, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &stake_pool_accounts.staker], + last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .err() + .unwrap() + .unwrap(); + + assert_eq!( + error, + TransactionError::InstructionError( + 0, + InstructionError::Custom(StakePoolError::InvalidProgramAddress as u32) + ) + ); +} + #[tokio::test] async fn fail_with_wrong_staker() { let ( @@ -810,6 +913,7 @@ async fn fail_with_wrong_staker() { &wrong_staker.pubkey(), &stake_pool_accounts.withdraw_authority, &stake_pool_accounts.validator_list.pubkey(), + &stake_pool_accounts.reserve_stake.pubkey(), &source_validator_stake.stake_account, &source_validator_stake.transient_stake_account, &ephemeral_stake, @@ -993,7 +1097,7 @@ async fn fail_redelegate_twice() { destination_validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let last_blockhash = context .banks_client diff --git a/stake-pool/program/tests/set_deposit_fee.rs b/stake-pool/program/tests/set_deposit_fee.rs index 85397ffb4c4..6cd6f63decf 100644 --- a/stake-pool/program/tests/set_deposit_fee.rs +++ b/stake-pool/program/tests/set_deposit_fee.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, diff --git a/stake-pool/program/tests/set_epoch_fee.rs b/stake-pool/program/tests/set_epoch_fee.rs index 57d183bdb38..db3b039ae99 100644 --- a/stake-pool/program/tests/set_epoch_fee.rs +++ b/stake-pool/program/tests/set_epoch_fee.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, @@ -80,10 +80,8 @@ async fn success() { let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + let slot = first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -108,9 +106,7 @@ async fn success() { .get_new_latest_blockhash(&context.last_blockhash) .await .unwrap(); - context - .warp_to_slot(first_normal_slot + 2 * slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot + slots_per_epoch).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -220,10 +216,7 @@ async fn fail_not_updated() { // move forward so an update is required let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); let transaction = Transaction::new_signed_with_payer( &[instruction::set_fee( diff --git a/stake-pool/program/tests/set_funding_authority.rs b/stake-pool/program/tests/set_funding_authority.rs index 5fe7b8a959c..e8729691bf3 100644 --- a/stake-pool/program/tests/set_funding_authority.rs +++ b/stake-pool/program/tests/set_funding_authority.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, hash::Hash, instruction::{AccountMeta, Instruction}, }, diff --git a/stake-pool/program/tests/set_manager.rs b/stake-pool/program/tests/set_manager.rs index 0dff30aa1b9..e0163e84202 100644 --- a/stake-pool/program/tests/set_manager.rs +++ b/stake-pool/program/tests/set_manager.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, hash::Hash, instruction::{AccountMeta, Instruction}, }, diff --git a/stake-pool/program/tests/set_preferred.rs b/stake-pool/program/tests/set_preferred.rs index 8c2802772a2..13a93ace967 100644 --- a/stake-pool/program/tests/set_preferred.rs +++ b/stake-pool/program/tests/set_preferred.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -8,7 +8,7 @@ use { solana_program::hash::Hash, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, signature::{Keypair, Signer}, @@ -74,7 +74,7 @@ async fn success_deposit() { Some(vote_account_address), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await; let stake_pool = try_from_slice_unchecked::(stake_pool.data.as_slice()).unwrap(); @@ -102,7 +102,7 @@ async fn success_withdraw() { Some(vote_account_address), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await; let stake_pool = try_from_slice_unchecked::(stake_pool.data.as_slice()).unwrap(); @@ -129,7 +129,7 @@ async fn success_unset() { Some(vote_account_address), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await; let stake_pool = try_from_slice_unchecked::(stake_pool.data.as_slice()).unwrap(); @@ -148,7 +148,7 @@ async fn success_unset() { None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await; let stake_pool = try_from_slice_unchecked::(stake_pool.data.as_slice()).unwrap(); @@ -230,7 +230,7 @@ async fn fail_ready_for_removal() { &stake_pool_accounts.stake_pool.pubkey(), transient_stake_seed, ); - let remove_err = stake_pool_accounts + let error = stake_pool_accounts .remove_validator_from_pool( &mut banks_client, &payer, @@ -239,7 +239,7 @@ async fn fail_ready_for_removal() { &transient_stake_address, ) .await; - assert!(remove_err.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .set_preferred_validator( diff --git a/stake-pool/program/tests/set_referral_fee.rs b/stake-pool/program/tests/set_referral_fee.rs index 829a2fb667c..45d3ced9167 100644 --- a/stake-pool/program/tests/set_referral_fee.rs +++ b/stake-pool/program/tests/set_referral_fee.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, diff --git a/stake-pool/program/tests/set_staker.rs b/stake-pool/program/tests/set_staker.rs index bf30fac6bf9..30d3d4919cf 100644 --- a/stake-pool/program/tests/set_staker.rs +++ b/stake-pool/program/tests/set_staker.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, hash::Hash, instruction::{AccountMeta, Instruction}, }, diff --git a/stake-pool/program/tests/set_withdrawal_fee.rs b/stake-pool/program/tests/set_withdrawal_fee.rs index 6ebf6ea384f..5c548b0adc9 100644 --- a/stake-pool/program/tests/set_withdrawal_fee.rs +++ b/stake-pool/program/tests/set_withdrawal_fee.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { helpers::*, solana_program_test::*, solana_sdk::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, signature::{Keypair, Signer}, transaction::{Transaction, TransactionError}, @@ -109,10 +109,9 @@ async fn success() { let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; + let slot = first_normal_slot + 1; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -145,9 +144,7 @@ async fn success() { .get_new_latest_blockhash(&context.last_blockhash) .await .unwrap(); - context - .warp_to_slot(first_normal_slot + 2 * slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot + slots_per_epoch).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -237,10 +234,9 @@ async fn success_fee_cannot_increase_more_than_once() { let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; + let slot = first_normal_slot + 1; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -273,9 +269,7 @@ async fn success_fee_cannot_increase_more_than_once() { .get_new_latest_blockhash(&context.last_blockhash) .await .unwrap(); - context - .warp_to_slot(first_normal_slot + 2 * slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot + slots_per_epoch).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -356,7 +350,7 @@ async fn success_fee_cannot_increase_more_than_once() { &context.last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check that nothing has changed after updating the stake pool let stake_pool = get_account( @@ -443,11 +437,7 @@ async fn success_reset_fee_after_one_epoch() { ); let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -603,10 +593,8 @@ async fn success_increase_fee_from_0() { let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + let slot = first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -639,9 +627,7 @@ async fn success_increase_fee_from_0() { .get_new_latest_blockhash(&context.last_blockhash) .await .unwrap(); - context - .warp_to_slot(first_normal_slot + 2 * slots_per_epoch) - .unwrap(); + context.warp_to_slot(slot + slots_per_epoch).unwrap(); stake_pool_accounts .update_all( &mut context.banks_client, @@ -902,10 +888,8 @@ async fn fail_not_updated() { // move forward so an update is required let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + let slot = first_normal_slot + 1; + context.warp_to_slot(slot).unwrap(); let transaction = Transaction::new_signed_with_payer( &[instruction::set_fee( diff --git a/stake-pool/program/tests/update_pool_token_metadata.rs b/stake-pool/program/tests/update_pool_token_metadata.rs index cfa5a3b989f..efe30a963dd 100644 --- a/stake-pool/program/tests/update_pool_token_metadata.rs +++ b/stake-pool/program/tests/update_pool_token_metadata.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/stake-pool/program/tests/update_stake_pool_balance.rs b/stake-pool/program/tests/update_stake_pool_balance.rs index b062355009d..50f9c3ee618 100644 --- a/stake-pool/program/tests/update_stake_pool_balance.rs +++ b/stake-pool/program/tests/update_stake_pool_balance.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ mod helpers; use { helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, }, solana_program_test::*, solana_sdk::{ @@ -42,7 +42,7 @@ async fn setup( .unwrap(); let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -60,7 +60,7 @@ async fn setup( None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let mut last_blockhash = context .banks_client @@ -95,7 +95,7 @@ async fn setup( stake_account.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let mut deposit_account = DepositStakeAccount::new_with_vote( stake_account.vote.pubkey(), @@ -165,7 +165,7 @@ async fn success() { } // Update epoch - let slot = context.genesis_config().epoch_schedule.first_normal_slot; + let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1; context.warp_to_slot(slot).unwrap(); let last_blockhash = context @@ -188,7 +188,7 @@ async fn success() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check fee let post_balance = get_validator_list_sum( @@ -279,7 +279,7 @@ async fn success_absorbing_extra_lamports() { let expected_fee = stake_pool.calc_epoch_fee_amount(extra_lamports).unwrap(); // Update epoch - let slot = context.genesis_config().epoch_schedule.first_normal_slot; + let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1; context.warp_to_slot(slot).unwrap(); let last_blockhash = context .banks_client @@ -301,7 +301,7 @@ async fn success_absorbing_extra_lamports() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check extra lamports are absorbed and fee'd as rewards let post_balance = get_validator_list_sum( diff --git a/stake-pool/program/tests/update_validator_list_balance.rs b/stake-pool/program/tests/update_validator_list_balance.rs index 841b8b8849f..bfc6b7e0ef2 100644 --- a/stake-pool/program/tests/update_validator_list_balance.rs +++ b/stake-pool/program/tests/update_validator_list_balance.rs @@ -1,13 +1,13 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; use { helpers::*, - solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey}, + solana_program::{borsh0_10::try_from_slice_unchecked, program_pack::Pack, pubkey::Pubkey}, solana_program_test::*, - solana_sdk::{hash::Hash, signature::Signer, stake::state::StakeState}, + solana_sdk::{hash::Hash, signature::Signer, stake::state::StakeStateV2}, spl_stake_pool::{ state::{StakePool, StakeStatus, ValidatorList}, MAX_VALIDATORS_TO_UPDATE, MINIMUM_RESERVE_LAMPORTS, @@ -31,7 +31,7 @@ async fn setup( let mut context = program_test().start_with_context().await; let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - let mut slot = first_normal_slot; + let mut slot = first_normal_slot + 1; context.warp_to_slot(slot).unwrap(); let reserve_stake_amount = TEST_STAKE_AMOUNT * 2 * num_validators as u64; @@ -74,7 +74,7 @@ async fn setup( stake_account.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let deposit_account = DepositStakeAccount::new_with_vote( stake_account.vote.pubkey(), @@ -183,7 +183,7 @@ async fn success_with_normal() { // Check current balance in the list let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let stake_pool_info = get_account( &mut context.banks_client, &stake_pool_accounts.stake_pool.pubkey(), @@ -279,7 +279,7 @@ async fn merge_into_reserve() { println!("Decrease from all validators"); for stake_account in &stake_accounts { let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -287,9 +287,10 @@ async fn merge_into_reserve() { &stake_account.transient_stake_account, lamports, stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } println!("Update, should not change, no merges yet"); @@ -394,7 +395,7 @@ async fn merge_into_validator_stake() { .await; // Increase stake to all validators - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -417,7 +418,7 @@ async fn merge_into_validator_stake() { stake_account.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } // Warp just a little bit to get a new blockhash and update again @@ -442,7 +443,7 @@ async fn merge_into_validator_stake() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let expected_lamports = get_validator_list_sum( &mut context.banks_client, @@ -482,7 +483,7 @@ async fn merge_into_validator_stake() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let current_lamports = get_validator_list_sum( &mut context.banks_client, &stake_pool_accounts.reserve_stake.pubkey(), @@ -543,7 +544,7 @@ async fn merge_transient_stake_after_remove() { ) = setup(1).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -554,7 +555,7 @@ async fn merge_transient_stake_after_remove() { // Decrease and remove all validators for stake_account in &stake_accounts { let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -562,9 +563,10 @@ async fn merge_transient_stake_after_remove() { &stake_account.transient_stake_account, deactivated_lamports, stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .remove_validator_from_pool( &mut context.banks_client, @@ -574,7 +576,7 @@ async fn merge_transient_stake_after_remove() { &stake_account.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } // Warp forward to merge time @@ -596,7 +598,7 @@ async fn merge_transient_stake_after_remove() { true, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, @@ -608,15 +610,15 @@ async fn merge_transient_stake_after_remove() { assert_eq!(validator_list.validators.len(), 1); assert_eq!( validator_list.validators[0].status, - StakeStatus::DeactivatingAll + StakeStatus::DeactivatingAll.into() ); assert_eq!( - validator_list.validators[0].active_stake_lamports, + u64::from(validator_list.validators[0].active_stake_lamports), stake_rent + current_minimum_delegation ); assert_eq!( - validator_list.validators[0].transient_stake_lamports, - deactivated_lamports + u64::from(validator_list.validators[0].transient_stake_lamports), + deactivated_lamports + stake_rent ); // Update with merge, status should be ReadyForRemoval and no lamports @@ -633,7 +635,7 @@ async fn merge_transient_stake_after_remove() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // stake accounts were merged in, none exist anymore for stake_account in &stake_accounts { @@ -660,7 +662,7 @@ async fn merge_transient_stake_after_remove() { assert_eq!(validator_list.validators.len(), 1); assert_eq!( validator_list.validators[0].status, - StakeStatus::ReadyForRemoval + StakeStatus::ReadyForRemoval.into() ); assert_eq!(validator_list.validators[0].stake_lamports().unwrap(), 0); @@ -679,7 +681,7 @@ async fn merge_transient_stake_after_remove() { let error = stake_pool_accounts .update_stake_pool_balance(&mut context.banks_client, &context.payer, &last_blockhash) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .cleanup_removed_validator_entries( @@ -688,7 +690,7 @@ async fn merge_transient_stake_after_remove() { &last_blockhash, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, diff --git a/stake-pool/program/tests/update_validator_list_balance_hijack.rs b/stake-pool/program/tests/update_validator_list_balance_hijack.rs index 71e8f13cc27..ad444b20e2b 100644 --- a/stake-pool/program/tests/update_validator_list_balance_hijack.rs +++ b/stake-pool/program/tests/update_validator_list_balance_hijack.rs @@ -1,17 +1,17 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; use { helpers::*, - solana_program::{borsh::try_from_slice_unchecked, pubkey::Pubkey, stake}, + solana_program::{borsh0_10::try_from_slice_unchecked, pubkey::Pubkey, stake}, solana_program_test::*, solana_sdk::{ hash::Hash, instruction::InstructionError, signature::Signer, - stake::state::{Authorized, Lockup, StakeState}, + stake::state::{Authorized, Lockup, StakeStateV2}, system_instruction, transaction::{Transaction, TransactionError}, }, @@ -38,7 +38,7 @@ async fn setup( let mut context = program_test().start_with_context().await; let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - let mut slot = first_normal_slot; + let mut slot = first_normal_slot + 1; context.warp_to_slot(slot).unwrap(); let reserve_stake_amount = TEST_STAKE_AMOUNT * 2 * num_validators as u64; @@ -81,7 +81,7 @@ async fn setup( stake_account.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let deposit_account = DepositStakeAccount::new_with_vote( stake_account.vote.pubkey(), @@ -206,7 +206,7 @@ async fn check_ignored_hijacked_transient_stake( ) = setup(num_validators).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let pre_lamports = get_validator_list_sum( &mut context.banks_client, @@ -220,7 +220,7 @@ async fn check_ignored_hijacked_transient_stake( println!("Decrease from all validators"); let stake_account = &stake_accounts[0]; let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -228,9 +228,10 @@ async fn check_ignored_hijacked_transient_stake( &stake_account.transient_stake_account, lamports, stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); println!("Warp one epoch so the stakes deactivate and merge"); let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; @@ -296,7 +297,7 @@ async fn check_ignored_hijacked_transient_stake( .process_transaction(transaction) .await .err(); - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); println!("Update again normally, should be no change in the lamports"); let last_blockhash = context @@ -371,7 +372,7 @@ async fn check_ignored_hijacked_validator_stake( ) = setup(num_validators).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let pre_lamports = get_validator_list_sum( &mut context.banks_client, @@ -384,7 +385,7 @@ async fn check_ignored_hijacked_validator_stake( let stake_account = &stake_accounts[0]; let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -392,9 +393,10 @@ async fn check_ignored_hijacked_validator_stake( &stake_account.transient_stake_account, lamports, stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .remove_validator_from_pool( @@ -405,7 +407,7 @@ async fn check_ignored_hijacked_validator_stake( &stake_account.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); println!("Warp one epoch so the stakes deactivate and merge"); let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; @@ -464,7 +466,7 @@ async fn check_ignored_hijacked_validator_stake( .process_transaction(transaction) .await .err(); - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); println!("Update again normally, should be no change in the lamports"); let last_blockhash = context @@ -542,7 +544,7 @@ async fn check_ignored_hijacked_validator_stake( seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let stake_pool_info = get_account( &mut context.banks_client, diff --git a/stake-pool/program/tests/vsa_add.rs b/stake-pool/program/tests/vsa_add.rs index 9b71900cd04..e32a762309d 100644 --- a/stake-pool/program/tests/vsa_add.rs +++ b/stake-pool/program/tests/vsa_add.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -8,7 +8,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, hash::Hash, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, @@ -37,7 +37,7 @@ async fn setup( ) { let (mut banks_client, payer, recent_blockhash) = program_test().start().await; let rent = banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation(&mut banks_client, &payer, &recent_blockhash).await; let minimum_for_validator = stake_rent + current_minimum_delegation; @@ -88,7 +88,7 @@ async fn success() { validator_stake.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check if validator account was added to the list let validator_list = get_account( @@ -99,7 +99,7 @@ async fn success() { let validator_list = try_from_slice_unchecked::(validator_list.data.as_slice()).unwrap(); let rent = banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation(&mut banks_client, &payer, &recent_blockhash).await; assert_eq!( @@ -110,26 +110,27 @@ async fn success() { max_validators: stake_pool_accounts.max_validators, }, validators: vec![state::ValidatorStakeInfo { - status: state::StakeStatus::Active, + status: state::StakeStatus::Active.into(), vote_account_address: validator_stake.vote.pubkey(), - last_update_epoch: 0, - active_stake_lamports: stake_rent + current_minimum_delegation, - transient_stake_lamports: 0, - transient_seed_suffix: 0, - unused: 0, + last_update_epoch: 0.into(), + active_stake_lamports: (stake_rent + current_minimum_delegation).into(), + transient_stake_lamports: 0.into(), + transient_seed_suffix: 0.into(), + unused: 0.into(), validator_seed_suffix: validator_stake .validator_stake_seed .map(|s| s.get()) - .unwrap_or(0), + .unwrap_or(0) + .into(), }] } ); // Check stake account existence and authority let stake = get_account(&mut banks_client, &validator_stake.stake_account).await; - let stake_state = deserialize::(&stake.data).unwrap(); + let stake_state = deserialize::(&stake.data).unwrap(); match stake_state { - stake::state::StakeState::Stake(meta, _) => { + stake::state::StakeStateV2::Stake(meta, _, _) => { assert_eq!( &meta.authorized.staker, &stake_pool_accounts.withdraw_authority @@ -283,6 +284,7 @@ async fn fail_without_signature() { AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(stake::program::id(), false), @@ -338,6 +340,7 @@ async fn fail_with_wrong_stake_program_id() { AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(wrong_stake_program, false), @@ -391,6 +394,7 @@ async fn fail_with_wrong_system_program_id() { AccountMeta::new_readonly(sysvar::rent::id(), false), AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false), + #[allow(deprecated)] AccountMeta::new_readonly(stake::config::id(), false), AccountMeta::new_readonly(wrong_system_program, false), AccountMeta::new_readonly(stake::program::id(), false), @@ -430,7 +434,7 @@ async fn fail_with_wrong_system_program_id() { async fn fail_add_too_many_validator_stake_accounts() { let (mut banks_client, payer, recent_blockhash) = program_test().start().await; let rent = banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation(&mut banks_client, &payer, &recent_blockhash).await; let minimum_for_validator = stake_rent + current_minimum_delegation; @@ -470,7 +474,7 @@ async fn fail_add_too_many_validator_stake_accounts() { validator_stake.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey(), None, 0); @@ -589,13 +593,13 @@ async fn success_with_lamports_in_account() { validator_stake.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check stake account existence and authority let stake = get_account(&mut banks_client, &validator_stake.stake_account).await; - let stake_state = deserialize::(&stake.data).unwrap(); + let stake_state = deserialize::(&stake.data).unwrap(); match stake_state { - stake::state::StakeState::Stake(meta, _) => { + stake::state::StakeStateV2::Stake(meta, _, _) => { assert_eq!( &meta.authorized.staker, &stake_pool_accounts.withdraw_authority diff --git a/stake-pool/program/tests/vsa_remove.rs b/stake-pool/program/tests/vsa_remove.rs index b2594bb751c..febada59196 100644 --- a/stake-pool/program/tests/vsa_remove.rs +++ b/stake-pool/program/tests/vsa_remove.rs @@ -1,13 +1,14 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; use { + bincode::deserialize, borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, stake, system_instruction, sysvar, @@ -62,7 +63,7 @@ async fn setup() -> (ProgramTestContext, StakePoolAccounts, ValidatorStakeAccoun validator_stake.validator_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); (context, stake_pool_accounts, validator_stake) } @@ -79,7 +80,7 @@ async fn success() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .update_all( @@ -90,7 +91,7 @@ async fn success() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check if account was removed from the list of stake accounts let validator_list = get_account( @@ -243,7 +244,7 @@ async fn success_at_large_value() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -259,7 +260,7 @@ async fn fail_double_remove() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .update_all( @@ -270,7 +271,7 @@ async fn fail_double_remove() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let last_blockhash = context .banks_client @@ -389,7 +390,7 @@ async fn fail_no_signature() { } #[tokio::test] -async fn fail_with_activating_transient_stake() { +async fn success_with_activating_transient_stake() { let (mut context, stake_pool_accounts, validator_stake) = setup().await; // increase the validator stake @@ -405,7 +406,7 @@ async fn fail_with_activating_transient_stake() { validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .remove_validator_from_pool( @@ -415,19 +416,20 @@ async fn fail_with_activating_transient_stake() { &validator_stake.stake_account, &validator_stake.transient_stake_account, ) - .await - .unwrap() - .unwrap(); - match error { - TransactionError::InstructionError( - _, - InstructionError::Custom(error_index), - ) => { - let program_error = StakePoolError::WrongStakeState as u32; - assert_eq!(error_index, program_error); - } - _ => panic!("Wrong error occurs while removing validator stake account while transient stake is activating"), - } + .await; + assert!(error.is_none(), "{:?}", error); + + // transient stake should be inactive now + let stake = get_account( + &mut context.banks_client, + &validator_stake.transient_stake_account, + ) + .await; + let stake_state = deserialize::(&stake.data).unwrap(); + assert_ne!( + stake_state.stake().unwrap().delegation.deactivation_epoch, + u64::MAX + ); } #[tokio::test] @@ -435,7 +437,7 @@ async fn success_with_deactivating_transient_stake() { let (mut context, stake_pool_accounts, validator_stake) = setup().await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -455,7 +457,7 @@ async fn success_with_deactivating_transient_stake() { // increase the validator stake let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -463,9 +465,10 @@ async fn success_with_deactivating_transient_stake() { &validator_stake.transient_stake_account, TEST_STAKE_AMOUNT + stake_rent, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .remove_validator_from_pool( @@ -476,7 +479,7 @@ async fn success_with_deactivating_transient_stake() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // fail deposit let maybe_deposit = simple_deposit_stake( @@ -542,17 +545,18 @@ async fn success_with_deactivating_transient_stake() { max_validators: stake_pool_accounts.max_validators, }, validators: vec![state::ValidatorStakeInfo { - status: state::StakeStatus::DeactivatingAll, + status: state::StakeStatus::DeactivatingAll.into(), vote_account_address: validator_stake.vote.pubkey(), - last_update_epoch: 0, - active_stake_lamports: stake_rent + current_minimum_delegation, - transient_stake_lamports: TEST_STAKE_AMOUNT + stake_rent, - transient_seed_suffix: validator_stake.transient_stake_seed, - unused: 0, + last_update_epoch: 0.into(), + active_stake_lamports: (stake_rent + current_minimum_delegation).into(), + transient_stake_lamports: (TEST_STAKE_AMOUNT + stake_rent * 2).into(), + transient_seed_suffix: validator_stake.transient_stake_seed.into(), + unused: 0.into(), validator_seed_suffix: validator_stake .validator_stake_seed .map(|s| s.get()) - .unwrap_or(0), + .unwrap_or(0) + .into(), }], }; assert_eq!(validator_list, expected_list); @@ -567,7 +571,7 @@ async fn success_with_deactivating_transient_stake() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let validator_list = get_account( &mut context.banks_client, @@ -618,7 +622,7 @@ async fn success_resets_preferred_validator() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let error = stake_pool_accounts .update_all( @@ -629,7 +633,7 @@ async fn success_resets_preferred_validator() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check if account was removed from the list of stake accounts let validator_list = get_account( @@ -663,7 +667,7 @@ async fn success_resets_preferred_validator() { async fn success_with_hijacked_transient_account() { let (mut context, stake_pool_accounts, validator_stake) = setup().await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let current_minimum_delegation = stake_pool_get_minimum_delegation( &mut context.banks_client, &context.payer, @@ -685,12 +689,12 @@ async fn success_with_hijacked_transient_account() { validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to merge let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - let mut slot = first_normal_slot + slots_per_epoch; + let mut slot = first_normal_slot + slots_per_epoch + 1; context.warp_to_slot(slot).unwrap(); stake_pool_accounts .update_all( @@ -704,7 +708,7 @@ async fn success_with_hijacked_transient_account() { // decrease let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -712,9 +716,10 @@ async fn success_with_hijacked_transient_account() { &validator_stake.transient_stake_account, increase_amount, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to merge slot += slots_per_epoch; @@ -783,7 +788,7 @@ async fn success_with_hijacked_transient_account() { .process_transaction(transaction) .await .err(); - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // activate transient stake account delegate_stake_account( @@ -806,7 +811,7 @@ async fn success_with_hijacked_transient_account() { &validator_stake.transient_stake_account, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to merge slot += slots_per_epoch; @@ -821,7 +826,7 @@ async fn success_with_hijacked_transient_account() { false, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check if account was removed from the list of stake accounts let validator_list = get_account( diff --git a/stake-pool/program/tests/withdraw.rs b/stake-pool/program/tests/withdraw.rs index 6ee5bf60435..c0154cbd311 100644 --- a/stake-pool/program/tests/withdraw.rs +++ b/stake-pool/program/tests/withdraw.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,7 +7,7 @@ use { borsh::BorshSerialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, instruction::{AccountMeta, Instruction, InstructionError}, pubkey::Pubkey, sysvar, @@ -49,7 +49,7 @@ async fn _success(token_program_id: Pubkey, test_type: SuccessTestType) { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(token_program_id).await; + ) = setup_for_withdraw(token_program_id, 0).await; // Save stake pool state before withdrawal let stake_pool_before = get_account( @@ -167,7 +167,7 @@ async fn _success(token_program_id: Pubkey, test_type: SuccessTestType) { tokens_to_withdraw, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check pool stats let stake_pool = get_account( @@ -223,7 +223,7 @@ async fn _success(token_program_id: Pubkey, test_type: SuccessTestType) { validator_stake_item_before.stake_lamports().unwrap() - tokens_burnt ); assert_eq!( - validator_stake_item.active_stake_lamports, + u64::from(validator_stake_item.active_stake_lamports), validator_stake_item.stake_lamports().unwrap(), ); @@ -246,7 +246,7 @@ async fn _success(token_program_id: Pubkey, test_type: SuccessTestType) { .await; assert_eq!( validator_stake_account.lamports, - validator_stake_item.active_stake_lamports + u64::from(validator_stake_item.active_stake_lamports) ); // Check user recipient stake account balance @@ -268,7 +268,7 @@ async fn fail_with_wrong_stake_program() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let new_authority = Pubkey::new_unique(); let wrong_stake_program = Pubkey::new_unique(); @@ -328,7 +328,7 @@ async fn fail_with_wrong_withdraw_authority() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let new_authority = Pubkey::new_unique(); stake_pool_accounts.withdraw_authority = Keypair::new().pubkey(); @@ -370,7 +370,7 @@ async fn fail_with_wrong_token_program_id() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let new_authority = Pubkey::new_unique(); let wrong_token_program = Keypair::new(); @@ -421,7 +421,7 @@ async fn fail_with_wrong_validator_list() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let new_authority = Pubkey::new_unique(); stake_pool_accounts.validator_list = Keypair::new(); @@ -465,7 +465,7 @@ async fn fail_with_unknown_validator() { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let unknown_stake = create_unknown_validator_stake( &mut context.banks_client, @@ -512,7 +512,7 @@ async fn fail_double_withdraw_to_the_same_account() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let new_authority = Pubkey::new_unique(); let error = stake_pool_accounts @@ -528,7 +528,7 @@ async fn fail_double_withdraw_to_the_same_account() { tokens_to_burn / 2, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let latest_blockhash = context.banks_client.get_latest_blockhash().await.unwrap(); @@ -578,7 +578,7 @@ async fn fail_without_token_approval() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; revoke_tokens( &mut context.banks_client, @@ -630,7 +630,7 @@ async fn fail_with_not_enough_tokens() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let last_blockhash = context .banks_client @@ -773,7 +773,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(token_program_id).await; + ) = setup_for_withdraw(token_program_id, 0).await; // Save user token balance let user_token_balance_before = get_token_balance( @@ -825,7 +825,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { received_lamports, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check tokens used let user_token_balance = get_token_balance( diff --git a/stake-pool/program/tests/withdraw_edge_cases.rs b/stake-pool/program/tests/withdraw_edge_cases.rs index 27e25d9bfed..4af70cba498 100644 --- a/stake-pool/program/tests/withdraw_edge_cases.rs +++ b/stake-pool/program/tests/withdraw_edge_cases.rs @@ -1,4 +1,5 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] +#![allow(clippy::items_after_test_module)] #![cfg(feature = "test-sbf")] mod helpers; @@ -7,11 +8,11 @@ use { bincode::deserialize, helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, }, solana_program_test::*, solana_sdk::{signature::Signer, transaction::TransactionError}, - spl_stake_pool::{error::StakePoolError, instruction, state, MINIMUM_RESERVE_LAMPORTS}, + spl_stake_pool::{error::StakePoolError, instruction, state}, test_case::test_case, }; @@ -25,11 +26,11 @@ async fn fail_remove_validator() { user_transfer_authority, user_stake_recipient, _, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; // decrease a little stake, not all let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -37,19 +38,17 @@ async fn fail_remove_validator() { &validator_stake.transient_stake_account, deposit_info.stake_lamports / 2, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to deactivation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); // update to merge deactivated stake into reserve - stake_pool_accounts + let error = stake_pool_accounts .update_all( &mut context.banks_client, &context.payer, @@ -58,6 +57,7 @@ async fn fail_remove_validator() { false, ) .await; + assert!(error.is_none(), "{:?}", error); // Withdraw entire account, fail because some stake left let validator_stake_account = @@ -102,7 +102,7 @@ async fn success_remove_validator(multiple: u64) { user_transfer_authority, user_stake_recipient, _, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; // make pool tokens very valuable, so it isn't possible to exactly get down to the minimum transfer( @@ -124,7 +124,7 @@ async fn success_remove_validator(multiple: u64) { .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let stake_pool = stake_pool_accounts .get_stake_pool(&mut context.banks_client) .await; @@ -132,7 +132,7 @@ async fn success_remove_validator(multiple: u64) { // decrease all of stake except for lamports_per_pool_token lamports, must be withdrawable let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -140,16 +140,14 @@ async fn success_remove_validator(multiple: u64) { &validator_stake.transient_stake_account, deposit_info.stake_lamports + stake_rent - lamports_per_pool_token, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to deactivation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); let last_blockhash = context .banks_client @@ -196,7 +194,7 @@ async fn success_remove_validator(multiple: u64) { pool_tokens, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check validator stake account gone let validator_stake_account = context @@ -211,7 +209,7 @@ async fn success_remove_validator(multiple: u64) { get_account(&mut context.banks_client, &user_stake_recipient.pubkey()).await; assert_eq!( user_stake_recipient_account.lamports, - remaining_lamports + stake_rent + 1 + remaining_lamports + stake_rent ); // Check that cleanup happens correctly @@ -244,11 +242,11 @@ async fn fail_with_reserve() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; // decrease a little stake, not all let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -256,16 +254,14 @@ async fn fail_with_reserve() { &validator_stake.transient_stake_account, deposit_info.stake_lamports / 2, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to deactivation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); // update to merge deactivated stake into reserve stake_pool_accounts @@ -314,14 +310,14 @@ async fn success_with_reserve() { user_transfer_authority, user_stake_recipient, _, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // decrease all of stake let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &context.last_blockhash, @@ -329,16 +325,14 @@ async fn success_with_reserve() { &validator_stake.transient_stake_account, deposit_info.stake_lamports + stake_rent, validator_stake.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to deactivation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); // update to merge deactivated stake into reserve stake_pool_accounts @@ -366,7 +360,7 @@ async fn success_with_reserve() { deposit_info.pool_tokens, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // first and only deposit, lamports:pool 1:1 let stake_pool = get_account( @@ -407,10 +401,11 @@ async fn success_with_reserve() { &stake_pool_accounts.reserve_stake.pubkey(), ) .await; - let stake_state = deserialize::(&reserve_stake_account.data).unwrap(); + let stake_state = + deserialize::(&reserve_stake_account.data).unwrap(); let meta = stake_state.meta().unwrap(); assert_eq!( - MINIMUM_RESERVE_LAMPORTS + meta.rent_exempt_reserve + withdrawal_fee + deposit_fee, + meta.rent_exempt_reserve + withdrawal_fee + deposit_fee + stake_rent, reserve_stake_account.lamports ); @@ -419,9 +414,7 @@ async fn success_with_reserve() { get_account(&mut context.banks_client, &user_stake_recipient.pubkey()).await; assert_eq!( user_stake_recipient_account.lamports, - MINIMUM_RESERVE_LAMPORTS + deposit_info.stake_lamports + stake_rent * 2 - - withdrawal_fee - - deposit_fee + deposit_info.stake_lamports + stake_rent * 2 - withdrawal_fee - deposit_fee ); } @@ -435,7 +428,7 @@ async fn success_with_empty_preferred_withdraw() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let preferred_validator = simple_add_validator_to_pool( &mut context.banks_client, @@ -471,7 +464,7 @@ async fn success_with_empty_preferred_withdraw() { tokens_to_burn / 2, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -484,7 +477,7 @@ async fn success_and_fail_with_preferred_withdraw() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let last_blockhash = context .banks_client @@ -567,7 +560,7 @@ async fn success_and_fail_with_preferred_withdraw() { tokens_to_burn / 2, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -580,7 +573,7 @@ async fn fail_withdraw_from_transient() { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; let last_blockhash = context .banks_client @@ -615,11 +608,11 @@ async fn fail_withdraw_from_transient() { .unwrap(); let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); // decrease to minimum stake + 2 lamports let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -627,9 +620,10 @@ async fn fail_withdraw_from_transient() { &validator_stake_account.transient_stake_account, deposit_info.stake_lamports + stake_rent - 2, validator_stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // fail withdrawing from transient, still a lamport in the validator stake account let new_user_authority = Pubkey::new_unique(); @@ -667,7 +661,7 @@ async fn success_withdraw_from_transient() { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), STAKE_ACCOUNT_RENT_EXEMPTION).await; let last_blockhash = context .banks_client @@ -696,7 +690,7 @@ async fn success_withdraw_from_transient() { .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let last_blockhash = context .banks_client @@ -706,7 +700,7 @@ async fn success_withdraw_from_transient() { // decrease all of stake let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -714,9 +708,10 @@ async fn success_withdraw_from_transient() { &validator_stake_account.transient_stake_account, deposit_info.stake_lamports + stake_rent, validator_stake_account.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // nothing left in the validator stake account (or any others), so withdrawing // from the transient account is ok! @@ -734,7 +729,7 @@ async fn success_withdraw_from_transient() { tokens_to_withdraw / 2, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -747,7 +742,7 @@ async fn success_with_small_preferred_withdraw() { user_transfer_authority, user_stake_recipient, tokens_to_burn, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let last_blockhash = context .banks_client @@ -801,7 +796,7 @@ async fn success_with_small_preferred_withdraw() { // add a tiny bit of stake, less than lamports per pool token to preferred validator let rent = context.banks_client.get_rent().await.unwrap(); - let rent_exempt = rent.minimum_balance(std::mem::size_of::()); + let rent_exempt = rent.minimum_balance(std::mem::size_of::()); let stake_minimum_delegation = stake_get_minimum_delegation(&mut context.banks_client, &context.payer, &last_blockhash) .await; @@ -820,7 +815,7 @@ async fn success_with_small_preferred_withdraw() { // decrease all stake except for 1 lamport let error = stake_pool_accounts - .decrease_validator_stake( + .decrease_validator_stake_either( &mut context.banks_client, &context.payer, &last_blockhash, @@ -828,16 +823,14 @@ async fn success_with_small_preferred_withdraw() { &preferred_validator.transient_stake_account, minimum_lamports, preferred_validator.transient_stake_seed, + DecreaseInstruction::Reserve, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // warp forward to deactivation let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; - let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; - context - .warp_to_slot(first_normal_slot + slots_per_epoch) - .unwrap(); + context.warp_to_slot(first_normal_slot + 1).unwrap(); // update to merge deactivated stake into reserve stake_pool_accounts @@ -885,5 +878,5 @@ async fn success_with_small_preferred_withdraw() { tokens_to_burn / 6, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } diff --git a/stake-pool/program/tests/withdraw_sol.rs b/stake-pool/program/tests/withdraw_sol.rs index 8370dd92414..67b60a7a221 100644 --- a/stake-pool/program/tests/withdraw_sol.rs +++ b/stake-pool/program/tests/withdraw_sol.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ mod helpers; use { helpers::*, solana_program::{ - borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, + borsh0_10::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey, stake, }, solana_program_test::*, solana_sdk::{ @@ -65,7 +65,7 @@ async fn setup( None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); let tokens_issued = get_token_balance(&mut context.banks_client, &pool_token_account.pubkey()).await; @@ -114,7 +114,7 @@ async fn success(token_program_id: Pubkey) { None, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Stake pool should add its balance to the pool balance let post_stake_pool = get_account( @@ -199,7 +199,7 @@ async fn fail_overdraw_reserve() { .await; let rent = context.banks_client.get_rent().await.unwrap(); - let stake_rent = rent.minimum_balance(std::mem::size_of::()); + let stake_rent = rent.minimum_balance(std::mem::size_of::()); let error = stake_pool_accounts .increase_validator_stake( &mut context.banks_client, @@ -212,7 +212,7 @@ async fn fail_overdraw_reserve() { validator_stake.transient_stake_seed, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // try to withdraw one lamport, will overdraw let error = stake_pool_accounts @@ -273,7 +273,7 @@ async fn success_with_sol_withdraw_authority() { Some(&sol_withdraw_authority), ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); } #[tokio::test] @@ -373,7 +373,7 @@ async fn success_with_slippage(token_program_id: Pubkey) { amount_received, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check burned tokens let user_token_balance = diff --git a/stake-pool/program/tests/withdraw_with_fee.rs b/stake-pool/program/tests/withdraw_with_fee.rs index 1817ba405f3..b55283fbd1a 100644 --- a/stake-pool/program/tests/withdraw_with_fee.rs +++ b/stake-pool/program/tests/withdraw_with_fee.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; @@ -6,7 +6,7 @@ mod helpers; use { bincode::deserialize, helpers::*, - solana_program::{borsh::try_from_slice_unchecked, pubkey::Pubkey, stake}, + solana_program::{borsh0_10::try_from_slice_unchecked, pubkey::Pubkey, stake}, solana_program_test::*, solana_sdk::signature::{Keypair, Signer}, spl_stake_pool::{minimum_stake_lamports, state}, @@ -22,7 +22,7 @@ async fn success_withdraw_all_fee_tokens() { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let last_blockhash = context .banks_client @@ -78,7 +78,7 @@ async fn success_withdraw_all_fee_tokens() { fee_tokens, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check balance is 0 let fee_tokens = get_token_balance( @@ -99,7 +99,7 @@ async fn success_empty_out_stake_with_fee() { user_transfer_authority, user_stake_recipient, tokens_to_withdraw, - ) = setup_for_withdraw(spl_token::id()).await; + ) = setup_for_withdraw(spl_token::id(), 0).await; let last_blockhash = context .banks_client @@ -176,7 +176,7 @@ async fn success_empty_out_stake_with_fee() { ) .await; let stake_state = - deserialize::(&validator_stake_account.data).unwrap(); + deserialize::(&validator_stake_account.data).unwrap(); let meta = stake_state.meta().unwrap(); let stake_minimum_delegation = stake_get_minimum_delegation(&mut context.banks_client, &context.payer, &last_blockhash) @@ -217,7 +217,7 @@ async fn success_empty_out_stake_with_fee() { pool_tokens_to_withdraw, ) .await; - assert!(error.is_none()); + assert!(error.is_none(), "{:?}", error); // Check balance of validator stake account is MINIMUM + rent-exemption let validator_stake_account = get_account( @@ -226,7 +226,7 @@ async fn success_empty_out_stake_with_fee() { ) .await; let stake_state = - deserialize::(&validator_stake_account.data).unwrap(); + deserialize::(&validator_stake_account.data).unwrap(); let meta = stake_state.meta().unwrap(); assert_eq!( validator_stake_account.lamports, diff --git a/stake-pool/py/requirements.txt b/stake-pool/py/requirements.txt index 6469d7656e6..b14e692089c 100644 --- a/stake-pool/py/requirements.txt +++ b/stake-pool/py/requirements.txt @@ -1,7 +1,7 @@ anyio==3.6.1 base58==2.1.1 cachetools==4.2.4 -certifi==2022.12.7 +certifi==2023.7.22 cffi==1.15.1 charset-normalizer==2.1.0 construct==2.10.68 @@ -16,4 +16,4 @@ rfc3986==1.5.0 sniffio==1.2.0 solana==0.18.1 typing_extensions==4.3.0 -urllib3==1.26.11 +urllib3==1.26.18 diff --git a/stake-pool/py/stake/state.py b/stake-pool/py/stake/state.py index 9ff48d34aec..8d8ac81f128 100644 --- a/stake-pool/py/stake/state.py +++ b/stake-pool/py/stake/state.py @@ -48,7 +48,7 @@ class StakeAuthorize(IntEnum): WITHDRAWER = 1 -class StakeStateType(IntEnum): +class StakeStakeType(IntEnum): """Stake State Types.""" UNINITIALIZED = 0 INITIALIZED = 1 @@ -56,8 +56,8 @@ class StakeStateType(IntEnum): REWARDS_POOL = 3 -class StakeState(NamedTuple): - state_type: StakeStateType +class StakeStake(NamedTuple): + state_type: StakeStakeType state: Container """Stake state.""" @@ -65,7 +65,7 @@ class StakeState(NamedTuple): def decode(cls, data: str, encoding: str): data_bytes = decode_byte_string(data, encoding) parsed = STAKE_STATE_LAYOUT.parse(data_bytes) - return StakeState( + return StakeStake( state_type=parsed['state_type'], state=parsed['state'], ) @@ -122,9 +122,9 @@ def decode(cls, data: str, encoding: str): # Switch( # lambda this: this.state, # { - # StakeStateType.UNINITIALIZED: Pass, - # StakeStateType.INITIALIZED: META_LAYOUT, - # StakeStateType.STAKE: STAKE_AND_META_LAYOUT, + # StakeStakeType.UNINITIALIZED: Pass, + # StakeStakeType.INITIALIZED: META_LAYOUT, + # StakeStakeType.STAKE: STAKE_AND_META_LAYOUT, # } # ), # diff --git a/stake-pool/py/stake_pool/actions.py b/stake-pool/py/stake_pool/actions.py index 6e681a31ebe..a7eff4d9547 100644 --- a/stake-pool/py/stake_pool/actions.py +++ b/stake-pool/py/stake_pool/actions.py @@ -569,17 +569,18 @@ async def decrease_validator_stake( txn = Transaction() txn.add( - sp.decrease_validator_stake( - sp.DecreaseValidatorStakeParams( + sp.decrease_validator_stake_with_reserve( + sp.DecreaseValidatorStakeWithReserveParams( program_id=STAKE_POOL_PROGRAM_ID, stake_pool=stake_pool_address, staker=staker.public_key, withdraw_authority=withdraw_authority, validator_list=stake_pool.validator_list, + reserve_stake=stake_pool.reserve_stake, validator_stake=validator_stake, transient_stake=transient_stake, clock_sysvar=SYSVAR_CLOCK_PUBKEY, - rent_sysvar=SYSVAR_RENT_PUBKEY, + stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY, system_program_id=sys.SYS_PROGRAM_ID, stake_program_id=STAKE_PROGRAM_ID, lamports=lamports, diff --git a/stake-pool/py/stake_pool/constants.py b/stake-pool/py/stake_pool/constants.py index 766a0d2d218..f6c278e2625 100644 --- a/stake-pool/py/stake_pool/constants.py +++ b/stake-pool/py/stake_pool/constants.py @@ -11,7 +11,7 @@ MAX_VALIDATORS_TO_UPDATE: int = 5 """Maximum number of validators to update during UpdateValidatorListBalance.""" -MINIMUM_RESERVE_LAMPORTS: int = 1 +MINIMUM_RESERVE_LAMPORTS: int = 0 """Minimum balance required in the stake pool reserve""" MINIMUM_ACTIVE_STAKE: int = MINIMUM_DELEGATION diff --git a/stake-pool/py/stake_pool/instructions.py b/stake-pool/py/stake_pool/instructions.py index 5152567c94a..4a773a2d7f1 100644 --- a/stake-pool/py/stake_pool/instructions.py +++ b/stake-pool/py/stake_pool/instructions.py @@ -173,6 +173,42 @@ class DecreaseValidatorStakeParams(NamedTuple): """Seed to used to create the transient stake account.""" +class DecreaseValidatorStakeWithReserveParams(NamedTuple): + """(Staker only) Decrease active stake on a validator, eventually moving it to the reserve""" + + # Accounts + program_id: PublicKey + """SPL Stake Pool program account.""" + stake_pool: PublicKey + """`[]` Stake pool.""" + staker: PublicKey + """`[s]` Staker.""" + withdraw_authority: PublicKey + """`[]` Stake pool withdraw authority.""" + validator_list: PublicKey + """`[w]` Validator stake list storage account.""" + reserve_stake: PublicKey + """`[w]` Stake pool's reserve.""" + validator_stake: PublicKey + """`[w]` Canonical stake to split from.""" + transient_stake: PublicKey + """`[w]` Transient stake account to receive split.""" + clock_sysvar: PublicKey + """`[]` Clock sysvar.""" + stake_history_sysvar: PublicKey + """'[]' Stake history sysvar.""" + system_program_id: PublicKey + """`[]` System program.""" + stake_program_id: PublicKey + """`[]` Stake program.""" + + # Params + lamports: int + """Amount of lamports to split into the transient stake account.""" + transient_stake_seed: int + """Seed to used to create the transient stake account.""" + + class IncreaseValidatorStakeParams(NamedTuple): """(Staker only) Increase stake on a validator from the reserve account.""" @@ -529,6 +565,10 @@ class InstructionType(IntEnum): WITHDRAW_SOL = 16 CREATE_TOKEN_METADATA = 17 UPDATE_TOKEN_METADATA = 18 + INCREASE_ADDITIONAL_VALIDATOR_STAKE = 19 + DECREASE_ADDITIONAL_VALIDATOR_STAKE = 20 + DECREASE_VALIDATOR_STAKE_WITH_RESERVE = 21 + REDELEGATE = 22 INITIALIZE_LAYOUT = Struct( @@ -544,6 +584,12 @@ class InstructionType(IntEnum): "transient_stake_seed" / Int64ul, ) +MOVE_STAKE_LAYOUT_WITH_EPHEMERAL_STAKE = Struct( + "lamports" / Int64ul, + "transient_stake_seed" / Int64ul, + "ephemeral_stake_seed" / Int64ul, +) + UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT = Struct( "start_index" / Int32ul, "no_merge" / Int8ul, @@ -588,6 +634,9 @@ class InstructionType(IntEnum): InstructionType.WITHDRAW_SOL: AMOUNT_LAYOUT, InstructionType.CREATE_TOKEN_METADATA: TOKEN_METADATA_LAYOUT, InstructionType.UPDATE_TOKEN_METADATA: TOKEN_METADATA_LAYOUT, + InstructionType.DECREASE_ADDITIONAL_VALIDATOR_STAKE: MOVE_STAKE_LAYOUT_WITH_EPHEMERAL_STAKE, + InstructionType.INCREASE_ADDITIONAL_VALIDATOR_STAKE: MOVE_STAKE_LAYOUT_WITH_EPHEMERAL_STAKE, + InstructionType.DECREASE_VALIDATOR_STAKE_WITH_RESERVE: MOVE_STAKE_LAYOUT, }, ), ) @@ -700,7 +749,7 @@ def remove_validator_from_pool(params: RemoveValidatorFromPoolParams) -> Transac AccountMeta(pubkey=params.withdraw_authority, is_signer=False, is_writable=False), AccountMeta(pubkey=params.validator_list, is_signer=False, is_writable=True), AccountMeta(pubkey=params.validator_stake, is_signer=False, is_writable=True), - AccountMeta(pubkey=params.transient_stake, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.transient_stake, is_signer=False, is_writable=True), AccountMeta(pubkey=params.clock_sysvar, is_signer=False, is_writable=False), AccountMeta(pubkey=params.stake_program_id, is_signer=False, is_writable=False), ], @@ -988,6 +1037,35 @@ def decrease_validator_stake(params: DecreaseValidatorStakeParams) -> Transactio ) +def decrease_validator_stake_with_reserve(params: DecreaseValidatorStakeWithReserveParams) -> TransactionInstruction: + """Creates instruction to decrease the stake on a validator.""" + return TransactionInstruction( + keys=[ + AccountMeta(pubkey=params.stake_pool, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.staker, is_signer=True, is_writable=False), + AccountMeta(pubkey=params.withdraw_authority, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.validator_list, is_signer=False, is_writable=True), + AccountMeta(pubkey=params.reserve_stake, is_signer=False, is_writable=True), + AccountMeta(pubkey=params.validator_stake, is_signer=False, is_writable=True), + AccountMeta(pubkey=params.transient_stake, is_signer=False, is_writable=True), + AccountMeta(pubkey=params.clock_sysvar, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.stake_history_sysvar, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.system_program_id, is_signer=False, is_writable=False), + AccountMeta(pubkey=params.stake_program_id, is_signer=False, is_writable=False), + ], + program_id=params.program_id, + data=INSTRUCTIONS_LAYOUT.build( + dict( + instruction_type=InstructionType.DECREASE_VALIDATOR_STAKE_WITH_RESERVE, + args={ + 'lamports': params.lamports, + 'transient_stake_seed': params.transient_stake_seed + } + ) + ) + ) + + def create_token_metadata(params: CreateTokenMetadataParams) -> TransactionInstruction: """Creates an instruction to create metadata using the mpl token metadata program for the pool token.""" diff --git a/stake-pool/py/tests/test_a_time_sensitive.py b/stake-pool/py/tests/test_a_time_sensitive.py index 4b9164cf933..78abbf47d31 100644 --- a/stake-pool/py/tests/test_a_time_sensitive.py +++ b/stake-pool/py/tests/test_a_time_sensitive.py @@ -64,7 +64,7 @@ async def test_increase_decrease_this_is_very_slow(async_client, validators, pay data = resp['result']['value']['data'] validator_list = ValidatorList.decode(data[0], data[1]) for validator in validator_list.validators: - assert validator.transient_stake_lamports == decrease_amount + assert validator.transient_stake_lamports == decrease_amount + stake_rent_exemption assert validator.active_stake_lamports == increase_amount - decrease_amount + minimum_amount print("Waiting for epoch to roll over") diff --git a/stake-pool/py/tests/test_bot_rebalance.py b/stake-pool/py/tests/test_bot_rebalance.py index 548a2ac7c30..c0283ef5f77 100644 --- a/stake-pool/py/tests/test_bot_rebalance.py +++ b/stake-pool/py/tests/test_bot_rebalance.py @@ -54,10 +54,10 @@ async def test_rebalance_this_is_very_slow(async_client, validators, payer, stak max_in_reserve = total_lamports - minimum_amount * len(validators) await rebalance(ENDPOINT, stake_pool_address, payer, max_in_reserve / LAMPORTS_PER_SOL) - # should still only have minimum left + rent exemptions from increase + # should still only have minimum left resp = await async_client.get_account_info(stake_pool.reserve_stake, commitment=Confirmed) reserve_lamports = resp['result']['value']['lamports'] - assert reserve_lamports == stake_rent_exemption * (1 + len(validator_list.validators)) + MINIMUM_RESERVE_LAMPORTS + assert reserve_lamports == stake_rent_exemption + MINIMUM_RESERVE_LAMPORTS # should all be decreasing now resp = await async_client.get_account_info(validator_list_address, commitment=Confirmed) @@ -65,7 +65,7 @@ async def test_rebalance_this_is_very_slow(async_client, validators, payer, stak validator_list = ValidatorList.decode(data[0], data[1]) for validator in validator_list.validators: assert validator.active_stake_lamports == minimum_amount - assert validator.transient_stake_lamports == max_in_reserve / len(validators) - stake_rent_exemption + assert validator.transient_stake_lamports == max_in_reserve / len(validators) # Test case 3: Do nothing print('Waiting for next epoch') diff --git a/stake-pool/py/tests/test_deposit_withdraw_stake.py b/stake-pool/py/tests/test_deposit_withdraw_stake.py index 1580db246a2..890fb9d1f4d 100644 --- a/stake-pool/py/tests/test_deposit_withdraw_stake.py +++ b/stake-pool/py/tests/test_deposit_withdraw_stake.py @@ -5,7 +5,7 @@ from stake.actions import create_stake, delegate_stake from stake.constants import STAKE_LEN -from stake.state import StakeState +from stake.state import StakeStake from stake_pool.actions import deposit_stake, withdraw_stake, update_stake_pool from stake_pool.constants import MINIMUM_ACTIVE_STAKE from stake_pool.state import StakePool @@ -25,7 +25,7 @@ async def test_deposit_withdraw_stake(async_client, validators, payer, stake_poo await delegate_stake(async_client, payer, payer, stake, validator) resp = await async_client.get_account_info(stake, commitment=Confirmed) data = resp['result']['value']['data'] - stake_state = StakeState.decode(data[0], data[1]) + stake_state = StakeStake.decode(data[0], data[1]) token_account = get_associated_token_address(payer.public_key, stake_pool.pool_mint) pre_pool_token_balance = await async_client.get_token_account_balance(token_account, Confirmed) pre_pool_token_balance = int(pre_pool_token_balance['result']['value']['amount']) diff --git a/stake-pool/single-pool/program-id.md b/stake-pool/single-pool/program-id.md deleted file mode 100644 index 144e4641c47..00000000000 --- a/stake-pool/single-pool/program-id.md +++ /dev/null @@ -1 +0,0 @@ -3cqnsMsT6LE96pxv7GR4di5rLqHDZZbR3FbeSUeRLFqY diff --git a/stake-pool/single-pool/src/inline_mpl_token_metadata.rs b/stake-pool/single-pool/src/inline_mpl_token_metadata.rs deleted file mode 120000 index 1cab03a2449..00000000000 --- a/stake-pool/single-pool/src/inline_mpl_token_metadata.rs +++ /dev/null @@ -1 +0,0 @@ -../../program/src/inline_mpl_token_metadata.rs \ No newline at end of file diff --git a/stake-pool/single-pool/tests/fixtures/mpl_token_metadata.so b/stake-pool/single-pool/tests/fixtures/mpl_token_metadata.so deleted file mode 120000 index 2b5fa0e4533..00000000000 --- a/stake-pool/single-pool/tests/fixtures/mpl_token_metadata.so +++ /dev/null @@ -1 +0,0 @@ -../../../program/tests/fixtures/mpl_token_metadata.so \ No newline at end of file diff --git a/stateless-asks/program/Cargo.toml b/stateless-asks/program/Cargo.toml index 50e2cbc3cf4..f7104df519c 100644 --- a/stateless-asks/program/Cargo.toml +++ b/stateless-asks/program/Cargo.toml @@ -12,14 +12,14 @@ test-sbf = [] [dependencies] borsh = "0.10" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } spl-associated-token-account = {version = "2.0", path = "../../associated-token-account/program", features = ["no-entrypoint"]} thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/stateless-asks/program/src/processor.rs b/stateless-asks/program/src/processor.rs index 5f0a81e4a09..374a060505c 100644 --- a/stateless-asks/program/src/processor.rs +++ b/stateless-asks/program/src/processor.rs @@ -10,7 +10,7 @@ use { solana_program::{ account_info::next_account_info, account_info::AccountInfo, - borsh::try_from_slice_unchecked, + borsh0_10::try_from_slice_unchecked, entrypoint::ProgramResult, msg, program::{invoke, invoke_signed}, diff --git a/token-collection/README.md b/token-collection/README.md new file mode 100644 index 00000000000..0b311156f35 --- /dev/null +++ b/token-collection/README.md @@ -0,0 +1,24 @@ +# SPL Token Collection + +This program serves as a reference implementation for using the SPL Token Group +interface to create an on-chain program for managing token collections - such +as NFT Collections. + +This program bears a lot of similarity to the example program found at +`token-group/example`, but with some additional implementations centered around +specifically token collections. + +## How Collections Work in this Program + +Strictly for demonstration purposes, this program is going to require the +following: + +- Group tokens must be NFTs (0 decimals, 1 supply) +- Group tokens must have metadata +- Member tokens can be any SPL token, but must have metadata +- Member tokens can be part of multiple collections + +## Demonstration + +For a particularly fleshed-out example of this program in action, check out the +`token-collections.rs` test under `tests`! \ No newline at end of file diff --git a/token-collection/program/Cargo.toml b/token-collection/program/Cargo.toml new file mode 100644 index 00000000000..17dc059c9f8 --- /dev/null +++ b/token-collection/program/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "spl-token-collection" +version = "0.1.0" +description = "Solana Program Library Token Collection" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[features] +no-entrypoint = [] +test-sbf = [] + +[dependencies] +solana-program = "1.17.2" +spl-pod = { version = "0.1.0", path = "../../libraries/pod" } +spl-program-error = { version = "0.3.0" , path = "../../libraries/program-error" } +spl-token-2022 = { version = "0.9.0", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-group-example = { version = "0.1.0", path = "../../token-group/example", features = ["no-entrypoint"] } +spl-token-group-interface = { version = "0.1.0", path = "../../token-group/interface" } +spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" } +spl-type-length-value = { version = "0.3.0", path = "../../libraries/type-length-value" } + +[dev-dependencies] +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +spl-discriminator = { version = "0.1.0", path = "../../libraries/discriminator" } +spl-token-client = { version = "0.8", path = "../../token/client" } + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/token-collection/program/src/entrypoint.rs b/token-collection/program/src/entrypoint.rs new file mode 100644 index 00000000000..762ec75255a --- /dev/null +++ b/token-collection/program/src/entrypoint.rs @@ -0,0 +1,23 @@ +//! Program entrypoint + +use { + crate::processor, + solana_program::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, + program_error::PrintProgramError, pubkey::Pubkey, + }, + spl_token_group_interface::error::TokenGroupError, +}; + +entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + if let Err(error) = processor::process(program_id, accounts, instruction_data) { + error.print::(); + return Err(error); + } + Ok(()) +} diff --git a/token-collection/program/src/lib.rs b/token-collection/program/src/lib.rs new file mode 100644 index 00000000000..6e539b2fd80 --- /dev/null +++ b/token-collection/program/src/lib.rs @@ -0,0 +1,10 @@ +//! Crate defining the Token Collection program implementing the +//! SPL Token Group interface. + +#![deny(missing_docs)] +#![forbid(unsafe_code)] + +pub mod processor; + +#[cfg(not(feature = "no-entrypoint"))] +mod entrypoint; diff --git a/token-collection/program/src/processor.rs b/token-collection/program/src/processor.rs new file mode 100644 index 00000000000..c5153a03771 --- /dev/null +++ b/token-collection/program/src/processor.rs @@ -0,0 +1,162 @@ +//! Program state processor + +use { + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + program_error::ProgramError, + program_option::COption, + pubkey::Pubkey, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, + spl_token_2022::{ + extension::{ + metadata_pointer::MetadataPointer, BaseStateWithExtensions, StateWithExtensions, + }, + state::Mint, + }, + spl_token_group_interface::{ + error::TokenGroupError, + instruction::{InitializeGroup, TokenGroupInstruction}, + state::{TokenGroup, TokenGroupMember}, + }, + spl_token_metadata_interface::state::TokenMetadata, + spl_type_length_value::state::TlvStateMut, +}; + +fn check_update_authority( + update_authority_info: &AccountInfo, + expected_update_authority: &OptionalNonZeroPubkey, +) -> ProgramResult { + if !update_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + let update_authority = Option::::from(*expected_update_authority) + .ok_or(TokenGroupError::ImmutableGroup)?; + if update_authority != *update_authority_info.key { + return Err(TokenGroupError::IncorrectUpdateAuthority.into()); + } + Ok(()) +} + +/// Checks that a mint is valid and contains metadata. +fn check_mint_and_metadata( + mint_info: &AccountInfo, + mint_authority_info: &AccountInfo, +) -> ProgramResult { + let mint_data = mint_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + if !mint_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if mint.base.mint_authority.as_ref() != COption::Some(mint_authority_info.key) { + return Err(TokenGroupError::IncorrectMintAuthority.into()); + } + + let metadata_pointer = mint.get_extension::()?; + let metadata_pointer_address = Option::::from(metadata_pointer.metadata_address); + + // If the metadata is inside the mint (Token2022), make sure it contains + // valid TokenMetadata + if metadata_pointer_address == Some(*mint_info.key) { + mint.get_variable_len_extension::()?; + } + + Ok(()) +} + +/// Processes an [InitializeGroup](enum.GroupInterfaceInstruction.html) +/// instruction to initialize a collection. +pub fn process_initialize_collection( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: InitializeGroup, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let collection_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let mint_authority_info = next_account_info(account_info_iter)?; + + check_mint_and_metadata(mint_info, mint_authority_info)?; + + // Initialize the collection + let mut buffer = collection_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let (collection, _) = state.init_value::(false)?; + *collection = TokenGroup::new(mint_info.key, data.update_authority, data.max_size.into()); + + Ok(()) +} + +/// Processes an [InitializeMember](enum.GroupInterfaceInstruction.html) +/// instruction +pub fn process_initialize_collection_member( + _program_id: &Pubkey, + accounts: &[AccountInfo], +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let member_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let mint_authority_info = next_account_info(account_info_iter)?; + let collection_info = next_account_info(account_info_iter)?; + let collection_update_authority_info = next_account_info(account_info_iter)?; + + check_mint_and_metadata(mint_info, mint_authority_info)?; + + if member_info.key == collection_info.key { + return Err(TokenGroupError::MemberAccountIsGroupAccount.into()); + } + + let mut buffer = collection_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let collection = state.get_first_value_mut::()?; + + check_update_authority( + collection_update_authority_info, + &collection.update_authority, + )?; + let member_number = collection.increment_size()?; + + let mut buffer = member_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + + // This program uses `allow_repetition: true` because the same mint can be + // a member of multiple collections. + let (member, _) = state.init_value::(/* allow_repetition */ true)?; + *member = TokenGroupMember::new(mint_info.key, collection_info.key, member_number); + + Ok(()) +} + +/// Processes an `SplTokenGroupInstruction` +pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = TokenGroupInstruction::unpack(input)?; + match instruction { + TokenGroupInstruction::InitializeGroup(data) => { + msg!("Instruction: InitializeCollection"); + process_initialize_collection(program_id, accounts, data) + } + TokenGroupInstruction::UpdateGroupMaxSize(data) => { + msg!("Instruction: UpdateCollectionMaxSize"); + // Same functionality as the example program + spl_token_group_example::processor::process_update_group_max_size( + program_id, accounts, data, + ) + } + TokenGroupInstruction::UpdateGroupAuthority(data) => { + msg!("Instruction: UpdateCollectionAuthority"); + // Same functionality as the example program + spl_token_group_example::processor::process_update_group_authority( + program_id, accounts, data, + ) + } + TokenGroupInstruction::InitializeMember(_) => { + msg!("Instruction: InitializeCollectionMember"); + process_initialize_collection_member(program_id, accounts) + } + } +} diff --git a/token-collection/program/tests/setup.rs b/token-collection/program/tests/setup.rs new file mode 100644 index 00000000000..709f6f92943 --- /dev/null +++ b/token-collection/program/tests/setup.rs @@ -0,0 +1,123 @@ +#![cfg(feature = "test-sbf")] + +use { + solana_program::system_instruction, + solana_program_test::{processor, tokio::sync::Mutex, ProgramTest, ProgramTestContext}, + solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction}, + spl_token_client::{ + client::{ + ProgramBanksClient, ProgramBanksClientProcessTransaction, ProgramClient, + SendTransaction, SimulateTransaction, + }, + token::{ExtensionInitializationParams, Token}, + }, + spl_token_group_interface::instruction::initialize_group, + spl_token_metadata_interface::state::TokenMetadata, + std::sync::Arc, +}; + +/// Set up a program test +pub async fn setup_program_test( + program_id: &Pubkey, +) -> ( + Arc>, + Arc>, + Arc, +) { + let mut program_test = ProgramTest::new( + "spl_token_collection", + *program_id, + processor!(spl_token_collection::processor::process), + ); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(spl_token_2022::processor::Processor::process), + ); + let context = program_test.start_with_context().await; + let payer = Arc::new(context.payer.insecure_clone()); + let context = Arc::new(Mutex::new(context)); + let client: Arc> = + Arc::new(ProgramBanksClient::new_from_context( + Arc::clone(&context), + ProgramBanksClientProcessTransaction, + )); + (context, client, payer) +} + +/// Set up a Token-2022 mint and metadata +pub async fn setup_mint_and_metadata( + token_client: &Token, + mint_keypair: &Keypair, + mint_authority_keypair: &Keypair, + token_metadata: &TokenMetadata, + payer: Arc, +) { + token_client + .create_mint( + &mint_authority_keypair.pubkey(), + None, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(mint_authority_keypair.pubkey()), + metadata_address: Some(mint_keypair.pubkey()), + }], + &[mint_keypair], + ) + .await + .unwrap(); + token_client + .token_metadata_initialize_with_rent_transfer( + &payer.pubkey(), + &mint_authority_keypair.pubkey(), + &mint_authority_keypair.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&payer, mint_authority_keypair], + ) + .await + .unwrap(); +} + +/// Initialize a token group +#[allow(clippy::too_many_arguments)] +pub async fn setup_group( + context: &mut ProgramTestContext, + program_id: &Pubkey, + group: &Keypair, + mint: &Keypair, + mint_authority: &Keypair, + update_authority: Option, + max_size: u32, + rent_lamports: u64, + space: usize, +) { + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + program_id, + ), + initialize_group( + program_id, + &group.pubkey(), + &mint.pubkey(), + &mint_authority.pubkey(), + update_authority, + max_size, + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, mint_authority, group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); +} diff --git a/token-collection/program/tests/token_collection.rs b/token-collection/program/tests/token_collection.rs new file mode 100644 index 00000000000..aeff8994ef8 --- /dev/null +++ b/token-collection/program/tests/token_collection.rs @@ -0,0 +1,389 @@ +#![cfg(feature = "test-sbf")] + +mod setup; + +use { + setup::{setup_group, setup_mint_and_metadata, setup_program_test}, + solana_program::{pubkey::Pubkey, system_instruction}, + solana_program_test::tokio, + solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction}, + spl_token_client::token::Token, + spl_token_group_interface::{ + instruction::initialize_member, + state::{TokenGroup, TokenGroupMember}, + }, + spl_token_metadata_interface::state::TokenMetadata, + spl_type_length_value::state::{TlvState, TlvStateBorrowed}, +}; + +/// All snakes are reptiles, but not all reptiles are snakes. +#[tokio::test] +async fn test_token_collection() { + let program_id = Pubkey::new_unique(); + let (context, client, payer) = setup_program_test(&program_id).await; + + // Set up the "Reptiles" collection mint and metadata + let reptile = Keypair::new(); + let reptile_mint = Keypair::new(); + let reptile_mint_authority = Keypair::new(); + let reptile_update_authority = Keypair::new(); + let reptile_metadata_state = TokenMetadata { + name: "Reptiles".to_string(), + symbol: "RPTL".to_string(), + ..TokenMetadata::default() + }; + setup_mint_and_metadata( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &reptile_mint.pubkey(), + Some(0), + payer.clone(), + ), + &reptile_mint, + &reptile_mint_authority, + &reptile_metadata_state, + payer.clone(), + ) + .await; + + // Set up the "Snakes" collection mint and metadata + let snake = Keypair::new(); + let snake_mint = Keypair::new(); + let snake_mint_authority = Keypair::new(); + let snake_update_authority = Keypair::new(); + let snake_metadata_state = TokenMetadata { + name: "Snakes".to_string(), + symbol: "SNKE".to_string(), + ..TokenMetadata::default() + }; + setup_mint_and_metadata( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &snake_mint.pubkey(), + Some(0), + payer.clone(), + ), + &snake_mint, + &snake_mint_authority, + &snake_metadata_state, + payer.clone(), + ) + .await; + + // Set up the "Python" mint and metadata + let python = Keypair::new(); + let python_mint = Keypair::new(); + let python_mint_authority = Keypair::new(); + let python_metadata_state = TokenMetadata { + name: "Python".to_string(), + symbol: "PYTH".to_string(), + ..TokenMetadata::default() + }; + setup_mint_and_metadata( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &python_mint.pubkey(), + Some(0), + payer.clone(), + ), + &python_mint, + &python_mint_authority, + &python_metadata_state, + payer.clone(), + ) + .await; + + // Set up the "Cobra" mint and metadata + let cobra = Keypair::new(); + let cobra_mint = Keypair::new(); + let cobra_mint_authority = Keypair::new(); + let cobra_metadata_state = TokenMetadata { + name: "Cobra".to_string(), + symbol: "CBRA".to_string(), + ..TokenMetadata::default() + }; + setup_mint_and_metadata( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &cobra_mint.pubkey(), + Some(0), + payer.clone(), + ), + &cobra_mint, + &cobra_mint_authority, + &cobra_metadata_state, + payer.clone(), + ) + .await; + + // Set up the "Iguana" mint and metadata + let iguana = Keypair::new(); + let iguana_mint = Keypair::new(); + let iguana_mint_authority = Keypair::new(); + let iguana_metadata_state = TokenMetadata { + name: "Iguana".to_string(), + symbol: "IGUA".to_string(), + ..TokenMetadata::default() + }; + setup_mint_and_metadata( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &iguana_mint.pubkey(), + Some(0), + payer.clone(), + ), + &iguana_mint, + &iguana_mint_authority, + &iguana_metadata_state, + payer.clone(), + ) + .await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let collection_space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let collection_rent_lamports = rent.minimum_balance(collection_space); + let member_space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let member_rent_lamports = rent.minimum_balance(member_space); + + // Create the collections using the SPL Token Collection program + setup_group( + &mut context, + &program_id, + &reptile, + &reptile_mint, + &reptile_mint_authority, + Some(reptile_update_authority.pubkey()), + 3, + collection_rent_lamports, + collection_space, + ) + .await; + setup_group( + &mut context, + &program_id, + &snake, + &snake_mint, + &snake_mint_authority, + Some(snake_update_authority.pubkey()), + 2, + collection_rent_lamports, + collection_space, + ) + .await; + + // Create the member accounts ahead of time + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &python.pubkey(), + member_rent_lamports.checked_mul(2).unwrap(), // 2 collections + u64::try_from(member_space).unwrap().checked_mul(2).unwrap(), // 2 collections + &program_id, + ), + system_instruction::create_account( + &context.payer.pubkey(), + &cobra.pubkey(), + member_rent_lamports.checked_mul(2).unwrap(), // 2 collections + u64::try_from(member_space).unwrap().checked_mul(2).unwrap(), // 2 collections + &program_id, + ), + system_instruction::create_account( + &context.payer.pubkey(), + &iguana.pubkey(), + member_rent_lamports, + member_space.try_into().unwrap(), + &program_id, + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &python, &cobra, &iguana], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // A python is both a reptile and a snake! + let transaction = Transaction::new_signed_with_payer( + &[ + initialize_member( + &program_id, + &python.pubkey(), + &python_mint.pubkey(), + &python_mint_authority.pubkey(), + &reptile.pubkey(), + &reptile_update_authority.pubkey(), + ), + initialize_member( + &program_id, + &python.pubkey(), + &python_mint.pubkey(), + &python_mint_authority.pubkey(), + &snake.pubkey(), + &snake_update_authority.pubkey(), + ), + ], + Some(&context.payer.pubkey()), + &[ + &context.payer, + &python_mint_authority, + &reptile_update_authority, + &snake_update_authority, + ], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // A cobra is both a reptile and a snake! + let transaction = Transaction::new_signed_with_payer( + &[ + initialize_member( + &program_id, + &cobra.pubkey(), + &cobra_mint.pubkey(), + &cobra_mint_authority.pubkey(), + &reptile.pubkey(), + &reptile_update_authority.pubkey(), + ), + initialize_member( + &program_id, + &cobra.pubkey(), + &cobra_mint.pubkey(), + &cobra_mint_authority.pubkey(), + &snake.pubkey(), + &snake_update_authority.pubkey(), + ), + ], + Some(&context.payer.pubkey()), + &[ + &context.payer, + &cobra_mint_authority, + &reptile_update_authority, + &snake_update_authority, + ], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // An iguana is a reptile but not a snake! + let mut transaction = Transaction::new_signed_with_payer( + &[initialize_member( + &program_id, + &iguana.pubkey(), + &iguana_mint.pubkey(), + &iguana_mint_authority.pubkey(), + &reptile.pubkey(), + &reptile_update_authority.pubkey(), + )], + Some(&context.payer.pubkey()), + &[ + &context.payer, + &iguana_mint_authority, + &reptile_update_authority, + ], + context.last_blockhash, + ); + transaction.sign( + &[ + &context.payer, + &iguana_mint_authority, + &reptile_update_authority, + ], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // The "Reptiles" collection should have 3 members + let buffer = context + .banks_client + .get_account(reptile.pubkey()) + .await + .unwrap() + .unwrap() + .data; + let state = TlvStateBorrowed::unpack(&buffer).unwrap(); + let collection = state.get_first_value::().unwrap(); + assert_eq!(u32::from(collection.size), 3); + + // The "Snakes" collection should have 2 members + let buffer = context + .banks_client + .get_account(snake.pubkey()) + .await + .unwrap() + .unwrap() + .data; + let state = TlvStateBorrowed::unpack(&buffer).unwrap(); + let collection = state.get_first_value::().unwrap(); + assert_eq!(u32::from(collection.size), 2); + + // The "Python" should be a member of 2 collections + let buffer = context + .banks_client + .get_account(python.pubkey()) + .await + .unwrap() + .unwrap() + .data; + let state = TlvStateBorrowed::unpack(&buffer).unwrap(); + let membership = state + .get_value_with_repetition::(0) + .unwrap(); + assert_eq!(membership.group, reptile.pubkey(),); + let membership = state + .get_value_with_repetition::(1) + .unwrap(); + assert_eq!(membership.group, snake.pubkey(),); + + // The "Cobra" should be a member of 2 collections + let buffer = context + .banks_client + .get_account(cobra.pubkey()) + .await + .unwrap() + .unwrap() + .data; + let state = TlvStateBorrowed::unpack(&buffer).unwrap(); + let membership = state + .get_value_with_repetition::(0) + .unwrap(); + assert_eq!(membership.group, reptile.pubkey(),); + let membership = state + .get_value_with_repetition::(1) + .unwrap(); + assert_eq!(membership.group, snake.pubkey(),); + + // The "Iguana" should be a member of 1 collection + let buffer = context + .banks_client + .get_account(iguana.pubkey()) + .await + .unwrap() + .unwrap() + .data; + let state = TlvStateBorrowed::unpack(&buffer).unwrap(); + let membership = state.get_first_value::().unwrap(); + assert_eq!(membership.group, reptile.pubkey(),); +} diff --git a/token-group/example/Cargo.toml b/token-group/example/Cargo.toml new file mode 100644 index 00000000000..e970c2d0bcc --- /dev/null +++ b/token-group/example/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "spl-token-group-example" +version = "0.1.0" +description = "Solana Program Library Token Group Example" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[features] +no-entrypoint = [] +test-sbf = [] + +[dependencies] +solana-program = "1.17.2" +spl-pod = { version = "0.1.0", path = "../../libraries/pod" } +spl-token-2022 = { version = "0.9.0", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-group-interface = { version = "0.1.0", path = "../interface" } +spl-type-length-value = { version = "0.3.0", path = "../../libraries/type-length-value" } + +[dev-dependencies] +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +spl-discriminator = { version = "0.1.0", path = "../../libraries/discriminator" } +spl-token-client = { version = "0.8", path = "../../token/client" } +spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" } + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/token-group/example/src/entrypoint.rs b/token-group/example/src/entrypoint.rs new file mode 100644 index 00000000000..762ec75255a --- /dev/null +++ b/token-group/example/src/entrypoint.rs @@ -0,0 +1,23 @@ +//! Program entrypoint + +use { + crate::processor, + solana_program::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, + program_error::PrintProgramError, pubkey::Pubkey, + }, + spl_token_group_interface::error::TokenGroupError, +}; + +entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + if let Err(error) = processor::process(program_id, accounts, instruction_data) { + error.print::(); + return Err(error); + } + Ok(()) +} diff --git a/token-group/example/src/lib.rs b/token-group/example/src/lib.rs new file mode 100644 index 00000000000..bd0da38115d --- /dev/null +++ b/token-group/example/src/lib.rs @@ -0,0 +1,10 @@ +//! Crate defining an example program for creating SPL token groups +//! using the SPL Token Group interface. + +#![deny(missing_docs)] +#![forbid(unsafe_code)] + +pub mod processor; + +#[cfg(not(feature = "no-entrypoint"))] +mod entrypoint; diff --git a/token-group/example/src/processor.rs b/token-group/example/src/processor.rs new file mode 100644 index 00000000000..fd1dd0c6647 --- /dev/null +++ b/token-group/example/src/processor.rs @@ -0,0 +1,226 @@ +//! Program state processor + +use { + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + program_error::ProgramError, + program_option::COption, + pubkey::Pubkey, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, + spl_token_2022::{extension::StateWithExtensions, state::Mint}, + spl_token_group_interface::{ + error::TokenGroupError, + instruction::{ + InitializeGroup, TokenGroupInstruction, UpdateGroupAuthority, UpdateGroupMaxSize, + }, + state::{TokenGroup, TokenGroupMember}, + }, + spl_type_length_value::state::TlvStateMut, +}; + +fn check_update_authority( + update_authority_info: &AccountInfo, + expected_update_authority: &OptionalNonZeroPubkey, +) -> Result<(), ProgramError> { + if !update_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + let update_authority = Option::::from(*expected_update_authority) + .ok_or(TokenGroupError::ImmutableGroup)?; + if update_authority != *update_authority_info.key { + return Err(TokenGroupError::IncorrectUpdateAuthority.into()); + } + Ok(()) +} + +/// Processes an [InitializeGroup](enum.GroupInterfaceInstruction.html) +/// instruction +pub fn process_initialize_group( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: InitializeGroup, +) -> ProgramResult { + // Assumes one has already created a mint for the group. + let account_info_iter = &mut accounts.iter(); + + // Accounts expected by this instruction: + // + // 0. `[w]` Group + // 1. `[]` Mint + // 2. `[s]` Mint authority + let group_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let mint_authority_info = next_account_info(account_info_iter)?; + + { + // IMPORTANT: this example program is designed to work with any + // program that implements the SPL token interface, so there is no + // ownership check on the mint account. + let mint_data = mint_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + if !mint_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if mint.base.mint_authority.as_ref() != COption::Some(mint_authority_info.key) { + return Err(TokenGroupError::IncorrectMintAuthority.into()); + } + } + + // Allocate a TLV entry for the space and write it in + let mut buffer = group_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let (group, _) = state.init_value::(false)?; + *group = TokenGroup::new(mint_info.key, data.update_authority, data.max_size.into()); + + Ok(()) +} + +/// Processes an +/// [UpdateGroupMaxSize](enum.GroupInterfaceInstruction.html) +/// instruction +pub fn process_update_group_max_size( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: UpdateGroupMaxSize, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + // Accounts expected by this instruction: + // + // 0. `[w]` Group + // 1. `[s]` Update authority + let group_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + + let mut buffer = group_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let group = state.get_first_value_mut::()?; + + check_update_authority(update_authority_info, &group.update_authority)?; + + // Update the max size (zero-copy) + group.update_max_size(data.max_size.into())?; + + Ok(()) +} + +/// Processes an +/// [UpdateGroupAuthority](enum.GroupInterfaceInstruction.html) +/// instruction +pub fn process_update_group_authority( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: UpdateGroupAuthority, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + // Accounts expected by this instruction: + // + // 0. `[w]` Group + // 1. `[s]` Current update authority + let group_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + + let mut buffer = group_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let group = state.get_first_value_mut::()?; + + check_update_authority(update_authority_info, &group.update_authority)?; + + // Update the authority (zero-copy) + group.update_authority = data.new_authority; + + Ok(()) +} + +/// Processes an [InitializeMember](enum.GroupInterfaceInstruction.html) +/// instruction +pub fn process_initialize_member(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + // For this group, we are going to assume the group has been + // initialized, and we're also assuming a mint has been created for the + // member. + // Group members in this example can have their own separate + // metadata that differs from the metadata of the group, since + // metadata is not involved here. + let account_info_iter = &mut accounts.iter(); + + // Accounts expected by this instruction: + // + // 0. `[w]` Member + // 1. `[]` Member Mint + // 2. `[s]` Member Mint authority + // 3. `[w]` Group + // 4. `[s]` Group update authority + let member_info = next_account_info(account_info_iter)?; + let member_mint_info = next_account_info(account_info_iter)?; + let member_mint_authority_info = next_account_info(account_info_iter)?; + let group_info = next_account_info(account_info_iter)?; + let group_update_authority_info = next_account_info(account_info_iter)?; + + // Mint checks on the member + { + // IMPORTANT: this example program is designed to work with any + // program that implements the SPL token interface, so there is no + // ownership check on the mint account. + let member_mint_data = member_mint_info.try_borrow_data()?; + let member_mint = StateWithExtensions::::unpack(&member_mint_data)?; + + if !member_mint_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if member_mint.base.mint_authority.as_ref() != COption::Some(member_mint_authority_info.key) + { + return Err(TokenGroupError::IncorrectMintAuthority.into()); + } + } + + // Make sure the member account is not the same as the group accout + if member_info.key == group_info.key { + return Err(TokenGroupError::MemberAccountIsGroupAccount.into()); + } + + // Increment the size of the group + let mut buffer = group_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + let group = state.get_first_value_mut::()?; + + check_update_authority(group_update_authority_info, &group.update_authority)?; + let member_number = group.increment_size()?; + + // Allocate a TLV entry for the space and write it in + let mut buffer = member_info.try_borrow_mut_data()?; + let mut state = TlvStateMut::unpack(&mut buffer)?; + // Note if `allow_repetition: true` is instead used here, one can initialize + // the same token as a member of multiple groups! + let (member, _) = state.init_value::(false)?; + *member = TokenGroupMember::new(member_mint_info.key, group_info.key, member_number); + + Ok(()) +} + +/// Processes an `SplTokenGroupInstruction` +pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = TokenGroupInstruction::unpack(input)?; + match instruction { + TokenGroupInstruction::InitializeGroup(data) => { + msg!("Instruction: InitializeGroup"); + process_initialize_group(program_id, accounts, data) + } + TokenGroupInstruction::UpdateGroupMaxSize(data) => { + msg!("Instruction: UpdateGroupMaxSize"); + process_update_group_max_size(program_id, accounts, data) + } + TokenGroupInstruction::UpdateGroupAuthority(data) => { + msg!("Instruction: UpdateGroupAuthority"); + process_update_group_authority(program_id, accounts, data) + } + TokenGroupInstruction::InitializeMember(_) => { + msg!("Instruction: InitializeMember"); + process_initialize_member(program_id, accounts) + } + } +} diff --git a/token-group/example/tests/initialize_group.rs b/token-group/example/tests/initialize_group.rs new file mode 100644 index 00000000000..a6461324eb8 --- /dev/null +++ b/token-group/example/tests/initialize_group.rs @@ -0,0 +1,149 @@ +#![cfg(feature = "test-sbf")] + +mod setup; + +use { + setup::{setup_mint, setup_program_test}, + solana_program::{instruction::InstructionError, pubkey::Pubkey, system_instruction}, + solana_program_test::tokio, + solana_sdk::{ + signature::Keypair, + signer::Signer, + transaction::{Transaction, TransactionError}, + }, + spl_token_client::token::Token, + spl_token_group_interface::{instruction::initialize_group, state::TokenGroup}, + spl_type_length_value::{ + error::TlvError, + state::{TlvState, TlvStateBorrowed}, + }, +}; + +#[tokio::test] +async fn test_initialize_group() { + let program_id = Pubkey::new_unique(); + let group = Keypair::new(); + let group_mint = Keypair::new(); + let group_mint_authority = Keypair::new(); + + let group_state = TokenGroup::new(&group_mint.pubkey(), None.try_into().unwrap(), 50); + + let (context, client, payer) = setup_program_test(&program_id).await; + + let token_client = Token::new( + client, + &spl_token_2022::id(), + &group_mint.pubkey(), + Some(0), + payer.clone(), + ); + setup_mint(&token_client, &group_mint, &group_mint_authority).await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let rent_lamports = rent.minimum_balance(space); + + // Fail: mint authority not signer + let mut init_group_ix = initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + group_state.update_authority.try_into().unwrap(), + group_state.max_size.into(), + ); + init_group_ix.accounts[2].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + init_group_ix, + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature) + ); + + // Success: create the group + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + group_state.update_authority.try_into().unwrap(), + group_state.max_size.into(), + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority, &group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fetch the group account and ensure it matches our state + let fetched_group_account = context + .banks_client + .get_account(group.pubkey()) + .await + .unwrap() + .unwrap(); + let fetched_meta = TlvStateBorrowed::unpack(&fetched_group_account.data).unwrap(); + let fetched_group_state = fetched_meta.get_first_value::().unwrap(); + assert_eq!(fetched_group_state, &group_state); + + // Fail: can't initialize twice + let transaction = Transaction::new_signed_with_payer( + &[initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + Pubkey::new_unique().into(), // Intentionally changed + group_state.max_size.into(), + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TlvError::TypeAlreadyExists as u32) + ) + ); +} diff --git a/token-group/example/tests/initialize_member.rs b/token-group/example/tests/initialize_member.rs new file mode 100644 index 00000000000..b0f7193d3cc --- /dev/null +++ b/token-group/example/tests/initialize_member.rs @@ -0,0 +1,260 @@ +#![cfg(feature = "test-sbf")] + +mod setup; + +use { + setup::{setup_mint, setup_program_test}, + solana_program::{instruction::InstructionError, pubkey::Pubkey, system_instruction}, + solana_program_test::tokio, + solana_sdk::{ + signature::Keypair, + signer::Signer, + transaction::{Transaction, TransactionError}, + }, + spl_token_client::token::Token, + spl_token_group_interface::{ + error::TokenGroupError, + instruction::{initialize_group, initialize_member}, + state::{TokenGroup, TokenGroupMember}, + }, + spl_type_length_value::state::{TlvState, TlvStateBorrowed}, +}; + +#[tokio::test] +async fn test_initialize_group_member() { + let program_id = Pubkey::new_unique(); + let group = Keypair::new(); + let group_mint = Keypair::new(); + let group_mint_authority = Keypair::new(); + let group_update_authority = Keypair::new(); + let member = Keypair::new(); + let member_mint = Keypair::new(); + let member_mint_authority = Keypair::new(); + + let group_state = TokenGroup::new( + &group_mint.pubkey(), + Some(group_update_authority.pubkey()).try_into().unwrap(), + 50, + ); + + let (context, client, payer) = setup_program_test(&program_id).await; + + setup_mint( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &group_mint.pubkey(), + Some(0), + payer.clone(), + ), + &group_mint, + &group_mint_authority, + ) + .await; + setup_mint( + &Token::new( + client.clone(), + &spl_token_2022::id(), + &member_mint.pubkey(), + Some(0), + payer.clone(), + ), + &member_mint, + &member_mint_authority, + ) + .await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let rent_lamports = rent.minimum_balance(space); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + group_state.update_authority.try_into().unwrap(), + group_state.max_size.into(), + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority, &group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + let member_space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let member_rent_lamports = rent.minimum_balance(member_space); + + // Fail: member mint authority not signer + let mut init_member_ix = initialize_member( + &program_id, + &member.pubkey(), + &member_mint.pubkey(), + &member_mint_authority.pubkey(), + &group.pubkey(), + &group_update_authority.pubkey(), + ); + init_member_ix.accounts[2].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &member.pubkey(), + member_rent_lamports, + member_space.try_into().unwrap(), + &program_id, + ), + init_member_ix, + ], + Some(&context.payer.pubkey()), + &[&context.payer, &member, &group_update_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature) + ); + + // Fail: group update authority not signer + let mut init_member_ix = initialize_member( + &program_id, + &member.pubkey(), + &member_mint.pubkey(), + &member_mint_authority.pubkey(), + &group.pubkey(), + &group_update_authority.pubkey(), + ); + init_member_ix.accounts[4].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &member.pubkey(), + member_rent_lamports, + member_space.try_into().unwrap(), + &program_id, + ), + init_member_ix, + ], + Some(&context.payer.pubkey()), + &[&context.payer, &member, &member_mint_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature) + ); + + // Fail: member account is group account + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &member.pubkey(), + member_rent_lamports, + member_space.try_into().unwrap(), + &program_id, + ), + initialize_member( + &program_id, + &member.pubkey(), + &member_mint.pubkey(), + &member_mint_authority.pubkey(), + &member.pubkey(), + &group_update_authority.pubkey(), + ), + ], + Some(&context.payer.pubkey()), + &[ + &context.payer, + &member, + &member_mint_authority, + &group_update_authority, + ], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 1, + InstructionError::Custom(TokenGroupError::MemberAccountIsGroupAccount as u32) + ) + ); + + // Success: initialize member + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &member.pubkey(), + member_rent_lamports, + member_space.try_into().unwrap(), + &program_id, + ), + initialize_member( + &program_id, + &member.pubkey(), + &member_mint.pubkey(), + &member_mint_authority.pubkey(), + &group.pubkey(), + &group_update_authority.pubkey(), + ), + ], + Some(&context.payer.pubkey()), + &[ + &context.payer, + &member, + &member_mint_authority, + &group_update_authority, + ], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fetch the member account and ensure it matches our state + let member_account = context + .banks_client + .get_account(member.pubkey()) + .await + .unwrap() + .unwrap(); + let fetched_meta = TlvStateBorrowed::unpack(&member_account.data).unwrap(); + let fetched_group_member_state = fetched_meta.get_first_value::().unwrap(); + assert_eq!(fetched_group_member_state.group, group.pubkey()); + assert_eq!(u32::from(fetched_group_member_state.member_number), 1); +} diff --git a/token-group/example/tests/setup.rs b/token-group/example/tests/setup.rs new file mode 100644 index 00000000000..14cb091830c --- /dev/null +++ b/token-group/example/tests/setup.rs @@ -0,0 +1,61 @@ +#![cfg(feature = "test-sbf")] + +use { + solana_program_test::{processor, tokio::sync::Mutex, ProgramTest, ProgramTestContext}, + solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}, + spl_token_client::{ + client::{ + ProgramBanksClient, ProgramBanksClientProcessTransaction, ProgramClient, + SendTransaction, SimulateTransaction, + }, + token::Token, + }, + std::sync::Arc, +}; + +/// Set up a program test +pub async fn setup_program_test( + program_id: &Pubkey, +) -> ( + Arc>, + Arc>, + Arc, +) { + let mut program_test = ProgramTest::new( + "spl_token_group_example", + *program_id, + processor!(spl_token_group_example::processor::process), + ); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(spl_token_2022::processor::Processor::process), + ); + let context = program_test.start_with_context().await; + let payer = Arc::new(context.payer.insecure_clone()); + let context = Arc::new(Mutex::new(context)); + let client: Arc> = + Arc::new(ProgramBanksClient::new_from_context( + Arc::clone(&context), + ProgramBanksClientProcessTransaction, + )); + (context, client, payer) +} + +/// Set up a Token-2022 mint +pub async fn setup_mint( + token_client: &Token, + mint_keypair: &Keypair, + mint_authority_keypair: &Keypair, +) { + token_client + .create_mint( + &mint_authority_keypair.pubkey(), + None, + vec![], + &[mint_keypair], + ) + .await + .unwrap(); +} diff --git a/token-group/example/tests/update_group_authority.rs b/token-group/example/tests/update_group_authority.rs new file mode 100644 index 00000000000..35c74ec8682 --- /dev/null +++ b/token-group/example/tests/update_group_authority.rs @@ -0,0 +1,187 @@ +#![cfg(feature = "test-sbf")] + +mod setup; + +use { + setup::{setup_mint, setup_program_test}, + solana_program::{instruction::InstructionError, pubkey::Pubkey, system_instruction}, + solana_program_test::tokio, + solana_sdk::{ + signature::Keypair, + signer::Signer, + transaction::{Transaction, TransactionError}, + }, + spl_token_client::token::Token, + spl_token_group_interface::{ + error::TokenGroupError, + instruction::{initialize_group, update_group_authority}, + state::TokenGroup, + }, + spl_type_length_value::state::{TlvState, TlvStateBorrowed}, +}; + +#[tokio::test] +async fn test_update_group_authority() { + let program_id = Pubkey::new_unique(); + let group = Keypair::new(); + let group_mint = Keypair::new(); + let group_mint_authority = Keypair::new(); + let group_update_authority = Keypair::new(); + + let group_state = TokenGroup::new( + &group_mint.pubkey(), + Some(group_update_authority.pubkey()).try_into().unwrap(), + 50, + ); + + let (context, client, payer) = setup_program_test(&program_id).await; + + let token_client = Token::new( + client, + &spl_token_2022::id(), + &group_mint.pubkey(), + Some(0), + payer.clone(), + ); + setup_mint(&token_client, &group_mint, &group_mint_authority).await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let rent_lamports = rent.minimum_balance(space); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + group_state.update_authority.try_into().unwrap(), + group_state.max_size.into(), + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority, &group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fail: update authority not signer + let mut update_ix = update_group_authority( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + None, + ); + update_ix.accounts[1].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[update_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ); + + // Fail: incorrect update authority + let transaction = Transaction::new_signed_with_payer( + &[update_group_authority( + &program_id, + &group.pubkey(), + &group.pubkey(), + None, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenGroupError::IncorrectUpdateAuthority as u32) + ) + ); + + // Success: update authority + let transaction = Transaction::new_signed_with_payer( + &[update_group_authority( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + None, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_update_authority], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fetch the account and assert the new authority + let fetched_group_account = context + .banks_client + .get_account(group.pubkey()) + .await + .unwrap() + .unwrap(); + let fetched_meta = TlvStateBorrowed::unpack(&fetched_group_account.data).unwrap(); + let fetched_group_state = fetched_meta.get_first_value::().unwrap(); + assert_eq!( + fetched_group_state.update_authority, + None.try_into().unwrap(), + ); + + // Fail: immutable group + let transaction = Transaction::new_signed_with_payer( + &[update_group_authority( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + Some(group_update_authority.pubkey()), + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_update_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenGroupError::ImmutableGroup as u32) + ) + ); +} diff --git a/token-group/example/tests/update_group_max_size.rs b/token-group/example/tests/update_group_max_size.rs new file mode 100644 index 00000000000..412335bfc18 --- /dev/null +++ b/token-group/example/tests/update_group_max_size.rs @@ -0,0 +1,283 @@ +#![cfg(feature = "test-sbf")] + +mod setup; + +use { + setup::{setup_mint, setup_program_test}, + solana_program::{instruction::InstructionError, pubkey::Pubkey, system_instruction}, + solana_program_test::tokio, + solana_sdk::{ + account::Account as SolanaAccount, + signature::Keypair, + signer::Signer, + transaction::{Transaction, TransactionError}, + }, + spl_token_client::token::Token, + spl_token_group_interface::{ + error::TokenGroupError, + instruction::{initialize_group, update_group_max_size}, + state::TokenGroup, + }, + spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut}, +}; + +#[tokio::test] +async fn test_update_group_max_size() { + let program_id = Pubkey::new_unique(); + let group = Keypair::new(); + let group_mint = Keypair::new(); + let group_mint_authority = Keypair::new(); + let group_update_authority = Keypair::new(); + + let group_state = TokenGroup::new( + &group_mint.pubkey(), + Some(group_update_authority.pubkey()).try_into().unwrap(), + 50, + ); + + let (context, client, payer) = setup_program_test(&program_id).await; + + let token_client = Token::new( + client, + &spl_token_2022::id(), + &group_mint.pubkey(), + Some(0), + payer.clone(), + ); + setup_mint(&token_client, &group_mint, &group_mint_authority).await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let rent_lamports = rent.minimum_balance(space); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + group_state.update_authority.try_into().unwrap(), + group_state.max_size.into(), + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority, &group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fail: update authority not signer + let mut update_ix = update_group_max_size(&program_id, &group.pubkey(), &group.pubkey(), 100); + update_ix.accounts[1].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[update_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ); + + // Fail: incorrect update authority + let transaction = Transaction::new_signed_with_payer( + &[update_group_max_size( + &program_id, + &group.pubkey(), + &group.pubkey(), + 100, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenGroupError::IncorrectUpdateAuthority as u32) + ) + ); + + // Fail: size exceeds new max size + let fetched_group_account = context + .banks_client + .get_account(group.pubkey()) + .await + .unwrap() + .unwrap(); + let mut data = fetched_group_account.data; + let mut state = TlvStateMut::unpack(&mut data).unwrap(); + let group_data = state.get_first_value_mut::().unwrap(); + group_data.size = 30.into(); + context.set_account( + &group.pubkey(), + &SolanaAccount { + data, + ..fetched_group_account + } + .into(), + ); + let transaction = Transaction::new_signed_with_payer( + &[update_group_max_size( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + 20, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_update_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenGroupError::SizeExceedsNewMaxSize as u32) + ) + ); + + // Success: update max size + let transaction = Transaction::new_signed_with_payer( + &[update_group_max_size( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + 100, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_update_authority], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + // Fetch the account and assert the new max size + let fetched_group_account = context + .banks_client + .get_account(group.pubkey()) + .await + .unwrap() + .unwrap(); + let fetched_meta = TlvStateBorrowed::unpack(&fetched_group_account.data).unwrap(); + let fetched_group_state = fetched_meta.get_first_value::().unwrap(); + assert_eq!(fetched_group_state.max_size, 100.into()); +} + +// Fail: immutable group +#[tokio::test] +async fn test_update_group_max_size_fail_immutable_group() { + let program_id = Pubkey::new_unique(); + let group = Keypair::new(); + let group_mint = Keypair::new(); + let group_mint_authority = Keypair::new(); + let group_update_authority = Keypair::new(); + + let group_state = TokenGroup::new( + &group_mint.pubkey(), + Some(group_update_authority.pubkey()).try_into().unwrap(), + 50, + ); + + let (context, client, payer) = setup_program_test(&program_id).await; + + let token_client = Token::new( + client, + &spl_token_2022::id(), + &group_mint.pubkey(), + Some(0), + payer.clone(), + ); + setup_mint(&token_client, &group_mint, &group_mint_authority).await; + + let mut context = context.lock().await; + + let rent = context.banks_client.get_rent().await.unwrap(); + let space = TlvStateBorrowed::get_base_len() + std::mem::size_of::(); + let rent_lamports = rent.minimum_balance(space); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &group.pubkey(), + rent_lamports, + space.try_into().unwrap(), + &program_id, + ), + initialize_group( + &program_id, + &group.pubkey(), + &group_mint.pubkey(), + &group_mint_authority.pubkey(), + None, + group_state.max_size.into(), + ), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &group_mint_authority, &group], + context.last_blockhash, + ); + context + .banks_client + .process_transaction(transaction) + .await + .unwrap(); + + let transaction = Transaction::new_signed_with_payer( + &[update_group_max_size( + &program_id, + &group.pubkey(), + &group_update_authority.pubkey(), + 100, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &group_update_authority], + context.last_blockhash, + ); + assert_eq!( + context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(), + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenGroupError::ImmutableGroup as u32) + ) + ); +} diff --git a/token-group/interface/Cargo.toml b/token-group/interface/Cargo.toml new file mode 100644 index 00000000000..7f5b1676334 --- /dev/null +++ b/token-group/interface/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "spl-token-group-interface" +version = "0.1.0" +description = "Solana Program Library Token Group Interface" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +bytemuck = "1.14.0" +solana-program = "1.17.2" +spl-discriminator = { version = "0.1.0" , path = "../../libraries/discriminator" } +spl-pod = { version = "0.1.0" , path = "../../libraries/pod", features = ["borsh"] } +spl-program-error = { version = "0.3.0" , path = "../../libraries/program-error" } + +[dev-dependencies] +spl-type-length-value = { version = "0.3.0", path = "../../libraries/type-length-value", features = ["derive"] } + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/token-group/interface/src/error.rs b/token-group/interface/src/error.rs new file mode 100644 index 00000000000..6840a060863 --- /dev/null +++ b/token-group/interface/src/error.rs @@ -0,0 +1,26 @@ +//! Interface error types + +use spl_program_error::*; + +/// Errors that may be returned by the interface. +#[spl_program_error] +pub enum TokenGroupError { + /// Size is greater than proposed max size + #[error("Size is greater than proposed max size")] + SizeExceedsNewMaxSize, + /// Size is greater than max size + #[error("Size is greater than max size")] + SizeExceedsMaxSize, + /// Group is immutable + #[error("Group is immutable")] + ImmutableGroup, + /// Incorrect mint authority has signed the instruction + #[error("Incorrect mint authority has signed the instruction")] + IncorrectMintAuthority, + /// Incorrect update authority has signed the instruction + #[error("Incorrect update authority has signed the instruction")] + IncorrectUpdateAuthority, + /// Member account should not be the same as the group account + #[error("Member account should not be the same as the group account")] + MemberAccountIsGroupAccount, +} diff --git a/token-group/interface/src/instruction.rs b/token-group/interface/src/instruction.rs new file mode 100644 index 00000000000..67536d896bd --- /dev/null +++ b/token-group/interface/src/instruction.rs @@ -0,0 +1,308 @@ +//! Instruction types + +use { + bytemuck::{Pod, Zeroable}, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, + }, + spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, + spl_pod::{ + bytemuck::{pod_bytes_of, pod_from_bytes}, + optional_keys::OptionalNonZeroPubkey, + primitives::PodU32, + }, +}; + +/// Instruction data for initializing a new `Group` +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:initialize_token_group")] +pub struct InitializeGroup { + /// Update authority for the group + pub update_authority: OptionalNonZeroPubkey, + /// The maximum number of group members + pub max_size: PodU32, +} + +/// Instruction data for updating the max size of a `Group` +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:update_group_max_size")] +pub struct UpdateGroupMaxSize { + /// New max size for the group + pub max_size: PodU32, +} + +/// Instruction data for updating the authority of a `Group` +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:update_authority")] +pub struct UpdateGroupAuthority { + /// New authority for the group, or unset if `None` + pub new_authority: OptionalNonZeroPubkey, +} + +/// Instruction data for initializing a new `Member` of a `Group` +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:initialize_member")] +pub struct InitializeMember; + +/// All instructions that must be implemented in the SPL Token Group Interface +#[derive(Clone, Debug, PartialEq)] +pub enum TokenGroupInstruction { + /// Initialize a new `Group` + /// + /// Assumes one has already initialized a mint for the + /// group. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[w]` Group + /// 1. `[]` Mint + /// 2. `[s]` Mint authority + InitializeGroup(InitializeGroup), + + /// Update the max size of a `Group` + /// + /// Accounts expected by this instruction: + /// + /// 0. `[w]` Group + /// 1. `[s]` Update authority + UpdateGroupMaxSize(UpdateGroupMaxSize), + + /// Update the authority of a `Group` + /// + /// Accounts expected by this instruction: + /// + /// 0. `[w]` Group + /// 1. `[s]` Current update authority + UpdateGroupAuthority(UpdateGroupAuthority), + + /// Initialize a new `Member` of a `Group` + /// + /// Assumes the `Group` has already been initialized, + /// as well as the mint for the member. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[w]` Member + /// 1. `[]` Member mint + /// 1. `[s]` Member mint authority + /// 2. `[w]` Group + /// 3. `[s]` Group update authority + InitializeMember(InitializeMember), +} +impl TokenGroupInstruction { + /// Unpacks a byte buffer into a `TokenGroupInstruction` + pub fn unpack(input: &[u8]) -> Result { + if input.len() < ArrayDiscriminator::LENGTH { + return Err(ProgramError::InvalidInstructionData); + } + let (discriminator, rest) = input.split_at(ArrayDiscriminator::LENGTH); + Ok(match discriminator { + InitializeGroup::SPL_DISCRIMINATOR_SLICE => { + let data = pod_from_bytes::(rest)?; + Self::InitializeGroup(*data) + } + UpdateGroupMaxSize::SPL_DISCRIMINATOR_SLICE => { + let data = pod_from_bytes::(rest)?; + Self::UpdateGroupMaxSize(*data) + } + UpdateGroupAuthority::SPL_DISCRIMINATOR_SLICE => { + let data = pod_from_bytes::(rest)?; + Self::UpdateGroupAuthority(*data) + } + InitializeMember::SPL_DISCRIMINATOR_SLICE => { + let data = pod_from_bytes::(rest)?; + Self::InitializeMember(*data) + } + _ => return Err(ProgramError::InvalidInstructionData), + }) + } + + /// Packs a `TokenGroupInstruction` into a byte buffer. + pub fn pack(&self) -> Vec { + let mut buf = vec![]; + match self { + Self::InitializeGroup(data) => { + buf.extend_from_slice(InitializeGroup::SPL_DISCRIMINATOR_SLICE); + buf.extend_from_slice(pod_bytes_of(data)); + } + Self::UpdateGroupMaxSize(data) => { + buf.extend_from_slice(UpdateGroupMaxSize::SPL_DISCRIMINATOR_SLICE); + buf.extend_from_slice(pod_bytes_of(data)); + } + Self::UpdateGroupAuthority(data) => { + buf.extend_from_slice(UpdateGroupAuthority::SPL_DISCRIMINATOR_SLICE); + buf.extend_from_slice(pod_bytes_of(data)); + } + Self::InitializeMember(data) => { + buf.extend_from_slice(InitializeMember::SPL_DISCRIMINATOR_SLICE); + buf.extend_from_slice(pod_bytes_of(data)); + } + }; + buf + } +} + +/// Creates a `InitializeGroup` instruction +pub fn initialize_group( + program_id: &Pubkey, + group: &Pubkey, + mint: &Pubkey, + mint_authority: &Pubkey, + update_authority: Option, + max_size: u32, +) -> Instruction { + let update_authority = OptionalNonZeroPubkey::try_from(update_authority) + .expect("Failed to deserialize `Option`"); + let data = TokenGroupInstruction::InitializeGroup(InitializeGroup { + update_authority, + max_size: max_size.into(), + }) + .pack(); + Instruction { + program_id: *program_id, + accounts: vec![ + AccountMeta::new(*group, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new_readonly(*mint_authority, true), + ], + data, + } +} + +/// Creates a `UpdateGroupMaxSize` instruction +pub fn update_group_max_size( + program_id: &Pubkey, + group: &Pubkey, + update_authority: &Pubkey, + max_size: u32, +) -> Instruction { + let data = TokenGroupInstruction::UpdateGroupMaxSize(UpdateGroupMaxSize { + max_size: max_size.into(), + }) + .pack(); + Instruction { + program_id: *program_id, + accounts: vec![ + AccountMeta::new(*group, false), + AccountMeta::new_readonly(*update_authority, true), + ], + data, + } +} + +/// Creates a `UpdateGroupAuthority` instruction +pub fn update_group_authority( + program_id: &Pubkey, + group: &Pubkey, + current_authority: &Pubkey, + new_authority: Option, +) -> Instruction { + let new_authority = OptionalNonZeroPubkey::try_from(new_authority) + .expect("Failed to deserialize `Option`"); + let data = + TokenGroupInstruction::UpdateGroupAuthority(UpdateGroupAuthority { new_authority }).pack(); + Instruction { + program_id: *program_id, + accounts: vec![ + AccountMeta::new(*group, false), + AccountMeta::new_readonly(*current_authority, true), + ], + data, + } +} + +/// Creates a `InitializeMember` instruction +#[allow(clippy::too_many_arguments)] +pub fn initialize_member( + program_id: &Pubkey, + member: &Pubkey, + member_mint: &Pubkey, + member_mint_authority: &Pubkey, + group: &Pubkey, + group_update_authority: &Pubkey, +) -> Instruction { + let data = TokenGroupInstruction::InitializeMember(InitializeMember {}).pack(); + Instruction { + program_id: *program_id, + accounts: vec![ + AccountMeta::new(*member, false), + AccountMeta::new_readonly(*member_mint, false), + AccountMeta::new_readonly(*member_mint_authority, true), + AccountMeta::new(*group, false), + AccountMeta::new_readonly(*group_update_authority, true), + ], + data, + } +} + +#[cfg(test)] +mod test { + use {super::*, crate::NAMESPACE, solana_program::hash}; + + #[repr(C)] + #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, SplDiscriminate)] + #[discriminator_hash_input("mock_group")] + struct MockGroup; + + fn instruction_pack_unpack(instruction: TokenGroupInstruction, discriminator: &[u8], data: I) + where + I: core::fmt::Debug + PartialEq + Pod + Zeroable + SplDiscriminate, + { + let mut expect = vec![]; + expect.extend_from_slice(discriminator.as_ref()); + expect.extend_from_slice(pod_bytes_of(&data)); + let packed = instruction.pack(); + assert_eq!(packed, expect); + let unpacked = TokenGroupInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, instruction); + } + + #[test] + fn initialize_group_pack() { + let data = InitializeGroup { + update_authority: OptionalNonZeroPubkey::default(), + max_size: 100.into(), + }; + let instruction = TokenGroupInstruction::InitializeGroup(data); + let preimage = hash::hashv(&[format!("{NAMESPACE}:initialize_token_group").as_bytes()]); + let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; + instruction_pack_unpack::(instruction, discriminator, data); + } + + #[test] + fn update_group_max_size_pack() { + let data = UpdateGroupMaxSize { + max_size: 200.into(), + }; + let instruction = TokenGroupInstruction::UpdateGroupMaxSize(data); + let preimage = hash::hashv(&[format!("{NAMESPACE}:update_group_max_size").as_bytes()]); + let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; + instruction_pack_unpack::(instruction, discriminator, data); + } + + #[test] + fn update_authority_pack() { + let data = UpdateGroupAuthority { + new_authority: OptionalNonZeroPubkey::default(), + }; + let instruction = TokenGroupInstruction::UpdateGroupAuthority(data); + let preimage = hash::hashv(&[format!("{NAMESPACE}:update_authority").as_bytes()]); + let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; + instruction_pack_unpack::(instruction, discriminator, data); + } + + #[test] + fn initialize_member_pack() { + let data = InitializeMember {}; + let instruction = TokenGroupInstruction::InitializeMember(data); + let preimage = hash::hashv(&[format!("{NAMESPACE}:initialize_member").as_bytes()]); + let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; + instruction_pack_unpack::(instruction, discriminator, data); + } +} diff --git a/token-group/interface/src/lib.rs b/token-group/interface/src/lib.rs new file mode 100644 index 00000000000..6867b86acf8 --- /dev/null +++ b/token-group/interface/src/lib.rs @@ -0,0 +1,11 @@ +//! Crate defining the SPL Token Group Interface + +#![deny(missing_docs)] +#![cfg_attr(not(test), forbid(unsafe_code))] + +pub mod error; +pub mod instruction; +pub mod state; + +/// Namespace for all programs implementing spl-token-group +pub const NAMESPACE: &str = "spl_token_group_interface"; diff --git a/token-group/interface/src/state.rs b/token-group/interface/src/state.rs new file mode 100644 index 00000000000..9f73ecc442b --- /dev/null +++ b/token-group/interface/src/state.rs @@ -0,0 +1,194 @@ +//! Interface state types + +use { + crate::error::TokenGroupError, + bytemuck::{Pod, Zeroable}, + solana_program::{program_error::ProgramError, pubkey::Pubkey}, + spl_discriminator::SplDiscriminate, + spl_pod::{error::PodSliceError, optional_keys::OptionalNonZeroPubkey, primitives::PodU32}, +}; + +/// Data struct for a `TokenGroup` +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:group")] +pub struct TokenGroup { + /// The authority that can sign to update the group + pub update_authority: OptionalNonZeroPubkey, + /// The associated mint, used to counter spoofing to be sure that group + /// belongs to a particular mint + pub mint: Pubkey, + /// The current number of group members + pub size: PodU32, + /// The maximum number of group members + pub max_size: PodU32, +} + +impl TokenGroup { + /// Creates a new `TokenGroup` state + pub fn new(mint: &Pubkey, update_authority: OptionalNonZeroPubkey, max_size: u32) -> Self { + Self { + mint: *mint, + update_authority, + size: PodU32::default(), // [0, 0, 0, 0] + max_size: max_size.into(), + } + } + + /// Updates the max size for a group + pub fn update_max_size(&mut self, new_max_size: u32) -> Result<(), ProgramError> { + // The new max size cannot be less than the current size + if new_max_size < u32::from(self.size) { + return Err(TokenGroupError::SizeExceedsNewMaxSize.into()); + } + self.max_size = new_max_size.into(); + Ok(()) + } + + /// Increment the size for a group, returning the new size + pub fn increment_size(&mut self) -> Result { + // The new size cannot be greater than the max size + let new_size = u32::from(self.size) + .checked_add(1) + .ok_or::(PodSliceError::CalculationFailure.into())?; + if new_size > u32::from(self.max_size) { + return Err(TokenGroupError::SizeExceedsMaxSize.into()); + } + self.size = new_size.into(); + Ok(new_size) + } +} + +/// Data struct for a `TokenGroupMember` +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, SplDiscriminate)] +#[discriminator_hash_input("spl_token_group_interface:member")] +pub struct TokenGroupMember { + /// The associated mint, used to counter spoofing to be sure that member + /// belongs to a particular mint + pub mint: Pubkey, + /// The pubkey of the `TokenGroup` + pub group: Pubkey, + /// The member number + pub member_number: PodU32, +} +impl TokenGroupMember { + /// Creates a new `TokenGroupMember` state + pub fn new(mint: &Pubkey, group: &Pubkey, member_number: u32) -> Self { + Self { + mint: *mint, + group: *group, + member_number: member_number.into(), + } + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::NAMESPACE, + solana_program::hash, + spl_discriminator::ArrayDiscriminator, + spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut}, + std::mem::size_of, + }; + + #[test] + fn discriminators() { + let preimage = hash::hashv(&[format!("{NAMESPACE}:group").as_bytes()]); + let discriminator = + ArrayDiscriminator::try_from(&preimage.as_ref()[..ArrayDiscriminator::LENGTH]).unwrap(); + assert_eq!(TokenGroup::SPL_DISCRIMINATOR, discriminator); + + let preimage = hash::hashv(&[format!("{NAMESPACE}:member").as_bytes()]); + let discriminator = + ArrayDiscriminator::try_from(&preimage.as_ref()[..ArrayDiscriminator::LENGTH]).unwrap(); + assert_eq!(TokenGroupMember::SPL_DISCRIMINATOR, discriminator); + } + + #[test] + fn tlv_state_pack() { + // Make sure we can pack more than one instance of each type + let group = TokenGroup { + mint: Pubkey::new_unique(), + update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(), + size: 10.into(), + max_size: 20.into(), + }; + + let member = TokenGroupMember { + mint: Pubkey::new_unique(), + group: Pubkey::new_unique(), + member_number: 0.into(), + }; + + let account_size = TlvStateBorrowed::get_base_len() + + size_of::() + + TlvStateBorrowed::get_base_len() + + size_of::(); + let mut buffer = vec![0; account_size]; + let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); + + let group_data = state.init_value::(false).unwrap().0; + *group_data = group; + + let member_data = state.init_value::(false).unwrap().0; + *member_data = member; + + assert_eq!(state.get_first_value::().unwrap(), &group); + assert_eq!( + state.get_first_value::().unwrap(), + &member + ); + } + + #[test] + fn update_max_size() { + // Test with a `Some` max size + let max_size = 10; + let mut group = TokenGroup { + mint: Pubkey::new_unique(), + update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(), + size: 0.into(), + max_size: max_size.into(), + }; + + let new_max_size = 30; + group.update_max_size(new_max_size).unwrap(); + assert_eq!(u32::from(group.max_size), new_max_size); + + // Change the current size to 30 + group.size = 30.into(); + + // Try to set the max size to 20, which is less than the current size + let new_max_size = 20; + assert_eq!( + group.update_max_size(new_max_size), + Err(ProgramError::from(TokenGroupError::SizeExceedsNewMaxSize)) + ); + + let new_max_size = 30; + group.update_max_size(new_max_size).unwrap(); + assert_eq!(u32::from(group.max_size), new_max_size); + } + + #[test] + fn increment_current_size() { + let mut group = TokenGroup { + mint: Pubkey::new_unique(), + update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(), + size: 0.into(), + max_size: 1.into(), + }; + + group.increment_size().unwrap(); + assert_eq!(u32::from(group.size), 1); + + // Try to increase the current size to 2, which is greater than the max size + assert_eq!( + group.increment_size(), + Err(ProgramError::from(TokenGroupError::SizeExceedsMaxSize)) + ); + } +} diff --git a/token-lending/Anchor.toml b/token-lending/Anchor.toml deleted file mode 100644 index 2394d60290f..00000000000 --- a/token-lending/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -anchor_version = "0.14.0" - -[workspace] -members = ["program"] - -[provider] -cluster = "devnet" -wallet = "~/.config/solana/id.json" - -[programs.devnet] -spl-token-lending = "LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi" \ No newline at end of file diff --git a/token-lending/cli/Cargo.toml b/token-lending/cli/Cargo.toml index f504cd72465..147859a00e7 100644 --- a/token-lending/cli/Cargo.toml +++ b/token-lending/cli/Cargo.toml @@ -10,12 +10,12 @@ version = "0.2.0" [dependencies] clap = "2.33.3" -solana-clap-utils = "=1.16.1" -solana-cli-config = "=1.16.1" -solana-client = "=1.16.1" -solana-logger = "=1.16.1" -solana-sdk = "=1.16.1" -solana-program = "=1.16.1" +solana-clap-utils = "=1.17.2" +solana-cli-config = "=1.17.2" +solana-client = "=1.17.2" +solana-logger = "=1.17.2" +solana-sdk = "=1.17.2" +solana-program = "=1.17.2" spl-token-lending = { version = "0.2", path="../program", features = [ "no-entrypoint" ] } spl-token = { version = "4.0", path="../../token/program", features = [ "no-entrypoint" ] } diff --git a/token-lending/cli/src/main.rs b/token-lending/cli/src/main.rs index ea7339519a1..3ba1eb293a6 100644 --- a/token-lending/cli/src/main.rs +++ b/token-lending/cli/src/main.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use { clap::{ crate_description, crate_name, crate_version, value_t, App, AppSettings, Arg, ArgMatches, diff --git a/token-lending/flash_loan_receiver/Cargo.toml b/token-lending/flash_loan_receiver/Cargo.toml index c9f5227f052..bb0429c5a9f 100644 --- a/token-lending/flash_loan_receiver/Cargo.toml +++ b/token-lending/flash_loan_receiver/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] arrayref = "0.3.7" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features=["no-entrypoint"] } [lib] diff --git a/token-lending/js/package.json b/token-lending/js/package.json index 7cf4d796bcd..d07a60f38ec 100644 --- a/token-lending/js/package.json +++ b/token-lending/js/package.json @@ -47,18 +47,18 @@ "@types/eslint": "^8.4.1", "@types/eslint-plugin-prettier": "^3.1.0", "@types/node": "^20.1.4", - "@types/prettier": "^2.4.4", + "@types/prettier": "^3.0.0", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.12.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "gh-pages": "^5.0.0", - "prettier": "^2.6.1", - "rollup": "^3.22.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0-alpha.1", + "gh-pages": "^6.0.0", + "prettier": "^3.0.0", + "rollup": "^4.0.2", "ts-node": "^10.7.0", "tslib": "^2.3.0", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" } } diff --git a/token-lending/js/tsconfig.json b/token-lending/js/tsconfig.json index eda3ea00e1d..9a8ce86d220 100644 --- a/token-lending/js/tsconfig.json +++ b/token-lending/js/tsconfig.json @@ -3,7 +3,6 @@ "target": "esnext", "emitDeclarationOnly": true, "outDir": "lib", - "typeRoots": ["node_modules/@types", "types"], "module": "esnext", "esModuleInterop": true, "resolveJsonModule": true, diff --git a/token-lending/js/yarn.lock b/token-lending/js/yarn.lock index 79773fbe1be..add49620aa6 100644 --- a/token-lending/js/yarn.lock +++ b/token-lending/js/yarn.lock @@ -7,12 +7,12 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== +"@babel/runtime@^7.17.2", "@babel/runtime@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== dependencies: - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -28,15 +28,15 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": - version "4.5.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" - integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" + integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== -"@eslint/eslintrc@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" - integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -48,17 +48,17 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.44.0": - version "8.44.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" - integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== +"@eslint/js@8.52.0": + version "8.52.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c" + integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA== -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" + "@humanwhocodes/object-schema" "^2.0.1" debug "^4.1.1" minimatch "^3.0.5" @@ -67,17 +67,17 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== "@jridgewell/resolve-uri@^3.0.3": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -90,17 +90,17 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@noble/curves@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" - integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== +"@noble/curves@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: - "@noble/hashes" "1.3.0" + "@noble/hashes" "1.3.2" -"@noble/hashes@1.3.0", "@noble/hashes@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== +"@noble/hashes@1.3.2", "@noble/hashes@^1.3.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -123,22 +123,34 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgr/utils@^2.3.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" + integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== + dependencies: + cross-spawn "^7.0.3" + fast-glob "^3.3.0" + is-glob "^4.0.3" + open "^9.1.0" + picocolors "^1.0.0" + tslib "^2.6.0" + "@rollup/plugin-commonjs@^25.0.0": - version "25.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.2.tgz#7ed37d00a12fc7fdd3aadba5fa0de52f2372bbbb" - integrity sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow== + version "25.0.7" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf" + integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ== dependencies: "@rollup/pluginutils" "^5.0.1" commondir "^1.0.1" estree-walker "^2.0.2" glob "^8.0.3" is-reference "1.2.1" - magic-string "^0.27.0" + magic-string "^0.30.3" "@rollup/plugin-node-resolve@^15.0.2": - version "15.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz#9ffcd8e8c457080dba89bb9fcb583a6778dc757e" - integrity sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA== + version "15.2.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9" + integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ== dependencies: "@rollup/pluginutils" "^5.0.1" "@types/resolve" "1.20.2" @@ -148,9 +160,9 @@ resolve "^1.22.1" "@rollup/plugin-typescript@^11.1.1": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-11.1.2.tgz#09eb5690a650bb0334bf84125bce9abd296442e4" - integrity sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg== + version "11.1.5" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz#039c763bf943a5921f3f42be255895e75764cb91" + integrity sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA== dependencies: "@rollup/pluginutils" "^5.0.1" resolve "^1.22.1" @@ -164,6 +176,66 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.2.0.tgz#b945a7044afce5de03d03a55aef3503a50d92586" + integrity sha512-8PlggAxGxavr+pkCNeV1TM2wTb2o+cUWDg9M1cm9nR27Dsn287uZtSLYXoQqQcmq+sYfF7lHfd3sWJJinH9GmA== + +"@rollup/rollup-android-arm64@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.2.0.tgz#416abdc076810cde6f84f05b0fc9decd2d7c319f" + integrity sha512-+71T85hbMFrJI+zKQULNmSYBeIhru55PYoF/u75MyeN2FcxE4HSPw20319b+FcZ4lWx2Nx/Ql9tN+hoaD3GH/A== + +"@rollup/rollup-darwin-arm64@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.2.0.tgz#4c468e8fce7ffa121c8b2067d5f3feb1bbeefec6" + integrity sha512-IIIQLuG43QIElT1JZqUP/zqIdiJl4t9U/boa0GZnQTw9m1X0k3mlBuysbgYXeloLT1RozdL7bgw4lpSaI8GOXw== + +"@rollup/rollup-darwin-x64@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.2.0.tgz#3e8dee93e01f60f380a8d3828acf6eb07c356b1b" + integrity sha512-BXcXvnLaea1Xz900omrGJhxHFJfH9jZ0CpJuVsbjjhpniJ6qiLXz3xA8Lekaa4MuhFcJd4f0r+Ky1G4VFbYhWw== + +"@rollup/rollup-linux-arm-gnueabihf@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.2.0.tgz#d4913e0d66ca9ae10863c2b81300bd0c79386390" + integrity sha512-f4K3MKw9Y4AKi4ANGnmPIglr+S+8tO858YrGVuqAHXxJdVghBmz9CPU9kDpOnGvT4g4vg5uNyIFpOOFvffXyMA== + +"@rollup/rollup-linux-arm64-gnu@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.2.0.tgz#811ec171fcf75dfcab59b1a04502c62c8410235e" + integrity sha512-bNsTYQBgp4H7w6cT7FZhesxpcUPahsSIy4NgdZjH1ZwEoZHxi4XKglj+CsSEkhsKi+x6toVvMylhjRKhEMYfnA== + +"@rollup/rollup-linux-arm64-musl@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.2.0.tgz#9291c7ec1a3572e9d3f395bcfff388ff173626ca" + integrity sha512-Jp1NxBJpGLuxRU2ihrQk4IZ+ia5nffobG6sOFUPW5PMYkF0kQtxEbeDuCa69Xif211vUOcxlOnf5IOEIpTEySA== + +"@rollup/rollup-linux-x64-gnu@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.2.0.tgz#30fb4756ebf5c7e059a12bdf42d900c38041e04d" + integrity sha512-3p3iRtQmv2aXw+vtKNyZMLOQ+LSRsqArXjKAh2Oj9cqwfIRe7OXvdkOzWfZOIp1F/x5KJzVAxGxnniF4cMbnsQ== + +"@rollup/rollup-linux-x64-musl@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.2.0.tgz#e5d4f4529142ba7873071d4661d37eb50e211a45" + integrity sha512-atih7IF/reUZe4LBLC5Izd44hth2tfDIG8LaPp4/cQXdHh9jabcZEvIeRPrpDq0i/Uu487Qu5gl5KwyAnWajnw== + +"@rollup/rollup-win32-arm64-msvc@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.2.0.tgz#0047005faab99cdea50b594f76388ed09ed44650" + integrity sha512-vYxF3tKJeUE4ceYzpNe2p84RXk/fGK30I8frpRfv/MyPStej/mRlojztkN7Jtd1014HHVeq/tYaMBz/3IxkxZw== + +"@rollup/rollup-win32-ia32-msvc@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.2.0.tgz#da08f1d2b0a6c58ca59aea026c2ffc5592ee8194" + integrity sha512-1LZJ6zpl93SaPQvas618bMFarVwufWTaczH4ESAbFcwiC4OtznA6Ym+hFPyIGaJaGEB8uMWWac0uXGPXOg5FGA== + +"@rollup/rollup-win32-x64-msvc@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.2.0.tgz#c01c43de5a3e786b603b37555961870fa5db2e1c" + integrity sha512-dgQfFdHCNg08nM5zBmqxqc9vrm0DVzhWotpavbPa0j4//MAOKZEB75yGAfzQE9fUJ+4pvM1239Y4IhL8f6sSog== + "@solana/buffer-layout-utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" @@ -191,23 +263,23 @@ buffer "^6.0.3" "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0": - version "1.77.3" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.77.3.tgz#2cbeaa1dd24f8fa386ac924115be82354dfbebab" - integrity sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w== + version "1.87.3" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.87.3.tgz#36871af8d41221d34bfefcf897f158e1793b3356" + integrity sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ== dependencies: - "@babel/runtime" "^7.12.5" - "@noble/curves" "^1.0.0" - "@noble/hashes" "^1.3.0" + "@babel/runtime" "^7.23.2" + "@noble/curves" "^1.2.0" + "@noble/hashes" "^1.3.1" "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.2.1" + agentkeepalive "^4.3.0" bigint-buffer "^1.1.5" - bn.js "^5.0.0" + bn.js "^5.2.1" borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.3" fast-stable-stringify "^1.0.0" jayson "^4.1.0" - node-fetch "^2.6.7" + node-fetch "^2.6.12" rpc-websockets "^7.5.1" superstruct "^0.14.2" @@ -239,16 +311,16 @@ "@types/node" "*" "@types/eslint-plugin-prettier@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#451b5e1e5f148a38dc41e9c5b61d45cd2e97af2c" - integrity sha512-6/UIuz99F0IvtDez4U3bRwAmN4VKnuw10Ibblf0iZhtNbmbonMSLqs/qqsXrGIAWvjy+vXqYwOljgtLhrETSMg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#3a169730b60e7c94aace778640ebc32726c648bd" + integrity sha512-+93WHgRJxdsNU+nhBJYs/o72QTVxAxrvIRDf13u0D4wLERt7Ab4Y5ezc3lh3oOSO8VUbnqdgG2m5U0AT77eFfg== dependencies: "@types/eslint" "*" "@types/eslint@*", "@types/eslint@^8.4.1": - version "8.40.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.2.tgz#2833bc112d809677864a4b0e7d1de4f04d7dac2d" - integrity sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ== + version "8.44.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.6.tgz#60e564551966dd255f4c01c459f0b4fb87068603" + integrity sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -264,19 +336,23 @@ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/node@*", "@types/node@^20.1.4": - version "20.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6" - integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw== + version "20.8.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e" + integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w== + dependencies: + undici-types "~5.26.4" "@types/node@^12.12.54": version "12.20.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== -"@types/prettier@^2.4.4": - version "2.7.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" - integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== +"@types/prettier@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-3.0.0.tgz#e9bc8160230d3a461dab5c5b41cceef1ef723057" + integrity sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA== + dependencies: + prettier "*" "@types/resolve@1.20.2": version "1.20.2" @@ -296,89 +372,94 @@ "@types/node" "*" "@typescript-eslint/eslint-plugin@^5.16.0": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz#81382d6ecb92b8dda70e91f9035611cb2fecd1c3" - integrity sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/type-utils" "5.60.1" - "@typescript-eslint/utils" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.16.0": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.60.1.tgz#0f2f58209c0862a73e3d5a56099abfdfa21d0fd3" - integrity sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q== + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/typescript-estree" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz#35abdb47f500c68c08f2f2b4f22c7c79472854bb" - integrity sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/visitor-keys" "5.60.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz#17770540e98d65ab4730c7aac618003f702893f4" - integrity sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.60.1" - "@typescript-eslint/utils" "5.60.1" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.60.1.tgz#a17473910f6b8d388ea83c9d7051af89c4eb7561" - integrity sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz#8c71824b7165b64d5ebd7aa42968899525959834" - integrity sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/visitor-keys" "5.60.1" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.60.1.tgz#6861ebedbefba1ac85482d2bdef6f2ff1eb65b80" - integrity sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.60.1" - "@typescript-eslint/types" "5.60.1" - "@typescript-eslint/typescript-estree" "5.60.1" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.60.1": - version "5.60.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz#19a877358bf96318ec35d90bfe6bd1445cce9434" - integrity sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.60.1" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -407,16 +488,14 @@ acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== -agentkeepalive@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== +agentkeepalive@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: - debug "^4.1.0" - depd "^1.1.2" humanize-ms "^1.2.1" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -492,6 +571,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" @@ -500,9 +584,9 @@ bigint-buffer@^1.1.5: bindings "^1.3.0" bignumber.js@^9.0.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" - integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== bindings@^1.3.0: version "1.5.0" @@ -511,7 +595,7 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.0.0, bn.js@^5.2.0: +bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -525,6 +609,13 @@ borsh@^0.7.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -574,6 +665,13 @@ builtin-modules@^3.3.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -599,7 +697,12 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -commander@^2.18.0, commander@^2.20.3: +commander@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" + integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== + +commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -619,7 +722,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.2: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -628,7 +731,7 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -645,16 +748,34 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -701,17 +822,18 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-prettier@^8.5.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== -eslint-plugin-prettier@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== +eslint-plugin-prettier@^5.0.0-alpha.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz#a3b399f04378f79f066379f544e42d6b73f11515" + integrity sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg== dependencies: prettier-linter-helpers "^1.0.0" + synckit "^0.8.5" eslint-scope@^5.1.1: version "5.1.1" @@ -721,40 +843,41 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.12.0: - version "8.44.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" - integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + version "8.52.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc" + integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.1.0" - "@eslint/js" "8.44.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.52.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.6.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -764,7 +887,6 @@ eslint@^8.12.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -776,13 +898,12 @@ eslint@^8.12.0: natural-compare "^1.4.0" optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" - integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: acorn "^8.9.0" acorn-jsx "^5.3.2" @@ -827,6 +948,36 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + eyes@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" @@ -853,6 +1004,17 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" + integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -946,14 +1108,14 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs.realpath@^1.0.0: version "1.0.0" @@ -970,17 +1132,22 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -gh-pages@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-5.0.0.tgz#e0893272a0e33f0453e53a3c017c33b91ddd6394" - integrity sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ== +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +gh-pages@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-6.0.0.tgz#3bb46ea13dc7cee306662db0d3f02bf05635cdc1" + integrity sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g== dependencies: async "^3.2.4" - commander "^2.18.0" + commander "^11.0.0" email-addresses "^5.0.0" filenamify "^4.3.0" find-cache-dir "^3.3.1" - fs-extra "^8.1.0" + fs-extra "^11.1.1" globby "^6.1.0" glob-parent@^5.1.2: @@ -1055,11 +1222,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -1077,6 +1239,16 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -1094,7 +1266,7 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -1134,6 +1306,16 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1146,6 +1328,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -1168,6 +1357,23 @@ is-reference@1.2.1: dependencies: "@types/estree" "*" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1223,10 +1429,12 @@ jsonc-parser@^3.2.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" @@ -1274,12 +1482,12 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -magic-string@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" - integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== +magic-string@^0.30.3: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" + "@jridgewell/sourcemap-codec" "^1.4.15" make-dir@^3.0.2: version "3.1.0" @@ -1298,6 +1506,11 @@ marked@^4.3.0: resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1311,6 +1524,16 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1325,10 +1548,10 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" - integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== +minimatch@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" @@ -1352,10 +1575,10 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -node-fetch@^2.6.7: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== dependencies: whatwg-url "^5.0.0" @@ -1364,6 +1587,20 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1376,6 +1613,30 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -1438,11 +1699,16 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -1453,6 +1719,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -1499,10 +1770,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.6.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@*, prettier@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== punycode@^2.1.0: version "2.1.1" @@ -1514,10 +1785,10 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== resolve-from@^4.0.0: version "4.0.0" @@ -1545,11 +1816,23 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^3.22.0: - version "3.26.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.0.tgz#9f2e0316a4ca641911cefd8515c562a9124e6130" - integrity sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg== +rollup@^4.0.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.2.0.tgz#c726e4004ebf1c7f74dc7b7d51d3a34db84e4f95" + integrity sha512-deaMa9Z+jPVeBD2dKXv+h7EbdKte9++V2potc/ADqvVgEr6DEJ3ia9u0joarjC2lX/ubaCRYz3QVx0TzuVqAJA== optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.2.0" + "@rollup/rollup-android-arm64" "4.2.0" + "@rollup/rollup-darwin-arm64" "4.2.0" + "@rollup/rollup-darwin-x64" "4.2.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.2.0" + "@rollup/rollup-linux-arm64-gnu" "4.2.0" + "@rollup/rollup-linux-arm64-musl" "4.2.0" + "@rollup/rollup-linux-x64-gnu" "4.2.0" + "@rollup/rollup-linux-x64-musl" "4.2.0" + "@rollup/rollup-win32-arm64-msvc" "4.2.0" + "@rollup/rollup-win32-ia32-msvc" "4.2.0" + "@rollup/rollup-win32-x64-msvc" "4.2.0" fsevents "~2.3.2" rpc-websockets@^7.5.1: @@ -1565,6 +1848,13 @@ rpc-websockets@^7.5.1: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -1578,14 +1868,14 @@ safe-buffer@^5.0.1: integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== semver@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.7: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -1611,6 +1901,11 @@ shiki@^0.14.1: vscode-oniguruma "^1.7.0" vscode-textmate "^8.0.0" +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -1623,7 +1918,17 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -1652,6 +1957,14 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" @@ -1667,6 +1980,11 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -1710,10 +2028,10 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.3.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" - integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== +tslib@^2.3.0, tslib@^2.5.0, tslib@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tsutils@^3.21.0: version "3.21.0" @@ -1734,25 +2052,35 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -typedoc@^0.24.7: - version "0.24.8" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.8.tgz#cce9f47ba6a8d52389f5e583716a2b3b4335b63e" - integrity sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w== +typedoc@^0.25.0: + version "0.25.3" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.3.tgz#53c6d668e1001b3d488e9a750fcdfb05433554c0" + integrity sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw== dependencies: lunr "^2.3.9" marked "^4.3.0" - minimatch "^9.0.0" + minimatch "^9.0.3" shiki "^0.14.1" typescript@^5.0.4: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== uri-js@^4.2.2: version "4.4.1" diff --git a/token-lending/program/Cargo.toml b/token-lending/program/Cargo.toml index 206f09f5d3e..7e7d9f36ff0 100644 --- a/token-lending/program/Cargo.toml +++ b/token-lending/program/Cargo.toml @@ -13,19 +13,19 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.13.1" +bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } thiserror = "1.0" uint = "0.9" [dev-dependencies] assert_matches = "1.5.0" -proptest = "1.2" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +proptest = "1.3" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-lending/program/src/lib.rs b/token-lending/program/src/lib.rs index 133205fb5d9..55de39ea7b3 100644 --- a/token-lending/program/src/lib.rs +++ b/token-lending/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] //! A lending program for the Solana blockchain. diff --git a/token-lending/program/tests/borrow_obligation_liquidity.rs b/token-lending/program/tests/borrow_obligation_liquidity.rs index 139f1834950..410d68926c0 100644 --- a/token-lending/program/tests/borrow_obligation_liquidity.rs +++ b/token-lending/program/tests/borrow_obligation_liquidity.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/deposit_obligation_collateral.rs b/token-lending/program/tests/deposit_obligation_collateral.rs index de125b5d5e6..0e44cb6bb55 100644 --- a/token-lending/program/tests/deposit_obligation_collateral.rs +++ b/token-lending/program/tests/deposit_obligation_collateral.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/deposit_reserve_liquidity.rs b/token-lending/program/tests/deposit_reserve_liquidity.rs index 1b7be4236d9..828a54ba816 100644 --- a/token-lending/program/tests/deposit_reserve_liquidity.rs +++ b/token-lending/program/tests/deposit_reserve_liquidity.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/flash_loan.rs b/token-lending/program/tests/flash_loan.rs index 8ab300ddcd9..0c59c948363 100644 --- a/token-lending/program/tests/flash_loan.rs +++ b/token-lending/program/tests/flash_loan.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/helpers/mod.rs b/token-lending/program/tests/helpers/mod.rs index 06da8328a73..baea93c9221 100644 --- a/token-lending/program/tests/helpers/mod.rs +++ b/token-lending/program/tests/helpers/mod.rs @@ -46,9 +46,9 @@ pub const TEST_RESERVE_CONFIG: ReserveConfig = ReserveConfig { optimal_borrow_rate: 4, max_borrow_rate: 30, fees: ReserveFees { - /// 0.00001% (Aave borrow fee) + // 0.00001% (Aave borrow fee) borrow_fee_wad: 100_000_000_000, - /// 0.3% (Aave flash loan fee) + // 0.3% (Aave flash loan fee) flash_loan_fee_wad: 3_000_000_000_000_000, host_fee_percentage: 20, }, @@ -1110,7 +1110,7 @@ pub fn add_oracle( panic!("Unable to locate {}", filename); })); - let mut pyth_price = pyth::load_mut::(pyth_price_data.as_mut_slice()).unwrap(); + let pyth_price = pyth::load_mut::(pyth_price_data.as_mut_slice()).unwrap(); let decimals = 10u64 .checked_pow(pyth_price.expo.checked_abs().unwrap().try_into().unwrap()) diff --git a/token-lending/program/tests/init_lending_market.rs b/token-lending/program/tests/init_lending_market.rs index f02610e7079..659ce8ff7c6 100644 --- a/token-lending/program/tests/init_lending_market.rs +++ b/token-lending/program/tests/init_lending_market.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/init_obligation.rs b/token-lending/program/tests/init_obligation.rs index 26b4e13255d..365395b8004 100644 --- a/token-lending/program/tests/init_obligation.rs +++ b/token-lending/program/tests/init_obligation.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/init_reserve.rs b/token-lending/program/tests/init_reserve.rs index 16c752fa4b2..ff81ba77b2b 100644 --- a/token-lending/program/tests/init_reserve.rs +++ b/token-lending/program/tests/init_reserve.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/liquidate_obligation.rs b/token-lending/program/tests/liquidate_obligation.rs index 457573f8bf4..82c79f86321 100644 --- a/token-lending/program/tests/liquidate_obligation.rs +++ b/token-lending/program/tests/liquidate_obligation.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/modify_reserve_config.rs b/token-lending/program/tests/modify_reserve_config.rs index ea79255ea00..4386baf1dac 100644 --- a/token-lending/program/tests/modify_reserve_config.rs +++ b/token-lending/program/tests/modify_reserve_config.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/obligation_end_to_end.rs b/token-lending/program/tests/obligation_end_to_end.rs index 468c569a0f6..badb9450378 100644 --- a/token-lending/program/tests/obligation_end_to_end.rs +++ b/token-lending/program/tests/obligation_end_to_end.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/redeem_reserve_collateral.rs b/token-lending/program/tests/redeem_reserve_collateral.rs index 0ea0979d956..a72cacd4e84 100644 --- a/token-lending/program/tests/redeem_reserve_collateral.rs +++ b/token-lending/program/tests/redeem_reserve_collateral.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/refresh_obligation.rs b/token-lending/program/tests/refresh_obligation.rs index 3af9d4f6681..cb2b20cf7e7 100644 --- a/token-lending/program/tests/refresh_obligation.rs +++ b/token-lending/program/tests/refresh_obligation.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/refresh_reserve.rs b/token-lending/program/tests/refresh_reserve.rs index 23e57d593b8..0b73943188c 100644 --- a/token-lending/program/tests/refresh_reserve.rs +++ b/token-lending/program/tests/refresh_reserve.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/repay_obligation_liquidity.rs b/token-lending/program/tests/repay_obligation_liquidity.rs index 5a55b285570..1fe9e6275b2 100644 --- a/token-lending/program/tests/repay_obligation_liquidity.rs +++ b/token-lending/program/tests/repay_obligation_liquidity.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/set_lending_market_owner.rs b/token-lending/program/tests/set_lending_market_owner.rs index e5bb9b6efd9..40315b85d90 100644 --- a/token-lending/program/tests/set_lending_market_owner.rs +++ b/token-lending/program/tests/set_lending_market_owner.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-lending/program/tests/withdraw_obligation_collateral.rs b/token-lending/program/tests/withdraw_obligation_collateral.rs index 227737e906c..1e68b5d80e1 100644 --- a/token-lending/program/tests/withdraw_obligation_collateral.rs +++ b/token-lending/program/tests/withdraw_obligation_collateral.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![cfg(feature = "test-sbf")] mod helpers; diff --git a/token-metadata/example/Cargo.toml b/token-metadata/example/Cargo.toml index 0e601cf9be8..acc64649bec 100644 --- a/token-metadata/example/Cargo.toml +++ b/token-metadata/example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token-metadata-example" -version = "0.1.0" +version = "0.2.0" description = "Solana Program Library Token Metadata Example Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -12,16 +12,17 @@ no-entrypoint = [] test-sbf = [] [dependencies] -solana-program = "1.16.1" -spl-token-2022 = { version = "0.7", path = "../../token/program-2022" } -spl-token-metadata-interface = { version = "0.1.0", path = "../interface" } -spl-type-length-value = { version = "0.2.0" , path = "../../libraries/type-length-value", features = ["borsh"] } +solana-program = "1.17.2" +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-metadata-interface = { version = "0.2.0", path = "../interface" } +spl-type-length-value = { version = "0.3.0" , path = "../../libraries/type-length-value" } +spl-pod = { version = "0.1.0", path = "../../libraries/pod" } [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -spl-token-client = { version = "0.5", path = "../../token/client" } -test-case = "3.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +spl-token-client = { version = "0.8", path = "../../token/client" } +test-case = "3.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-metadata/example/src/lib.rs b/token-metadata/example/src/lib.rs index e0271444f96..db86409dad6 100644 --- a/token-metadata/example/src/lib.rs +++ b/token-metadata/example/src/lib.rs @@ -1,6 +1,6 @@ //! Crate defining an example program for storing SPL token metadata -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/token-metadata/example/src/processor.rs b/token-metadata/example/src/processor.rs index b770761a996..1ff45bf12e3 100644 --- a/token-metadata/example/src/processor.rs +++ b/token-metadata/example/src/processor.rs @@ -3,7 +3,7 @@ use { solana_program::{ account_info::{next_account_info, AccountInfo}, - borsh::get_instance_packed_len, + borsh0_10::get_instance_packed_len, entrypoint::ProgramResult, msg, program::set_return_data, @@ -11,16 +11,17 @@ use { program_option::COption, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, spl_token_2022::{extension::StateWithExtensions, state::Mint}, spl_token_metadata_interface::{ error::TokenMetadataError, instruction::{ Emit, Initialize, RemoveKey, TokenMetadataInstruction, UpdateAuthority, UpdateField, }, - state::{OptionalNonZeroPubkey, TokenMetadata}, + state::TokenMetadata, }, spl_type_length_value::state::{ - realloc_and_borsh_serialize, TlvState, TlvStateBorrowed, TlvStateMut, + realloc_and_pack_first_variable_len, TlvState, TlvStateBorrowed, TlvStateMut, }, }; @@ -31,7 +32,7 @@ fn check_update_authority( if !update_authority_info.is_signer { return Err(ProgramError::MissingRequiredSignature); } - let update_authority = Option::::from(expected_update_authority.clone()) + let update_authority = Option::::from(*expected_update_authority) .ok_or(TokenMetadataError::ImmutableMetadata)?; if update_authority != *update_authority_info.key { return Err(TokenMetadataError::IncorrectUpdateAuthority.into()); @@ -83,8 +84,8 @@ pub fn process_initialize( // allocate a TLV entry for the space and write it in let mut buffer = metadata_info.try_borrow_mut_data()?; let mut state = TlvStateMut::unpack(&mut buffer)?; - state.alloc::(instance_size)?; - state.borsh_serialize(&token_metadata)?; + state.alloc::(instance_size, false)?; + state.pack_first_variable_len_value(&token_metadata)?; Ok(()) } @@ -104,7 +105,7 @@ pub fn process_update_field( let mut token_metadata = { let buffer = metadata_info.try_borrow_data()?; let state = TlvStateBorrowed::unpack(&buffer)?; - state.borsh_deserialize::()? + state.get_first_variable_len_value::()? }; check_update_authority(update_authority_info, &token_metadata.update_authority)?; @@ -113,7 +114,7 @@ pub fn process_update_field( token_metadata.update(data.field, data.value); // Update / realloc the account - realloc_and_borsh_serialize(metadata_info, &token_metadata)?; + realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?; Ok(()) } @@ -133,14 +134,14 @@ pub fn process_remove_key( let mut token_metadata = { let buffer = metadata_info.try_borrow_data()?; let state = TlvStateBorrowed::unpack(&buffer)?; - state.borsh_deserialize::()? + state.get_first_variable_len_value::()? }; check_update_authority(update_authority_info, &token_metadata.update_authority)?; if !token_metadata.remove_key(&data.key) && !data.idempotent { return Err(TokenMetadataError::KeyNotFound.into()); } - realloc_and_borsh_serialize(metadata_info, &token_metadata)?; + realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?; Ok(()) } @@ -160,13 +161,13 @@ pub fn process_update_authority( let mut token_metadata = { let buffer = metadata_info.try_borrow_data()?; let state = TlvStateBorrowed::unpack(&buffer)?; - state.borsh_deserialize::()? + state.get_first_variable_len_value::()? }; check_update_authority(update_authority_info, &token_metadata.update_authority)?; token_metadata.update_authority = data.new_authority; // Update the account, no realloc needed! - realloc_and_borsh_serialize(metadata_info, &token_metadata)?; + realloc_and_pack_first_variable_len(metadata_info, &token_metadata)?; Ok(()) } @@ -182,7 +183,7 @@ pub fn process_emit(program_id: &Pubkey, accounts: &[AccountInfo], data: Emit) - let buffer = metadata_info.try_borrow_data()?; let state = TlvStateBorrowed::unpack(&buffer)?; - let metadata_bytes = state.get_bytes::()?; + let metadata_bytes = state.get_first_bytes::()?; if let Some(range) = TokenMetadata::get_slice(metadata_bytes, data.start, data.end) { set_return_data(range); diff --git a/token-metadata/example/tests/emit.rs b/token-metadata/example/tests/emit.rs index c311b733cc5..00ef924d527 100644 --- a/token-metadata/example/tests/emit.rs +++ b/token-metadata/example/tests/emit.rs @@ -5,7 +5,7 @@ use { program_test::{setup, setup_metadata, setup_mint}, solana_program_test::tokio, solana_sdk::{ - borsh::try_from_slice_unchecked, program::MAX_RETURN_DATA, pubkey::Pubkey, + borsh0_10::try_from_slice_unchecked, program::MAX_RETURN_DATA, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, transaction::Transaction, }, spl_token_metadata_interface::{ diff --git a/token-metadata/example/tests/initialize.rs b/token-metadata/example/tests/initialize.rs index 41909d1cbf5..ccd50373e84 100644 --- a/token-metadata/example/tests/initialize.rs +++ b/token-metadata/example/tests/initialize.rs @@ -76,7 +76,7 @@ async fn success_initialize() { .unwrap(); let fetched_metadata_state = TlvStateBorrowed::unpack(&fetched_metadata_account.data).unwrap(); let fetched_metadata = fetched_metadata_state - .borsh_deserialize::() + .get_first_variable_len_value::() .unwrap(); assert_eq!(fetched_metadata, token_metadata); diff --git a/token-metadata/example/tests/program_test.rs b/token-metadata/example/tests/program_test.rs index 27ad8f2a31e..19e91eea18d 100644 --- a/token-metadata/example/tests/program_test.rs +++ b/token-metadata/example/tests/program_test.rs @@ -9,7 +9,7 @@ use { spl_token_client::{ client::{ ProgramBanksClient, ProgramBanksClientProcessTransaction, ProgramClient, - SendTransaction, + SendTransaction, SimulateTransaction, }, token::Token, }, @@ -56,7 +56,7 @@ pub async fn setup( (context, client, payer) } -pub async fn setup_mint( +pub async fn setup_mint( program_id: &Pubkey, mint_authority: &Pubkey, decimals: u8, @@ -101,7 +101,7 @@ pub async fn setup_metadata( initialize( metadata_program_id, &metadata_keypair.pubkey(), - &Option::::from(token_metadata.update_authority.clone()).unwrap(), + &Option::::from(token_metadata.update_authority).unwrap(), mint, &mint_authority.pubkey(), token_metadata.name.clone(), diff --git a/token-metadata/example/tests/remove_key.rs b/token-metadata/example/tests/remove_key.rs index def5086f6f3..505113fedd3 100644 --- a/token-metadata/example/tests/remove_key.rs +++ b/token-metadata/example/tests/remove_key.rs @@ -111,7 +111,7 @@ async fn success_remove() { ); let fetched_metadata_state = TlvStateBorrowed::unpack(&fetched_metadata_account.data).unwrap(); let fetched_metadata = fetched_metadata_state - .borsh_deserialize::() + .get_first_variable_len_value::() .unwrap(); assert_eq!(fetched_metadata, token_metadata); diff --git a/token-metadata/example/tests/update_authority.rs b/token-metadata/example/tests/update_authority.rs index 92653a9b27a..cb2251b8d30 100644 --- a/token-metadata/example/tests/update_authority.rs +++ b/token-metadata/example/tests/update_authority.rs @@ -11,10 +11,9 @@ use { signer::keypair::Keypair, transaction::{Transaction, TransactionError}, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, spl_token_metadata_interface::{ - error::TokenMetadataError, - instruction::update_authority, - state::{OptionalNonZeroPubkey, TokenMetadata}, + error::TokenMetadataError, instruction::update_authority, state::TokenMetadata, }, spl_type_length_value::state::{TlvState, TlvStateBorrowed}, }; @@ -68,14 +67,14 @@ async fn success_update() { let new_update_authority = Keypair::new(); let new_update_authority_pubkey = OptionalNonZeroPubkey::try_from(Some(new_update_authority.pubkey())).unwrap(); - token_metadata.update_authority = new_update_authority_pubkey.clone(); + token_metadata.update_authority = new_update_authority_pubkey; let transaction = Transaction::new_signed_with_payer( &[update_authority( &program_id, &metadata_pubkey, &authority.pubkey(), - new_update_authority_pubkey.clone(), + new_update_authority_pubkey, )], Some(&payer.pubkey()), &[&payer, &authority], @@ -100,7 +99,7 @@ async fn success_update() { ); let fetched_metadata_state = TlvStateBorrowed::unpack(&fetched_metadata_account.data).unwrap(); let fetched_metadata = fetched_metadata_state - .borsh_deserialize::() + .get_first_variable_len_value::() .unwrap(); assert_eq!(fetched_metadata, token_metadata); @@ -135,7 +134,7 @@ async fn success_update() { ); let fetched_metadata_state = TlvStateBorrowed::unpack(&fetched_metadata_account.data).unwrap(); let fetched_metadata = fetched_metadata_state - .borsh_deserialize::() + .get_first_variable_len_value::() .unwrap(); assert_eq!(fetched_metadata, token_metadata); diff --git a/token-metadata/example/tests/update_field.rs b/token-metadata/example/tests/update_field.rs index c5f44ae5f32..d44356df91b 100644 --- a/token-metadata/example/tests/update_field.rs +++ b/token-metadata/example/tests/update_field.rs @@ -1,4 +1,5 @@ #![cfg(feature = "test-sbf")] +#![allow(clippy::items_after_test_module)] mod program_test; use { @@ -144,7 +145,7 @@ async fn success_update(field: Field, value: String) { ); let fetched_metadata_state = TlvStateBorrowed::unpack(&fetched_metadata_account.data).unwrap(); let fetched_metadata = fetched_metadata_state - .borsh_deserialize::() + .get_first_variable_len_value::() .unwrap(); assert_eq!(fetched_metadata, token_metadata); } diff --git a/token-metadata/interface/Cargo.toml b/token-metadata/interface/Cargo.toml index aa10e39d324..d484ba32be3 100644 --- a/token-metadata/interface/Cargo.toml +++ b/token-metadata/interface/Cargo.toml @@ -1,18 +1,26 @@ [package] name = "spl-token-metadata-interface" -version = "0.1.0" +version = "0.2.0" description = "Solana Program Library Token Metadata Interface" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" license = "Apache-2.0" edition = "2021" +[features] +serde-traits = ["dep:serde", "spl-pod/serde-traits"] + [dependencies] borsh = "0.10" -solana-program = "1.16.1" -spl-discriminator = { version = "0.1.0" , path = "../../libraries/discriminator" } -spl-program-error = { version = "0.2.0" , path = "../../libraries/program-error" } -spl-type-length-value = { version = "0.2.0", features = ["borsh"], path = "../../libraries/type-length-value" } +serde = { version = "1.0.190", optional = true } +solana-program = "1.17.2" +spl-discriminator = { version = "0.1" , path = "../../libraries/discriminator" } +spl-program-error = { version = "0.3" , path = "../../libraries/program-error" } +spl-type-length-value = { version = "0.3", path = "../../libraries/type-length-value" } +spl-pod = { version = "0.1", path = "../../libraries/pod", features = ["borsh"] } + +[dev-dependencies] +serde_json = "1.0.108" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-metadata/interface/src/error.rs b/token-metadata/interface/src/error.rs index a0ebd0b64f3..3f5b2437865 100644 --- a/token-metadata/interface/src/error.rs +++ b/token-metadata/interface/src/error.rs @@ -3,7 +3,7 @@ use spl_program_error::*; /// Errors that may be returned by the interface. -#[spl_program_error] +#[spl_program_error(hash_error_code_start = 901_952_957)] pub enum TokenMetadataError { /// Incorrect account provided #[error("Incorrect account provided")] diff --git a/token-metadata/interface/src/instruction.rs b/token-metadata/interface/src/instruction.rs index 10e5831ccff..30865d391a2 100644 --- a/token-metadata/interface/src/instruction.rs +++ b/token-metadata/interface/src/instruction.rs @@ -1,7 +1,7 @@ //! Instruction types use { - crate::state::{Field, OptionalNonZeroPubkey}, + crate::state::Field, borsh::{BorshDeserialize, BorshSerialize}, solana_program::{ instruction::{AccountMeta, Instruction}, @@ -9,9 +9,15 @@ use { pubkey::Pubkey, }, spl_discriminator::{discriminator::ArrayDiscriminator, SplDiscriminate}, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Initialization instruction data +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)] #[discriminator_hash_input("spl_token_metadata_interface:initialize_account")] pub struct Initialize { @@ -24,6 +30,8 @@ pub struct Initialize { } /// Update field instruction data +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)] #[discriminator_hash_input("spl_token_metadata_interface:updating_field")] pub struct UpdateField { @@ -34,6 +42,8 @@ pub struct UpdateField { } /// Remove key instruction data +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)] #[discriminator_hash_input("spl_token_metadata_interface:remove_key_ix")] pub struct RemoveKey { @@ -46,6 +56,8 @@ pub struct RemoveKey { /// Update authority instruction data #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[discriminator_hash_input("spl_token_metadata_interface:update_the_authority")] pub struct UpdateAuthority { /// New authority for the token metadata, or unset if `None` @@ -53,6 +65,8 @@ pub struct UpdateAuthority { } /// Instruction data for Emit +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)] #[discriminator_hash_input("spl_token_metadata_interface:emitter")] pub struct Emit { @@ -63,6 +77,8 @@ pub struct Emit { } /// All instructions that must be implemented in the token-metadata interface +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq)] pub enum TokenMetadataInstruction { /// Initializes a TLV entry with the basic token-metadata fields. @@ -306,6 +322,9 @@ pub fn emit( mod test { use {super::*, crate::NAMESPACE, solana_program::hash}; + #[cfg(feature = "serde-traits")] + use std::str::FromStr; + fn check_pack_unpack( instruction: TokenMetadataInstruction, discriminator: &[u8], @@ -384,4 +403,104 @@ mod test { let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; check_pack_unpack(check, discriminator, data); } + + #[cfg(feature = "serde-traits")] + #[test] + fn initialize_serde() { + let data = Initialize { + name: "Token Name".to_string(), + symbol: "TST".to_string(), + uri: "uri.test".to_string(), + }; + let ix = TokenMetadataInstruction::Initialize(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = + "{\"initialize\":{\"name\":\"Token Name\",\"symbol\":\"TST\",\"uri\":\"uri.test\"}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn update_field_serde() { + let data = UpdateField { + field: Field::Key("MyField".to_string()), + value: "my field value".to_string(), + }; + let ix = TokenMetadataInstruction::UpdateField(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = + "{\"updateField\":{\"field\":{\"key\":\"MyField\"},\"value\":\"my field value\"}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn remove_key_serde() { + let data = RemoveKey { + key: "MyTestField".to_string(), + idempotent: true, + }; + let ix = TokenMetadataInstruction::RemoveKey(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = "{\"removeKey\":{\"idempotent\":true,\"key\":\"MyTestField\"}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn update_authority_serde() { + let update_authority_option: Option = + Some(Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap()); + let update_authority: OptionalNonZeroPubkey = update_authority_option.try_into().unwrap(); + let data = UpdateAuthority { + new_authority: update_authority, + }; + let ix = TokenMetadataInstruction::UpdateAuthority(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = "{\"updateAuthority\":{\"newAuthority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\"}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn update_authority_serde_with_none() { + let data = UpdateAuthority { + new_authority: OptionalNonZeroPubkey::default(), + }; + let ix = TokenMetadataInstruction::UpdateAuthority(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = "{\"updateAuthority\":{\"newAuthority\":null}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } + + #[cfg(feature = "serde-traits")] + #[test] + fn emit_serde() { + let data = Emit { + start: None, + end: Some(10), + }; + let ix = TokenMetadataInstruction::Emit(data); + let serialized = serde_json::to_string(&ix).unwrap(); + let serialized_expected = "{\"emit\":{\"start\":null,\"end\":10}}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::(&serialized).unwrap(); + assert_eq!(ix, deserialized); + } } diff --git a/token-metadata/interface/src/lib.rs b/token-metadata/interface/src/lib.rs index b668ba03074..5f1ec043e13 100644 --- a/token-metadata/interface/src/lib.rs +++ b/token-metadata/interface/src/lib.rs @@ -1,6 +1,6 @@ //! Crate defining an interface for token-metadata -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/token-metadata/interface/src/state.rs b/token-metadata/interface/src/state.rs index 24289bc1c31..08e54291bbb 100644 --- a/token-metadata/interface/src/state.rs +++ b/token-metadata/interface/src/state.rs @@ -2,41 +2,21 @@ use { borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, - solana_program::{borsh::get_instance_packed_len, program_error::ProgramError, pubkey::Pubkey}, + solana_program::{ + borsh0_10::{get_instance_packed_len, try_from_slice_unchecked}, + program_error::ProgramError, + pubkey::Pubkey, + }, spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, - spl_type_length_value::state::{TlvState, TlvStateBorrowed}, - std::convert::TryFrom, + spl_pod::optional_keys::OptionalNonZeroPubkey, + spl_type_length_value::{ + state::{TlvState, TlvStateBorrowed}, + variable_len_pack::VariableLenPack, + }, }; -/// A Pubkey that encodes `None` as all `0`, meant to be usable as a Pod type, -/// similar to all NonZero* number types from the bytemuck library. -#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -#[repr(transparent)] -pub struct OptionalNonZeroPubkey(Pubkey); -impl TryFrom> for OptionalNonZeroPubkey { - type Error = ProgramError; - fn try_from(p: Option) -> Result { - match p { - None => Ok(Self(Pubkey::default())), - Some(pubkey) => { - if pubkey == Pubkey::default() { - Err(ProgramError::InvalidArgument) - } else { - Ok(Self(pubkey)) - } - } - } - } -} -impl From for Option { - fn from(p: OptionalNonZeroPubkey) -> Self { - if p.0 == Pubkey::default() { - None - } else { - Some(p.0) - } - } -} +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; /// Data struct for all token-metadata, stored in a TLV entry /// @@ -117,8 +97,21 @@ impl TokenMetadata { data.get(start..end) } } +impl VariableLenPack for TokenMetadata { + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + borsh::to_writer(&mut dst[..], self).map_err(Into::into) + } + fn unpack_from_slice(src: &[u8]) -> Result { + try_from_slice_unchecked(src).map_err(Into::into) + } + fn get_packed_len(&self) -> Result { + get_instance_packed_len(self).map_err(Into::into) + } +} /// Fields in the metadata account, used for updating +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)] pub enum Field { /// The name field, corresponding to `TokenMetadata.name` diff --git a/token-metadata/js/.eslintignore b/token-metadata/js/.eslintignore new file mode 100644 index 00000000000..6da325effab --- /dev/null +++ b/token-metadata/js/.eslintignore @@ -0,0 +1,5 @@ +docs +lib +test-ledger + +package-lock.json diff --git a/token-metadata/js/.eslintrc b/token-metadata/js/.eslintrc new file mode 100644 index 00000000000..5aef10a4729 --- /dev/null +++ b/token-metadata/js/.eslintrc @@ -0,0 +1,34 @@ +{ + "root": true, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "plugin:require-extensions/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint", + "prettier", + "require-extensions" + ], + "rules": { + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/consistent-type-imports": "error" + }, + "overrides": [ + { + "files": [ + "examples/**/*", + "test/**/*" + ], + "rules": { + "require-extensions/require-extensions": "off", + "require-extensions/require-index": "off" + } + } + ] +} diff --git a/token-metadata/js/.gitignore b/token-metadata/js/.gitignore new file mode 100644 index 00000000000..21f33db819c --- /dev/null +++ b/token-metadata/js/.gitignore @@ -0,0 +1,13 @@ +.idea +.vscode +.DS_Store + +node_modules + +pnpm-lock.yaml +yarn.lock + +docs +lib +test-ledger +*.tsbuildinfo diff --git a/token-metadata/js/.mocharc.json b/token-metadata/js/.mocharc.json new file mode 100644 index 00000000000..451c14c3016 --- /dev/null +++ b/token-metadata/js/.mocharc.json @@ -0,0 +1,5 @@ +{ + "extension": ["ts"], + "node-option": ["experimental-specifier-resolution=node", "loader=ts-node/esm"], + "timeout": 5000 +} diff --git a/token-metadata/js/.nojekyll b/token-metadata/js/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/token-metadata/js/.prettierignore b/token-metadata/js/.prettierignore new file mode 100644 index 00000000000..6da325effab --- /dev/null +++ b/token-metadata/js/.prettierignore @@ -0,0 +1,5 @@ +docs +lib +test-ledger + +package-lock.json diff --git a/token-metadata/js/.prettierrc b/token-metadata/js/.prettierrc new file mode 100644 index 00000000000..b9ce4c1923a --- /dev/null +++ b/token-metadata/js/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": true +} \ No newline at end of file diff --git a/token-metadata/js/LICENSE b/token-metadata/js/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/token-metadata/js/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/token-metadata/js/README.md b/token-metadata/js/README.md new file mode 100644 index 00000000000..851a95d9035 --- /dev/null +++ b/token-metadata/js/README.md @@ -0,0 +1,61 @@ +# `@solana/spl-token-metadata` + +A TypeScript interface describing the instructions required for a program to implement to be considered a "token-metadata" program for SPL token mints. The interface can be implemented by any program. + +## Links + +- [TypeScript Docs](https://solana-labs.github.io/solana-program-library/token-metadata/js/) +- [FAQs (Frequently Asked Questions)](#faqs) +- [Install](#install) +- [Build from Source](#build-from-source) + +## FAQs + +### How can I get support? + +Please ask questions in the Solana Stack Exchange: https://solana.stackexchange.com/ + +If you've found a bug or you'd like to request a feature, please +[open an issue](https://github.com/solana-labs/solana-program-library/issues/new). + +## Install + +```shell +npm install --save @solana/spl-token-metadata @solana/web3.js +``` +_OR_ +```shell +yarn add @solana/spl-token-metadata @solana/web3.js +``` + +## Build from Source + +0. Prerequisites + +* Node 16+ +* NPM 8+ + +1. Clone the project: +```shell +git clone https://github.com/solana-labs/solana-program-library.git +``` + +2. Navigate to the library: +```shell +cd solana-program-library/token-metadata/js +``` + +3. Install the dependencies: +```shell +npm install +``` + +4. Build the library: +```shell +npm run build +``` + +5. Build the on-chain programs: +```shell +npm run test:build-programs +``` diff --git a/token-metadata/js/package-lock.json b/token-metadata/js/package-lock.json new file mode 100644 index 00000000000..52158371b44 --- /dev/null +++ b/token-metadata/js/package-lock.json @@ -0,0 +1,6493 @@ +{ + "name": "@solana/spl-token-metadata", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@solana/spl-token-metadata", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-data-structures": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396", + "@solana/codecs-strings": "2.0.0-experimental.398c396", + "@solana/options": "2.0.0-experimental.398c396", + "@solana/spl-type-length-value": "0.1.0" + }, + "devDependencies": { + "@solana/web3.js": "^1.47.4", + "@types/chai": "^4.3.3", + "@types/mocha": "^10.0.0", + "@types/node": "^20.8.7", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "chai": "^4.3.6", + "eslint": "^8.20.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-require-extensions": "^0.1.1", + "gh-pages": "^6.0.0", + "mocha": "^10.1.0", + "prettier": "^3.0.0", + "shx": "^0.3.4", + "ts-node": "^10.9.1", + "tslib": "^2.3.1", + "typedoc": "^0.25.0", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.47.4" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@noble/curves": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", + "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "1.3.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "dev": true, + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/codecs-core": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-E3kJzw7K6rkmmTX1tSOdE7Qg/OFqbKiIaaT9V2PTKTVZxwssPrqogUh6aLx0wdzlSJNymy8krKG/FbXsua0FIA==" + }, + "node_modules/@solana/codecs-data-structures": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-gJxLunF1GuBXF8Mz1lfnqGjNMkIrpyTmHgk0g4PyacRCI0MvQr6d3q5oXVHfYLIaRrYPUUxvbt02sqOoOiW2dQ==", + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + } + }, + "node_modules/@solana/codecs-numbers": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-mmUSfO3TOL5kCb/5UCWCz0UTC0KgC864pRe9veCtJbgh5G2YDcBYNJiyKTT661yo/Y+dvrh2hZtYprhB/qxjmw==", + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396" + } + }, + "node_modules/@solana/codecs-strings": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-RGj57x8pDpk5e2hnZ0dguXJlzzVVBUf93VNyp0NGmhm+oWlj5lG4nPinjLo8OunYBBlishFDZZGfCpcqivrDRg==", + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22" + } + }, + "node_modules/@solana/options": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-EyHhCADS1D20zf9fZHozkGcNXAi45bux/AUwBrU0kPBY9RH67IUGxyRwRoWVIYVzsUZNWr0zoqWY5ZS4IZxzJw==", + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + } + }, + "node_modules/@solana/spl-type-length-value": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz", + "integrity": "sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==", + "dependencies": { + "buffer": "^6.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.78.5", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.78.5.tgz", + "integrity": "sha512-2ZHsDNqkKdglJQrIvJ3p2DmgS3cGnary3VJyqt9C1SPrpAtLYzcElr3xyXJOznyQTU/8AMw+GoF11lFoKbicKg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.22.6", + "@noble/curves": "^1.0.0", + "@noble/hashes": "^1.3.1", + "@solana/buffer-layout": "^4.0.0", + "agentkeepalive": "^4.3.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.0", + "node-fetch": "^2.6.12", + "rpc-websockets": "^7.5.1", + "superstruct": "^0.14.2" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "deprecated": "This is a stub types definition. prettier provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "prettier": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base-x": { + "version": "3.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bs58": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bufferutil": { + "version": "4.0.6", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dev": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-require-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-require-extensions/-/eslint-plugin-require-extensions-0.1.3.tgz", + "integrity": "sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true, + "engines": { + "node": "> 0.1.90" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gh-pages": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "dev": true, + "dependencies": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/interpret": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true, + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jayson": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz", + "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==", + "dev": true, + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rpc-websockets": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.5.1.tgz", + "integrity": "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.2", + "eventemitter3": "^4.0.7", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shiki": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", + "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/shx": { + "version": "0.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/superstruct": { + "version": "0.14.2", + "dev": true, + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedoc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.9", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true + }, + "@noble/curves": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", + "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "dev": true, + "requires": { + "@noble/hashes": "1.3.0" + } + }, + "@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, + "@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "dev": true, + "requires": { + "buffer": "~6.0.3" + } + }, + "@solana/codecs-core": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-E3kJzw7K6rkmmTX1tSOdE7Qg/OFqbKiIaaT9V2PTKTVZxwssPrqogUh6aLx0wdzlSJNymy8krKG/FbXsua0FIA==" + }, + "@solana/codecs-data-structures": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-gJxLunF1GuBXF8Mz1lfnqGjNMkIrpyTmHgk0g4PyacRCI0MvQr6d3q5oXVHfYLIaRrYPUUxvbt02sqOoOiW2dQ==", + "requires": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + } + }, + "@solana/codecs-numbers": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-mmUSfO3TOL5kCb/5UCWCz0UTC0KgC864pRe9veCtJbgh5G2YDcBYNJiyKTT661yo/Y+dvrh2hZtYprhB/qxjmw==", + "requires": { + "@solana/codecs-core": "2.0.0-experimental.398c396" + } + }, + "@solana/codecs-strings": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-RGj57x8pDpk5e2hnZ0dguXJlzzVVBUf93VNyp0NGmhm+oWlj5lG4nPinjLo8OunYBBlishFDZZGfCpcqivrDRg==", + "requires": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + } + }, + "@solana/options": { + "version": "2.0.0-experimental.398c396", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-2.0.0-experimental.398c396.tgz", + "integrity": "sha512-EyHhCADS1D20zf9fZHozkGcNXAi45bux/AUwBrU0kPBY9RH67IUGxyRwRoWVIYVzsUZNWr0zoqWY5ZS4IZxzJw==", + "requires": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396" + } + }, + "@solana/spl-type-length-value": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz", + "integrity": "sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==", + "requires": { + "buffer": "^6.0.3" + } + }, + "@solana/web3.js": { + "version": "1.78.5", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.78.5.tgz", + "integrity": "sha512-2ZHsDNqkKdglJQrIvJ3p2DmgS3cGnary3VJyqt9C1SPrpAtLYzcElr3xyXJOznyQTU/8AMw+GoF11lFoKbicKg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.22.6", + "@noble/curves": "^1.0.0", + "@noble/hashes": "^1.3.1", + "@solana/buffer-layout": "^4.0.0", + "agentkeepalive": "^4.3.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.0", + "node-fetch": "^2.6.12", + "rpc-websockets": "^7.5.1", + "superstruct": "^0.14.2" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "dev": true + }, + "borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "dev": true, + "requires": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + } + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "dev": true + }, + "@types/chai": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", + "dev": true + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/mocha": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", + "dev": true + }, + "@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "dev": true, + "requires": { + "undici-types": "~5.25.1" + } + }, + "@types/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "dev": true, + "requires": { + "prettier": "*" + } + }, + "@types/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "dev": true + }, + "@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "dev": true + }, + "agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "dev": true + }, + "ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "dev": true + }, + "base-x": { + "version": "3.0.9", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "bigint-buffer": { + "version": "1.1.5", + "dev": true, + "requires": { + "bindings": "^1.3.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bn.js": { + "version": "5.2.1", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, + "brace-expansion": { + "version": "1.1.11", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bs58": { + "version": "4.0.1", + "dev": true, + "requires": { + "base-x": "^3.0.2" + } + }, + "buffer": { + "version": "6.0.3", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "bufferutil": { + "version": "4.0.6", + "dev": true, + "optional": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "commander": { + "version": "2.20.3", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "dev": true + }, + "eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + } + }, + "eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + } + }, + "eslint-plugin-require-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-require-extensions/-/eslint-plugin-require-extensions-0.1.3.tgz", + "integrity": "sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "dev": true + }, + "fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fast-stable-stringify": { + "version": "1.0.0", + "dev": true + }, + "fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, + "fastq": { + "version": "1.13.0", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "dev": true + }, + "filename-reserved-regex": { + "version": "2.0.0", + "dev": true + }, + "filenamify": { + "version": "4.3.0", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "gh-pages": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", + "dev": true, + "requires": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "dev": true + } + } + }, + "glob": { + "version": "7.2.0", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "ieee754": { + "version": "1.2.1" + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "dev": true + }, + "interpret": { + "version": "1.4.0", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.10.0", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, + "isexe": { + "version": "2.0.0", + "dev": true + }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true, + "requires": {} + }, + "jayson": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz", + "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==", + "dev": true, + "requires": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + } + } + }, + "js-yaml": { + "version": "4.1.0", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "dev": true + }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "dev": true + }, + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-gyp-build": { + "version": "4.5.0", + "dev": true, + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "dev": true + }, + "once": { + "version": "1.4.0", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rpc-websockets": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.5.1.tgz", + "integrity": "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.17.2", + "bufferutil": "^4.0.1", + "eventemitter3": "^4.0.7", + "utf-8-validate": "^5.0.2", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "dependencies": { + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "requires": {} + } + } + }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "dev": true + }, + "shelljs": { + "version": "0.8.5", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shiki": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", + "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", + "dev": true, + "requires": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "shx": { + "version": "0.3.4", + "dev": true, + "requires": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + } + }, + "signal-exit": { + "version": "3.0.7", + "dev": true + }, + "slash": { + "version": "3.0.0", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "dev": true + }, + "strip-outer": { + "version": "1.0.1", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + } + } + }, + "superstruct": { + "version": "0.14.2", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "dev": true + }, + "through": { + "version": "2.3.8", + "dev": true + }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "trim-repeated": { + "version": "1.0.0", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + } + } + }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, + "ts-node": { + "version": "10.9.1", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "dev": true + } + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typedoc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true + }, + "undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "utf-8-validate": { + "version": "5.0.9", + "dev": true, + "optional": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "uuid": { + "version": "8.3.2", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true + }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "dev": true + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yn": { + "version": "3.1.1", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "dev": true + } + } +} diff --git a/token-metadata/js/package.json b/token-metadata/js/package.json new file mode 100644 index 00000000000..7f9d3edead6 --- /dev/null +++ b/token-metadata/js/package.json @@ -0,0 +1,79 @@ +{ + "name": "@solana/spl-token-metadata", + "description": "SPL Token Metadata Interface JS API", + "version": "0.1.0", + "author": "Solana Labs Maintainers ", + "repository": "https://github.com/solana-labs/solana-program-library", + "license": "Apache-2.0", + "type": "module", + "sideEffects": false, + "engines": { + "node": ">=16" + }, + "files": [ + "lib", + "src", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "main": "./lib/cjs/index.js", + "module": "./lib/esm/index.js", + "types": "./lib/types/index.d.ts", + "exports": { + "types": "./lib/types/index.d.ts", + "require": "./lib/cjs/index.js", + "import": "./lib/esm/index.js" + }, + "scripts": { + "build": "tsc --build --verbose tsconfig.all.json", + "clean": "shx rm -rf lib **/*.tsbuildinfo || true", + "deploy": "npm run deploy:docs", + "deploy:docs": "npm run docs && gh-pages --dest token-metadata/js --dist docs --dotfiles", + "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", + "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", + "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", + "lint:fix": "npm run fmt && eslint --fix .", + "nuke": "shx rm -rf node_modules package-lock.json || true", + "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "reinstall": "npm run nuke && npm install", + "release": "npm run clean && npm run build", + "test": "mocha test", + "watch": "tsc --build --verbose --watch tsconfig.all.json" + }, + "peerDependencies": { + "@solana/web3.js": "^1.47.4" + }, + "dependencies": { + "@solana/codecs-core": "2.0.0-experimental.398c396", + "@solana/codecs-data-structures": "2.0.0-experimental.398c396", + "@solana/codecs-numbers": "2.0.0-experimental.398c396", + "@solana/codecs-strings": "2.0.0-experimental.398c396", + "@solana/options": "2.0.0-experimental.398c396", + "@solana/spl-type-length-value": "0.1.0" + }, + "devDependencies": { + "@solana/web3.js": "^1.47.4", + "@types/chai": "^4.3.3", + "@types/mocha": "^10.0.0", + "@types/node": "^20.8.7", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "chai": "^4.3.6", + "eslint": "^8.20.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-require-extensions": "^0.1.1", + "gh-pages": "^6.0.0", + "mocha": "^10.1.0", + "prettier": "^3.0.0", + "shx": "^0.3.4", + "ts-node": "^10.9.1", + "tslib": "^2.3.1", + "typedoc": "^0.25.0", + "typescript": "^5.0.4" + } +} diff --git a/token-metadata/js/src/errors.ts b/token-metadata/js/src/errors.ts new file mode 100644 index 00000000000..b86785b0bb6 --- /dev/null +++ b/token-metadata/js/src/errors.ts @@ -0,0 +1,39 @@ +// Errors match those in rust https://github.com/solana-labs/solana-program-library/blob/master/token-metadata/interface/src/error.rs +// Code follows: https://github.com/solana-labs/solana-program-library/blob/master/token/js/src/errors.tshttps://github.com/solana-labs/solana-program-library/blob/master/token/js/src/errors.ts + +/** Base class for errors */ +export class TokenMetadataError extends Error { + constructor(message?: string) { + super(message); + } +} + +/** Thrown if incorrect account provided */ +export class IncorrectAccountError extends TokenMetadataError { + name = 'IncorrectAccountError'; +} + +/** Thrown if Mint has no mint authority */ +export class MintHasNoMintAuthorityError extends TokenMetadataError { + name = 'MintHasNoMintAuthorityError'; +} + +/** Thrown if Incorrect mint authority has signed the instruction */ +export class IncorrectMintAuthorityError extends TokenMetadataError { + name = 'IncorrectMintAuthorityError'; +} + +/** Thrown if Incorrect mint authority has signed the instruction */ +export class IncorrectUpdateAuthorityError extends TokenMetadataError { + name = 'IncorrectUpdateAuthorityError'; +} + +/** Thrown if Token metadata has no update authority */ +export class ImmutableMetadataError extends TokenMetadataError { + name = 'ImmutableMetadataError'; +} + +/** Thrown if Key not found in metadata account */ +export class KeyNotFoundError extends TokenMetadataError { + name = 'KeyNotFoundError'; +} diff --git a/token-metadata/js/src/field.ts b/token-metadata/js/src/field.ts new file mode 100644 index 00000000000..7458177ffec --- /dev/null +++ b/token-metadata/js/src/field.ts @@ -0,0 +1,30 @@ +import type { DataEnumToCodecTuple } from '@solana/codecs-data-structures'; +import { getStructCodec, getTupleCodec, getUnitCodec } from '@solana/codecs-data-structures'; +import { getStringCodec } from '@solana/codecs-strings'; + +export enum Field { + Name, + Symbol, + Uri, +} + +type FieldLayout = { __kind: 'Name' } | { __kind: 'Symbol' } | { __kind: 'Uri' } | { __kind: 'Key'; value: [string] }; + +export const getFieldCodec = (): DataEnumToCodecTuple => [ + ['Name', getUnitCodec()], + ['Symbol', getUnitCodec()], + ['Uri', getUnitCodec()], + ['Key', getStructCodec<{ value: [string] }>([['value', getTupleCodec([getStringCodec()])]])], +]; + +export function getFieldConfig(field: Field | string): FieldLayout { + if (field === Field.Name || field === 'Name' || field === 'name') { + return { __kind: 'Name' }; + } else if (field === Field.Symbol || field === 'Symbol' || field === 'symbol') { + return { __kind: 'Symbol' }; + } else if (field === Field.Uri || field === 'Uri' || field === 'uri') { + return { __kind: 'Uri' }; + } else { + return { __kind: 'Key', value: [field] }; + } +} diff --git a/token-metadata/js/src/index.ts b/token-metadata/js/src/index.ts new file mode 100644 index 00000000000..faffcde7683 --- /dev/null +++ b/token-metadata/js/src/index.ts @@ -0,0 +1,4 @@ +export * from './errors.js'; +export * from './field.js'; +export * from './instruction.js'; +export * from './state.js'; diff --git a/token-metadata/js/src/instruction.ts b/token-metadata/js/src/instruction.ts new file mode 100644 index 00000000000..8375ff246ae --- /dev/null +++ b/token-metadata/js/src/instruction.ts @@ -0,0 +1,173 @@ +import type { StructToEncoderTuple } from '@solana/codecs-data-structures'; +import { getBooleanEncoder, getBytesEncoder, getDataEnumCodec, getStructEncoder } from '@solana/codecs-data-structures'; +import { getU64Encoder } from '@solana/codecs-numbers'; +import { getStringEncoder } from '@solana/codecs-strings'; +import { getOptionEncoder } from '@solana/options'; +import { splDiscriminate } from '@solana/spl-type-length-value'; +import type { PublicKey } from '@solana/web3.js'; +import { TransactionInstruction } from '@solana/web3.js'; + +import type { Field } from './field.js'; +import { getFieldCodec, getFieldConfig } from './field.js'; + +function packInstruction( + layout: StructToEncoderTuple, + discriminator: Uint8Array, + values: T +): Buffer { + const encoder = getStructEncoder(layout); + const data = encoder.encode(values); + return Buffer.concat([discriminator, data]); +} + +/** + * Initializes a TLV entry with the basic token-metadata fields. + * + * Assumes that the provided mint is an SPL token mint, that the metadata + * account is allocated and assigned to the program, and that the metadata + * account has enough lamports to cover the rent-exempt reserve. + */ +export interface InitializeInstructionArgs { + programId: PublicKey; + metadata: PublicKey; + updateAuthority: PublicKey; + mint: PublicKey; + mintAuthority: PublicKey; + name: string; + symbol: string; + uri: string; +} + +export function createInitializeInstruction(args: InitializeInstructionArgs): TransactionInstruction { + const { programId, metadata, updateAuthority, mint, mintAuthority, name, symbol, uri } = args; + return new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: false, isWritable: false, pubkey: updateAuthority }, + { isSigner: false, isWritable: false, pubkey: mint }, + { isSigner: true, isWritable: false, pubkey: mintAuthority }, + ], + data: packInstruction( + [ + ['name', getStringEncoder()], + ['symbol', getStringEncoder()], + ['uri', getStringEncoder()], + ], + splDiscriminate('spl_token_metadata_interface:initialize_account'), + { name, symbol, uri } + ), + }); +} + +/** + * If the field does not exist on the account, it will be created. + * If the field does exist, it will be overwritten. + */ +export interface UpdateFieldInstruction { + programId: PublicKey; + metadata: PublicKey; + updateAuthority: PublicKey; + field: Field | string; + value: string; +} + +export function createUpdateFieldInstruction(args: UpdateFieldInstruction): TransactionInstruction { + const { programId, metadata, updateAuthority, field, value } = args; + return new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: packInstruction( + [ + ['field', getDataEnumCodec(getFieldCodec())], + ['value', getStringEncoder()], + ], + splDiscriminate('spl_token_metadata_interface:updating_field'), + { field: getFieldConfig(field), value } + ), + }); +} + +export interface RemoveKeyInstructionArgs { + programId: PublicKey; + metadata: PublicKey; + updateAuthority: PublicKey; + key: string; + idempotent: boolean; +} + +export function createRemoveKeyInstruction(args: RemoveKeyInstructionArgs) { + const { programId, metadata, updateAuthority, key, idempotent } = args; + return new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: packInstruction( + [ + ['idempotent', getBooleanEncoder()], + ['key', getStringEncoder()], + ], + splDiscriminate('spl_token_metadata_interface:remove_key_ix'), + { idempotent, key } + ), + }); +} + +export interface UpdateAuthorityInstructionArgs { + programId: PublicKey; + metadata: PublicKey; + oldAuthority: PublicKey; + newAuthority: PublicKey | null; +} + +export function createUpdateAuthorityInstruction(args: UpdateAuthorityInstructionArgs): TransactionInstruction { + const { programId, metadata, oldAuthority, newAuthority } = args; + + const newAuthorityBuffer = Buffer.alloc(32); + if (newAuthority) { + newAuthorityBuffer.set(newAuthority.toBuffer()); + } else { + newAuthorityBuffer.fill(0); + } + + return new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: oldAuthority }, + ], + data: packInstruction( + [['newAuthority', getBytesEncoder({ size: 32 })]], + splDiscriminate('spl_token_metadata_interface:update_the_authority'), + { newAuthority: newAuthorityBuffer } + ), + }); +} + +export interface EmitInstructionArgs { + programId: PublicKey; + metadata: PublicKey; + start?: bigint; + end?: bigint; +} + +export function createEmitInstruction(args: EmitInstructionArgs): TransactionInstruction { + const { programId, metadata, start, end } = args; + return new TransactionInstruction({ + programId, + keys: [{ isSigner: false, isWritable: false, pubkey: metadata }], + data: packInstruction( + [ + ['start', getOptionEncoder(getU64Encoder())], + ['end', getOptionEncoder(getU64Encoder())], + ], + splDiscriminate('spl_token_metadata_interface:emitter'), + { start: start ?? null, end: end ?? null } + ), + }); +} diff --git a/token-metadata/js/src/state.ts b/token-metadata/js/src/state.ts new file mode 100644 index 00000000000..3b147f4ad5a --- /dev/null +++ b/token-metadata/js/src/state.ts @@ -0,0 +1,68 @@ +import { getArrayDecoder, getBytesDecoder, getStructDecoder, getTupleDecoder } from '@solana/codecs-data-structures'; +import { getStringDecoder } from '@solana/codecs-strings'; +import { TlvState } from '@solana/spl-type-length-value'; +import { PublicKey } from '@solana/web3.js'; + +import { TokenMetadataError } from './errors.js'; + +export const TOKEN_METADATA_DISCRIMINATOR = Buffer.from([112, 132, 90, 90, 11, 88, 157, 87]); + +export interface TokenMetadata { + // The authority that can sign to update the metadata + updateAuthority?: PublicKey; + // The associated mint, used to counter spoofing to be sure that metadata belongs to a particular mint + mint: PublicKey; + // The longer name of the token + name: string; + // The shortened symbol for the token + symbol: string; + // The URI pointing to richer metadata + uri: string; + // Any additional metadata about the token as key-value pairs + additionalMetadata: [string, string][]; +} + +// Checks if all elements in the array are 0 +function isNonePubkey(buffer: Uint8Array): boolean { + for (let i = 0; i < buffer.length; i++) { + if (buffer[i] !== 0) { + return false; + } + } + return true; +} + +export function unpack(buffer: Buffer): TokenMetadata { + const tlv = new TlvState(buffer, 8, 4); + const bytes = tlv.firstBytes(TOKEN_METADATA_DISCRIMINATOR); + if (bytes === null) { + throw new TokenMetadataError('Invalid Data'); + } + const decoder = getStructDecoder([ + ['updateAuthority', getBytesDecoder({ size: 32 })], + ['mint', getBytesDecoder({ size: 32 })], + ['name', getStringDecoder()], + ['symbol', getStringDecoder()], + ['uri', getStringDecoder()], + ['additionalMetadata', getArrayDecoder(getTupleDecoder([getStringDecoder(), getStringDecoder()]))], + ]); + + const data = decoder.decode(bytes); + + return isNonePubkey(data[0].updateAuthority) + ? { + mint: new PublicKey(data[0].mint), + name: data[0].name, + symbol: data[0].symbol, + uri: data[0].uri, + additionalMetadata: data[0].additionalMetadata, + } + : { + updateAuthority: new PublicKey(data[0].updateAuthority), + mint: new PublicKey(data[0].mint), + name: data[0].name, + symbol: data[0].symbol, + uri: data[0].uri, + additionalMetadata: data[0].additionalMetadata, + }; +} diff --git a/token-metadata/js/test/instruction.test.ts b/token-metadata/js/test/instruction.test.ts new file mode 100644 index 00000000000..ba7be8164b0 --- /dev/null +++ b/token-metadata/js/test/instruction.test.ts @@ -0,0 +1,166 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { expect } from 'chai'; + +import { + createEmitInstruction, + createInitializeInstruction, + createRemoveKeyInstruction, + createUpdateAuthorityInstruction, + createUpdateFieldInstruction, +} from '../src'; + +describe('Token Metadata Instructions', () => { + const programId = new PublicKey('22222222222222222222222222222222222222222222'); + const metadata = new PublicKey('33333333333333333333333333333333333333333333'); + const updateAuthority = new PublicKey('44444444444444444444444444444444444444444444'); + const mint = new PublicKey('55555555555555555555555555555555555555555555'); + const mintAuthority = new PublicKey('66666666666666666666666666666666666666666666'); + + it('Can create Initialize Instruction', () => { + const instruction = createInitializeInstruction({ + programId, + metadata, + updateAuthority, + mint, + mintAuthority, + name: 'My test token', + symbol: 'TEST', + uri: 'http://test.test', + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: false, isWritable: false, pubkey: updateAuthority }, + { isSigner: false, isWritable: false, pubkey: mint }, + { isSigner: true, isWritable: false, pubkey: mintAuthority }, + ], + data: Buffer.from([ + // Output of rust implementation + 210, 225, 30, 162, 88, 184, 77, 141, 13, 0, 0, 0, 77, 121, 32, 116, 101, 115, 116, 32, 116, 111, + 107, 101, 110, 4, 0, 0, 0, 84, 69, 83, 84, 16, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, + 115, 116, 46, 116, 101, 115, 116, + ]), + }) + ); + }); + + it('Can create Update Field Instruction', () => { + const instruction = createUpdateFieldInstruction({ + programId, + metadata, + updateAuthority, + field: 'MyTestField', + value: 'http://test.uri', + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: Buffer.from([ + // Output of rust implementation + 221, 233, 49, 45, 181, 202, 220, 200, 3, 11, 0, 0, 0, 77, 121, 84, 101, 115, 116, 70, 105, 101, 108, + 100, 15, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, 115, 116, 46, 117, 114, 105, + ]), + }) + ); + }); + + it('Can create Update Field Instruction with Field Enum', () => { + const instruction = createUpdateFieldInstruction({ + programId, + metadata, + updateAuthority, + field: 'Name', + value: 'http://test.uri', + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: Buffer.from([ + // Output of rust implementation + 221, 233, 49, 45, 181, 202, 220, 200, 0, 15, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, 115, + 116, 46, 117, 114, 105, + ]), + }) + ); + }); + + it('Can create Remove Key Instruction', () => { + const instruction = createRemoveKeyInstruction({ + programId, + metadata, + updateAuthority: updateAuthority, + key: 'MyTestField', + idempotent: true, + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: Buffer.from([ + // Output of rust implementation + 234, 18, 32, 56, 89, 141, 37, 181, 1, 11, 0, 0, 0, 77, 121, 84, 101, 115, 116, 70, 105, 101, 108, + 100, + ]), + }) + ); + }); + + it('Can create Update Authority Instruction', () => { + const instruction = createUpdateAuthorityInstruction({ + programId, + metadata, + oldAuthority: updateAuthority, + newAuthority: PublicKey.default, + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [ + { isSigner: false, isWritable: true, pubkey: metadata }, + { isSigner: true, isWritable: false, pubkey: updateAuthority }, + ], + data: Buffer.from([ + // Output of rust implementation + 215, 228, 166, 228, 84, 100, 86, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]), + }) + ); + }); + it('Can create Emit Instruction', () => { + const instruction = createEmitInstruction({ + programId, + metadata, + end: BigInt(10), + }); + + expect(instruction).to.deep.equal( + new TransactionInstruction({ + programId, + keys: [{ isSigner: false, isWritable: false, pubkey: metadata }], + data: Buffer.from([ + // Output of rust implementation + 250, 166, 180, 250, 13, 12, 184, 70, 0, 1, 10, 0, 0, 0, 0, 0, 0, 0, + ]), + }) + ); + }); +}); diff --git a/token-metadata/js/test/state.test.ts b/token-metadata/js/test/state.test.ts new file mode 100644 index 00000000000..b832bea7056 --- /dev/null +++ b/token-metadata/js/test/state.test.ts @@ -0,0 +1,98 @@ +import { PublicKey } from '@solana/web3.js'; +import { expect } from 'chai'; + +import type { TokenMetadata } from '../src/state'; +import { TOKEN_METADATA_DISCRIMINATOR, unpack } from '../src/state'; +import { getArrayEncoder, getBytesEncoder, getStructEncoder, getTupleEncoder } from '@solana/codecs-data-structures'; +import { getStringEncoder } from '@solana/codecs-strings'; + +describe('Token Metadata State', () => { + const lengthBuffer = (buffer: Buffer | Uint8Array): Buffer => { + const length = Buffer.alloc(4); + length.writeUIntLE(buffer.length, 0, 4); + return length; + }; + + // Helper function to pack meta into tlv bytes slab + const pack = (meta: TokenMetadata) => { + const encoder = getStructEncoder([ + ['updateAuthority', getBytesEncoder({ size: 32 })], + ['mint', getBytesEncoder({ size: 32 })], + ['name', getStringEncoder()], + ['symbol', getStringEncoder()], + ['uri', getStringEncoder()], + ['additionalMetadata', getArrayEncoder(getTupleEncoder([getStringEncoder(), getStringEncoder()]))], + ]); + const data = encoder.encode({ + ...meta, + updateAuthority: meta.updateAuthority?.toBuffer(), + mint: meta.mint.toBuffer(), + }); + return Buffer.concat([TOKEN_METADATA_DISCRIMINATOR, lengthBuffer(data), data]); + }; + + it('Can unpack', () => { + const data = Buffer.from([ + // From rust implementation + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 110, 97, + 109, 101, 6, 0, 0, 0, 115, 121, 109, 98, 111, 108, 3, 0, 0, 0, 117, 114, 105, 0, 0, 0, 0, + ]); + + const input = Buffer.concat([TOKEN_METADATA_DISCRIMINATOR, lengthBuffer(data), data]); + + const meta = unpack(input); + expect(meta).to.deep.equal({ + mint: PublicKey.default, + name: 'name', + symbol: 'symbol', + uri: 'uri', + additionalMetadata: [], + }); + }); + + it('Can unpack with additionalMetadata', () => { + const data = Buffer.from([ + // From rust implementation + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 110, 101, + 119, 95, 110, 97, 109, 101, 10, 0, 0, 0, 110, 101, 119, 95, 115, 121, 109, 98, 111, 108, 7, 0, 0, 0, 110, + 101, 119, 95, 117, 114, 105, 2, 0, 0, 0, 4, 0, 0, 0, 107, 101, 121, 49, 6, 0, 0, 0, 118, 97, 108, 117, 101, + 49, 4, 0, 0, 0, 107, 101, 121, 50, 6, 0, 0, 0, 118, 97, 108, 117, 101, 50, + ]); + + const input = Buffer.concat([TOKEN_METADATA_DISCRIMINATOR, lengthBuffer(data), data]); + const meta = unpack(input); + expect(meta).to.deep.equal({ + mint: PublicKey.default, + name: 'new_name', + symbol: 'new_symbol', + uri: 'new_uri', + additionalMetadata: [ + ['key1', 'value1'], + ['key2', 'value2'], + ], + }); + }); + + it('Can pack and unpack with mint and updateAuthority', () => { + const input = pack({ + updateAuthority: new PublicKey('44444444444444444444444444444444444444444444'), + mint: new PublicKey('55555555555555555555555555555555555555555555'), + name: 'name', + symbol: 'symbol', + uri: 'uri', + additionalMetadata: [], + }); + + const meta = unpack(input); + expect(meta).to.deep.equal({ + updateAuthority: new PublicKey('44444444444444444444444444444444444444444444'), + mint: new PublicKey('55555555555555555555555555555555555555555555'), + name: 'name', + symbol: 'symbol', + uri: 'uri', + additionalMetadata: [], + }); + }); +}); diff --git a/token-metadata/js/tsconfig.all.json b/token-metadata/js/tsconfig.all.json new file mode 100644 index 00000000000..985513259e2 --- /dev/null +++ b/token-metadata/js/tsconfig.all.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.root.json", + "references": [ + { + "path": "./tsconfig.cjs.json" + }, + { + "path": "./tsconfig.esm.json" + } + ] +} diff --git a/token-metadata/js/tsconfig.base.json b/token-metadata/js/tsconfig.base.json new file mode 100644 index 00000000000..90620c4e485 --- /dev/null +++ b/token-metadata/js/tsconfig.base.json @@ -0,0 +1,14 @@ +{ + "include": [], + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "esModuleInterop": true, + "isolatedModules": true, + "noEmitOnError": true, + "resolveJsonModule": true, + "strict": true, + "stripInternal": true + } +} diff --git a/token-metadata/js/tsconfig.cjs.json b/token-metadata/js/tsconfig.cjs.json new file mode 100644 index 00000000000..2db9b71569e --- /dev/null +++ b/token-metadata/js/tsconfig.cjs.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/cjs", + "target": "ES2016", + "module": "CommonJS", + "sourceMap": true + } +} diff --git a/token-metadata/js/tsconfig.esm.json b/token-metadata/js/tsconfig.esm.json new file mode 100644 index 00000000000..25e7e25e751 --- /dev/null +++ b/token-metadata/js/tsconfig.esm.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/esm", + "declarationDir": "lib/types", + "target": "ES2020", + "module": "ES2020", + "sourceMap": true, + "declaration": true, + "declarationMap": true + } +} diff --git a/token-metadata/js/tsconfig.json b/token-metadata/js/tsconfig.json new file mode 100644 index 00000000000..2f9b239bfca --- /dev/null +++ b/token-metadata/js/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.all.json", + "include": ["src", "test"], + "compilerOptions": { + "noEmit": true, + "skipLibCheck": true + } +} diff --git a/token-metadata/js/tsconfig.root.json b/token-metadata/js/tsconfig.root.json new file mode 100644 index 00000000000..fadf294ab43 --- /dev/null +++ b/token-metadata/js/tsconfig.root.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "composite": true + } +} diff --git a/token-metadata/js/typedoc.json b/token-metadata/js/typedoc.json new file mode 100644 index 00000000000..c39fc53aee1 --- /dev/null +++ b/token-metadata/js/typedoc.json @@ -0,0 +1,5 @@ +{ + "entryPoints": ["src/index.ts"], + "out": "docs", + "readme": "README.md" +} diff --git a/token-swap/js/package-lock.json b/token-swap/js/package-lock.json index 0d629f7ba2e..f0b73866710 100644 --- a/token-swap/js/package-lock.json +++ b/token-swap/js/package-lock.json @@ -15,11 +15,11 @@ "devDependencies": { "@solana/spl-token": "0.3.8", "@types/bn.js": "^5.1.0", - "@typescript-eslint/eslint-plugin": "^5.59.5", - "@typescript-eslint/parser": "^5.59.5", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.31.0", "eslint-plugin-import": "^2.22.0", - "prettier": "^2.1.2", + "prettier": "^3.0.0", "start-server-and-test": "^2.0.0", "ts-node": "^10.0.0", "typescript": "^5.0.4", @@ -36,11 +36,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -74,18 +74,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -106,9 +106,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -130,12 +130,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -157,9 +157,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@jridgewell/resolve-uri": { @@ -194,29 +194,26 @@ "dev": true }, "node_modules/@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dependencies": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -318,23 +315,23 @@ } }, "node_modules/@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -364,9 +361,9 @@ "dev": true }, "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.4.tgz", + "integrity": "sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA==", "dev": true, "dependencies": { "@types/node": "*" @@ -381,9 +378,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/json5": { @@ -398,9 +395,9 @@ "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/ws": { @@ -412,32 +409,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", + "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/type-utils": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -446,9 +444,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -461,25 +459,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -488,16 +487,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -505,25 +504,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", + "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/utils": "6.9.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -532,12 +531,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -545,21 +544,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -572,9 +571,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -587,57 +586,34 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", + "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -650,22 +626,28 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -697,12 +679,10 @@ } }, "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", "humanize-ms": "^1.2.1" }, "engines": { @@ -775,15 +755,15 @@ } }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -802,15 +782,34 @@ "node": ">=8" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -821,14 +820,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -857,6 +856,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1035,13 +1055,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1146,6 +1167,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1164,6 +1186,20 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -1200,14 +1236,6 @@ "node": ">=0.4.0" } }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1248,25 +1276,26 @@ "dev": true }, "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -1274,19 +1303,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -1335,12 +1368,12 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -1386,27 +1419,28 @@ } }, "node_modules/eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1416,7 +1450,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -1428,7 +1461,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -1442,14 +1474,14 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -1488,26 +1520,28 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "engines": { "node": ">=4" @@ -1538,9 +1572,9 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -1554,9 +1588,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1648,9 +1682,9 @@ } }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -1910,21 +1944,24 @@ "dev": true }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -1943,14 +1980,15 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2017,9 +2055,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2094,12 +2132,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2187,6 +2219,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -2358,12 +2402,12 @@ } }, "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2539,16 +2583,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -2884,16 +2924,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2933,9 +2967,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2968,15 +3002,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -3102,15 +3165,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -3181,19 +3244,19 @@ ] }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -3203,12 +3266,12 @@ } }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3324,6 +3387,24 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3358,14 +3439,43 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3435,9 +3545,9 @@ } }, "node_modules/start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "dependencies": { "arg": "^5.0.2", @@ -3455,7 +3565,7 @@ "start-test": "src/bin/start.js" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/start-server-and-test/node_modules/arg": { @@ -3474,14 +3584,14 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -3491,28 +3601,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3622,6 +3732,18 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -3683,27 +3805,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3728,6 +3829,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -3743,9 +3895,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3914,17 +4066,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3995,11 +4146,11 @@ "dev": true }, "@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" } }, "@cspotcode/source-map-support": { @@ -4021,15 +4172,15 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -4044,9 +4195,9 @@ } }, "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true }, "@hapi/hoek": { @@ -4065,12 +4216,12 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" } @@ -4082,9 +4233,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@jridgewell/resolve-uri": { @@ -4116,17 +4267,17 @@ "dev": true }, "@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "requires": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" } }, "@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -4207,23 +4358,23 @@ } }, "@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "requires": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -4253,9 +4404,9 @@ "dev": true }, "@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.4.tgz", + "integrity": "sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA==", "dev": true, "requires": { "@types/node": "*" @@ -4270,9 +4421,9 @@ } }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "@types/json5": { @@ -4287,9 +4438,9 @@ "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "@types/ws": { @@ -4301,27 +4452,28 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", + "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/type-utils": "6.9.1", + "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4330,64 +4482,65 @@ } }, "@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", + "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", + "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1" } }, "@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", + "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/utils": "6.9.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", + "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", + "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/visitor-keys": "6.9.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4396,41 +4549,24 @@ } }, "@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", + "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.1", + "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/typescript-estree": "6.9.1", + "semver": "^7.5.4" + }, + "dependencies": { "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4439,15 +4575,21 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", + "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.1", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -4468,12 +4610,10 @@ "dev": true }, "agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", "humanize-ms": "^1.2.1" } }, @@ -4527,15 +4667,15 @@ } }, "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" } }, @@ -4545,27 +4685,40 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" } }, "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" } }, @@ -4582,6 +4735,21 @@ "is-string": "^1.0.7" } }, + "arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4712,13 +4880,14 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "callsites": { @@ -4799,6 +4968,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -4809,6 +4979,17 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -4830,11 +5011,6 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4866,25 +5042,26 @@ "dev": true }, "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "requires": { "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -4892,19 +5069,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" } }, "es-array-method-boxes-properly": { @@ -4941,12 +5122,12 @@ } }, "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "es-to-primitive": { @@ -4980,27 +5161,28 @@ "dev": true }, "eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5010,7 +5192,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -5022,7 +5203,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -5081,14 +5261,14 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "requires": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" }, "dependencies": { "debug": { @@ -5123,26 +5303,28 @@ } }, "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "dependencies": { "debug": { @@ -5166,9 +5348,9 @@ } }, "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -5176,15 +5358,15 @@ } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { "acorn": "^8.9.0", @@ -5388,21 +5570,21 @@ "dev": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" } }, "functions-have-names": { @@ -5412,14 +5594,15 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-stream": { @@ -5462,9 +5645,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -5517,12 +5700,6 @@ "get-intrinsic": "^1.1.3" } }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -5580,6 +5757,15 @@ "has-symbols": "^1.0.2" } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -5695,12 +5881,12 @@ "dev": true }, "is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "is-date-object": { @@ -5810,16 +5996,12 @@ } }, "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" } }, "is-weakref": { @@ -6086,16 +6268,10 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "requires": { "whatwg-url": "^5.0.0" } @@ -6116,9 +6292,9 @@ } }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-keys": { @@ -6139,15 +6315,38 @@ "object-keys": "^1.1.1" } }, + "object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "once": { @@ -6237,9 +6436,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "promise.allsettled": { @@ -6278,28 +6477,28 @@ "dev": true }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" } }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -6364,6 +6563,18 @@ "tslib": "^2.1.0" } }, + "safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6381,11 +6592,34 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6440,9 +6674,9 @@ } }, "start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "requires": { "arg": "^5.0.2", @@ -6473,36 +6707,36 @@ } }, "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "strip-ansi": { @@ -6582,6 +6816,13 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -6621,23 +6862,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6653,6 +6877,42 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, "typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -6665,9 +6925,9 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, "typescript-esm": { @@ -6790,17 +7050,16 @@ } }, "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "requires": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" } }, "wrappy": { diff --git a/token-swap/js/package.json b/token-swap/js/package.json index 6c85c8b634d..6b2612b9078 100644 --- a/token-swap/js/package.json +++ b/token-swap/js/package.json @@ -33,7 +33,7 @@ "build": "tsc -p tsconfig.json && tsc-esm -p tsconfig.json && tsc -p tsconfig.cjs.json", "postbuild": "echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && echo '{\"type\":\"module\"}' > dist/esm/package.json", "test": "ts-node test/main.ts", - "start-with-test-validator": "start-server-and-test 'solana-test-validator --bpf-program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw ../../target/deploy/spl_token_swap.so --reset --quiet' http://localhost:8899/health test", + "start-with-test-validator": "start-server-and-test 'solana-test-validator --bpf-program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw ../../target/deploy/spl_token_swap.so --reset --quiet' http://127.0.0.1:8899/health test", "lint": "npm run pretty && eslint --max-warnings 0 .", "lint:fix": "npm run pretty:fix && eslint . --fix", "build:program": "cargo build-sbf --manifest-path ../program/Cargo.toml", @@ -48,11 +48,11 @@ "devDependencies": { "@solana/spl-token": "0.3.8", "@types/bn.js": "^5.1.0", - "@typescript-eslint/eslint-plugin": "^5.59.5", - "@typescript-eslint/parser": "^5.59.5", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.31.0", "eslint-plugin-import": "^2.22.0", - "prettier": "^2.1.2", + "prettier": "^3.0.0", "start-server-and-test": "^2.0.0", "ts-node": "^10.0.0", "typescript": "^5.0.4", diff --git a/token-swap/js/src/index.ts b/token-swap/js/src/index.ts index f7a6376e67a..760ae73935a 100644 --- a/token-swap/js/src/index.ts +++ b/token-swap/js/src/index.ts @@ -407,9 +407,8 @@ export class TokenSwap { ); // Allocate memory for the account - const balanceNeeded = await TokenSwap.getMinBalanceRentForExemptTokenSwap( - connection, - ); + const balanceNeeded = + await TokenSwap.getMinBalanceRentForExemptTokenSwap(connection); transaction = new Transaction(); transaction.add( SystemProgram.createAccount({ diff --git a/token-swap/js/test/token-swap-test.ts b/token-swap/js/test/token-swap-test.ts index a0d0556be1a..9f0728934ed 100644 --- a/token-swap/js/test/token-swap-test.ts +++ b/token-swap/js/test/token-swap-test.ts @@ -81,7 +81,7 @@ let connection: Connection; async function getConnection(): Promise { if (connection) return connection; - const url = 'http://localhost:8899'; + const url = 'http://127.0.0.1:8899'; connection = new Connection(url, 'recent'); const version = await connection.getVersion(); diff --git a/token-swap/program/Cargo.toml b/token-swap/program/Cargo.toml index 8f9074d0530..138e861ebba 100644 --- a/token-swap/program/Cargo.toml +++ b/token-swap/program/Cargo.toml @@ -14,22 +14,22 @@ fuzz = ["arbitrary", "roots"] [dependencies] arrayref = "0.3.7" -enum_dispatch = "0.3.11" +enum_dispatch = "0.3.12" num-derive = "0.4" num-traits = "0.2" -solana-program = "1.16.1" +solana-program = "1.17.2" spl-math = { version = "0.2", path = "../../libraries/math", features = [ "no-entrypoint" ] } spl-token = { version = "4.0", path = "../../token/program", features = [ "no-entrypoint" ] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = [ "no-entrypoint" ] } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = [ "no-entrypoint" ] } thiserror = "1.0" -arbitrary = { version = "1.0", features = ["derive"], optional = true } +arbitrary = { version = "1.3", features = ["derive"], optional = true } roots = { version = "0.0.8", optional = true } [dev-dependencies] -proptest = "1.2" +proptest = "1.3" roots = "0.0.8" -solana-sdk = "1.16.1" -test-case = "3.1" +solana-sdk = "1.17.2" +test-case = "3.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-swap/program/fuzz/Cargo.toml b/token-swap/program/fuzz/Cargo.toml index 40612113c1f..eb1c90f9029 100644 --- a/token-swap/program/fuzz/Cargo.toml +++ b/token-swap/program/fuzz/Cargo.toml @@ -10,8 +10,8 @@ publish = false [dependencies] honggfuzz = { version = "0.5.55" } -arbitrary = { version = "1.0", features = ["derive"] } -solana-program = "1.16.1" +arbitrary = { version = "1.3", features = ["derive"] } +solana-program = "1.17.2" spl-math = { version = "0.2", path = "../../../libraries/math", features = [ "no-entrypoint" ] } spl-token = { version = "4.0", path = "../../../token/program", features = [ "no-entrypoint" ] } spl-token-swap = { path = "..", features = ["fuzz", "no-entrypoint"] } diff --git a/token-swap/program/fuzz/src/instructions.rs b/token-swap/program/fuzz/src/instructions.rs index 4818b0f7daf..62f765fde46 100644 --- a/token-swap/program/fuzz/src/instructions.rs +++ b/token-swap/program/fuzz/src/instructions.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use { arbitrary::Arbitrary, honggfuzz::fuzz, @@ -390,7 +390,7 @@ fn run_fuzz_instruction( || e == TokenError::InsufficientFunds.into()) { println!("{:?}", e); - Err(e).unwrap() + panic!("{:?}", e) } }) .ok(); diff --git a/token-swap/program/fuzz/src/lib.rs b/token-swap/program/fuzz/src/lib.rs index 88adb764d44..ac2eb1bc88d 100644 --- a/token-swap/program/fuzz/src/lib.rs +++ b/token-swap/program/fuzz/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] pub mod native_account_data; pub mod native_processor; pub mod native_token; diff --git a/token-swap/program/src/curve/base.rs b/token-swap/program/src/curve/base.rs index 6dfced9efae..b90585b12c7 100644 --- a/token-swap/program/src/curve/base.rs +++ b/token-swap/program/src/curve/base.rs @@ -382,7 +382,7 @@ mod test { let swap_source_amount: u128 = 1_000; let swap_destination_amount: u128 = 50_000; let source_amount: u128 = 100; - let curve = ConstantProductCurve::default(); + let curve = ConstantProductCurve; let fees = Fees::default(); let swap_curve = SwapCurve { curve_type: CurveType::ConstantProduct, @@ -409,7 +409,7 @@ mod test { pool_supply: u128, fees: Fees, ) -> (u128, u128) { - let curve = ConstantProductCurve::default(); + let curve = ConstantProductCurve; let swap_curve = SwapCurve { curve_type: CurveType::ConstantProduct, calculator: Arc::new(curve), diff --git a/token-swap/program/src/curve/constant_price.rs b/token-swap/program/src/curve/constant_price.rs index e4f1e10fbde..7b66df3058a 100644 --- a/token-swap/program/src/curve/constant_price.rs +++ b/token-swap/program/src/curve/constant_price.rs @@ -569,7 +569,6 @@ mod tests { ) { let curve = ConstantPriceCurve { token_b_price: token_b_price as u64 }; let pool_token_amount = pool_token_amount as u128; - let pool_token_supply = pool_token_supply; let swap_token_a_amount = swap_token_a_amount as u128; let swap_token_b_amount = swap_token_b_amount as u128; let token_b_price = token_b_price as u128; diff --git a/token-swap/program/src/curve/constant_product.rs b/token-swap/program/src/curve/constant_product.rs index 9d88cedad7d..7195119f93a 100644 --- a/token-swap/program/src/curve/constant_product.rs +++ b/token-swap/program/src/curve/constant_product.rs @@ -379,7 +379,7 @@ mod tests { #[test] fn constant_product_swap_rounding() { - let curve = ConstantProductCurve::default(); + let curve = ConstantProductCurve; // much too small assert!(curve diff --git a/token-swap/program/src/lib.rs b/token-swap/program/src/lib.rs index ad0f77ffd45..a8d325c9e6d 100644 --- a/token-swap/program/src/lib.rs +++ b/token-swap/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] //! An Uniswap-like program for the Solana blockchain. diff --git a/token-swap/program/src/processor.rs b/token-swap/program/src/processor.rs index c3988159e99..567acfc1c12 100644 --- a/token-swap/program/src/processor.rs +++ b/token-swap/program/src/processor.rs @@ -2154,7 +2154,7 @@ mod tests { ) -> (Pubkey, SolanaAccount) { let account_key = Pubkey::new_unique(); let space = if *program_id == spl_token_2022::id() { - ExtensionType::try_get_account_len::(&[ + ExtensionType::try_calculate_account_len::(&[ ExtensionType::ImmutableOwner, ExtensionType::TransferFeeAmount, ]) @@ -2218,14 +2218,16 @@ mod tests { let mint_key = Pubkey::new_unique(); let space = if *program_id == spl_token_2022::id() { if close_authority.is_some() { - ExtensionType::try_get_account_len::(&[ + ExtensionType::try_calculate_account_len::(&[ ExtensionType::MintCloseAuthority, ExtensionType::TransferFeeConfig, ]) .unwrap() } else { - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeConfig]) - .unwrap() + ExtensionType::try_calculate_account_len::(&[ + ExtensionType::TransferFeeConfig, + ]) + .unwrap() } } else { Mint::get_packed_len() diff --git a/token-upgrade/cli/Cargo.toml b/token-upgrade/cli/Cargo.toml index 6846fbce23f..64e3edc165d 100644 --- a/token-upgrade/cli/Cargo.toml +++ b/token-upgrade/cli/Cargo.toml @@ -13,21 +13,21 @@ walkdir = "2" [dependencies] clap = { version = "3", features = ["cargo"] } futures-util = "0.3.28" -solana-clap-v3-utils = "1.16.1" -solana-cli-config = "1.16.1" -solana-client = "1.16.1" -solana-logger = "1.16.1" -solana-remote-wallet = "1.16.1" -solana-sdk = "1.16.1" +solana-clap-v3-utils = "1.17.2" +solana-cli-config = "1.17.2" +solana-client = "1.17.2" +solana-logger = "1.17.2" +solana-remote-wallet = "1.17.2" +solana-sdk = "1.17.2" spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = ["no-entrypoint"] } spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = ["no-entrypoint"] } -spl-token-client = { version = "0.5", path = "../../token/client" } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-client = { version = "0.8", path = "../../token/client" } spl-token-upgrade = { version = "0.1", path = "../program", features = ["no-entrypoint"] } tokio = { version = "1", features = ["full"] } [dev-dependencies] -solana-test-validator = "1.16.1" +solana-test-validator = "1.17.2" [[bin]] name = "spl-token-upgrade" diff --git a/token-upgrade/cli/src/main.rs b/token-upgrade/cli/src/main.rs index 6c7b6bb4196..24527f778ba 100644 --- a/token-upgrade/cli/src/main.rs +++ b/token-upgrade/cli/src/main.rs @@ -1,10 +1,8 @@ use { clap::{crate_description, crate_name, crate_version, Arg, Command}, solana_clap_v3_utils::{ - input_parsers::pubkey_of, - input_validators::{ - is_url_or_moniker, is_valid_pubkey, is_valid_signer, normalize_to_url_if_moniker, - }, + input_parsers::{parse_url_or_moniker, pubkey_of}, + input_validators::{is_valid_pubkey, is_valid_signer, normalize_to_url_if_moniker}, keypair::{ signer_from_path, signer_from_path_with_config, DefaultSigner, SignerFromPathConfig, }, @@ -29,7 +27,7 @@ use { token::Token, }, spl_token_upgrade::{get_token_upgrade_authority_address, instruction::exchange}, - std::{error::Error, process::exit, sync::Arc}, + std::{error::Error, process::exit, rc::Rc, sync::Arc}, }; struct Config { @@ -245,7 +243,7 @@ async fn main() -> Result<(), Box> { .value_name("URL") .takes_value(true) .global(true) - .validator(|s| is_url_or_moniker(s)) + .value_parser(parse_url_or_moniker) .help("JSON RPC URL for the cluster [default: value from configuration file]"), ) .subcommand( @@ -344,7 +342,7 @@ async fn main() -> Result<(), Box> { .get_matches(); let (command, matches) = app_matches.subcommand().unwrap(); - let mut wallet_manager: Option> = None; + let mut wallet_manager: Option> = None; let config = { let cli_config = if let Some(config_file) = matches.value_of("config_file") { @@ -363,7 +361,7 @@ async fn main() -> Result<(), Box> { let json_rpc_url = normalize_to_url_if_moniker( matches - .value_of("json_rpc_url") + .get_one::("json_rpc_url") .unwrap_or(&cli_config.json_rpc_url), ); @@ -497,43 +495,23 @@ mod test { super::*, solana_sdk::{bpf_loader_upgradeable, signer::keypair::Keypair}, solana_test_validator::{TestValidator, TestValidatorGenesis, UpgradeableProgramInfo}, - spl_token_client::client::{ProgramClient, SendTransaction}, + spl_token_client::client::{ProgramClient, SendTransaction, SimulateTransaction}, std::path::PathBuf, }; async fn new_validator_for_test() -> (TestValidator, Keypair) { solana_logger::setup(); let mut test_validator_genesis = TestValidatorGenesis::default(); - test_validator_genesis.add_upgradeable_programs_with_path(&[ - UpgradeableProgramInfo { - program_id: spl_token::id(), - loader: bpf_loader_upgradeable::id(), - program_path: PathBuf::from("../../target/deploy/spl_token.so"), - upgrade_authority: Pubkey::new_unique(), - }, - UpgradeableProgramInfo { - program_id: spl_associated_token_account::id(), - loader: bpf_loader_upgradeable::id(), - program_path: PathBuf::from("../../target/deploy/spl_associated_token_account.so"), - upgrade_authority: Pubkey::new_unique(), - }, - UpgradeableProgramInfo { - program_id: spl_token_2022::id(), - loader: bpf_loader_upgradeable::id(), - program_path: PathBuf::from("../../target/deploy/spl_token_2022.so"), - upgrade_authority: Pubkey::new_unique(), - }, - UpgradeableProgramInfo { - program_id: spl_token_upgrade::id(), - loader: bpf_loader_upgradeable::id(), - program_path: PathBuf::from("../../target/deploy/spl_token_upgrade.so"), - upgrade_authority: Pubkey::new_unique(), - }, - ]); + test_validator_genesis.add_upgradeable_programs_with_path(&[UpgradeableProgramInfo { + program_id: spl_token_upgrade::id(), + loader: bpf_loader_upgradeable::id(), + program_path: PathBuf::from("../../target/deploy/spl_token_upgrade.so"), + upgrade_authority: Pubkey::new_unique(), + }]); test_validator_genesis.start_async().await } - async fn setup_mint( + async fn setup_mint( program_id: &Pubkey, mint_authority: &Pubkey, decimals: u8, @@ -586,17 +564,15 @@ mod test { .await; let account_keypair = Keypair::new(); - assert!(matches!( - process_create_escrow_account( - &rpc_client, - &payer, - original_token.get_address(), - new_token.get_address(), - Some(&account_keypair) - ) - .await, - Ok(_) - )); + assert!(process_create_escrow_account( + &rpc_client, + &payer, + original_token.get_address(), + new_token.get_address(), + Some(&account_keypair) + ) + .await + .is_ok()); let escrow_authority = get_token_upgrade_authority_address( original_token.get_address(), new_token.get_address(), @@ -609,17 +585,15 @@ mod test { assert_eq!(escrow.base.owner, escrow_authority); assert_eq!(&escrow.base.mint, new_token.get_address()); - assert!(matches!( - process_create_escrow_account( - &rpc_client, - &payer, - original_token.get_address(), - new_token.get_address(), - None - ) - .await, - Ok(_) - )); + assert!(process_create_escrow_account( + &rpc_client, + &payer, + original_token.get_address(), + new_token.get_address(), + None + ) + .await + .is_ok()); let escrow = new_token .get_account_info(&new_token.get_associated_token_address(&escrow_authority)) .await diff --git a/token-upgrade/program/Cargo.toml b/token-upgrade/program/Cargo.toml index 67d8b0fed4f..315bbe4033a 100644 --- a/token-upgrade/program/Cargo.toml +++ b/token-upgrade/program/Cargo.toml @@ -14,17 +14,17 @@ test-sbf = [] [dependencies] num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" -solana-program = "1.16.1" -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = ["no-entrypoint"] } +num_enum = "0.7.1" +solana-program = "1.17.2" +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -spl-token-client = { version = "0.5", path = "../../token/client" } -test-case = "3.1" +spl-token-client = { version = "0.8", path = "../../token/client" } +test-case = "3.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-upgrade/program/tests/functional.rs b/token-upgrade/program/tests/functional.rs index 0a22952dcb2..bc1c7626195 100644 --- a/token-upgrade/program/tests/functional.rs +++ b/token-upgrade/program/tests/functional.rs @@ -17,7 +17,7 @@ use { spl_token_client::{ client::{ ProgramBanksClient, ProgramBanksClientProcessTransaction, ProgramClient, - SendTransaction, + SendTransaction, SimulateTransaction, }, token::Token, }, @@ -68,7 +68,7 @@ async fn setup() -> ( (context, client, payer) } -async fn setup_mint( +async fn setup_mint( program_id: &Pubkey, mint_authority: &Pubkey, decimals: u8, diff --git a/token-wrap/program/Cargo.toml b/token-wrap/program/Cargo.toml index 76ec1f66415..97698a42947 100644 --- a/token-wrap/program/Cargo.toml +++ b/token-wrap/program/Cargo.toml @@ -12,12 +12,12 @@ no-entrypoint = [] test-sbf = [] [dependencies] -bytemuck = { version = "1.7.2", features = ["derive"] } -num_enum = "0.6" -solana-program = "1.16.1" +bytemuck = { version = "1.14.0", features = ["derive"] } +num_enum = "0.7" +solana-program = "1.17.2" spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = ["no-entrypoint"] } spl-token = { version = "4.0", path = "../../token/program", features = ["no-entrypoint"] } -spl-token-2022 = { version = "0.7", path = "../../token/program-2022", features = ["no-entrypoint"] } +spl-token-2022 = { version = "0.9", path = "../../token/program-2022", features = ["no-entrypoint"] } thiserror = "1.0" [lib] diff --git a/token/cli/Cargo.toml b/token/cli/Cargo.toml index 2f9202e52bb..3224a9ea3b3 100644 --- a/token/cli/Cargo.toml +++ b/token/cli/Cargo.toml @@ -6,40 +6,51 @@ homepage = "https://spl.solana.com/token" license = "Apache-2.0" name = "spl-token-cli" repository = "https://github.com/solana-labs/solana-program-library" -version = "3.0.0" +version = "3.3.0" [build-dependencies] walkdir = "2" [dependencies] +base64 = "0.21.5" clap = "2.33.3" console = "0.15.7" -serde = "1.0.164" +futures = "0.3" +serde = "1.0.190" serde_derive = "1.0.103" -serde_json = "1.0.99" -solana-account-decoder = "=1.16.1" -solana-clap-utils = "=1.16.1" -solana-cli-config = "=1.16.1" -solana-cli-output = "=1.16.1" -solana-client = "=1.16.1" -solana-logger = "=1.16.1" -solana-remote-wallet = "=1.16.1" -solana-sdk = "=1.16.1" -solana-transaction-status = "=1.16.1" -spl-token = { version = "4.0", path="../program", features = [ "no-entrypoint" ] } -spl-token-2022 = { version = "0.7", path="../program-2022", features = [ "no-entrypoint" ] } -spl-token-client = { version = "0.5", path="../client" } -spl-associated-token-account = { version = "2.0", path="../../associated-token-account/program", features = [ "no-entrypoint" ] } -spl-memo = { version = "4.0.0", path="../../memo/program", features = ["no-entrypoint"] } +serde_json = "1.0.108" +solana-account-decoder = "=1.17.2" +solana-clap-utils = "=1.17.2" +solana-cli-config = "=1.17.2" +solana-cli-output = "=1.17.2" +solana-client = "=1.17.2" +solana-logger = "=1.17.2" +solana-remote-wallet = "=1.17.2" +solana-sdk = "=1.17.2" +solana-transaction-status = "=1.17.2" +spl-token = { version = "4.0", path = "../program", features = [ + "no-entrypoint", +] } +spl-token-2022 = { version = "0.9", path = "../program-2022", features = [ + "no-entrypoint", +] } +spl-token-client = { version = "0.8", path = "../client" } +spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" } +spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = [ + "no-entrypoint", +] } +spl-memo = { version = "4.0.0", path = "../../memo/program", features = [ + "no-entrypoint", +] } strum = "0.25" strum_macros = "0.25" tokio = "1.14" [dev-dependencies] -solana-test-validator = "=1.16.1" -assert_cmd = "2.0.5" +solana-test-validator = "=1.17.2" +assert_cmd = "2.0.12" serial_test = "2.0.0" -tempfile = "3.6.0" +tempfile = "3.8.1" [[bin]] name = "spl-token" diff --git a/token/cli/src/bench.rs b/token/cli/src/bench.rs index 9c0a83839c8..27cf0a4482c 100644 --- a/token/cli/src/bench.rs +++ b/token/cli/src/bench.rs @@ -21,7 +21,7 @@ use { instruction, state::{Account, Mint}, }, - std::{sync::Arc, time::Instant}, + std::{rc::Rc, sync::Arc, time::Instant}, }; pub(crate) trait BenchSubCommand { @@ -169,7 +169,7 @@ pub(crate) async fn bench_process_command( matches: &ArgMatches<'_>, config: &Config<'_>, mut signers: Vec>, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> CommandResult { assert!(!config.sign_only); diff --git a/token/cli/src/config.rs b/token/cli/src/config.rs index e76b0f71b55..e66cbf7719a 100644 --- a/token/cli/src/config.rs +++ b/token/cli/src/config.rs @@ -22,7 +22,7 @@ use spl_token_2022::{ use spl_token_client::client::{ ProgramClient, ProgramOfflineClient, ProgramRpcClient, ProgramRpcClientSendTransaction, }; -use std::{process::exit, sync::Arc}; +use std::{process::exit, rc::Rc, sync::Arc}; pub(crate) struct MintInfo { pub program_id: Pubkey, @@ -50,7 +50,7 @@ pub(crate) struct Config<'a> { impl<'a> Config<'a> { pub(crate) async fn new( matches: &ArgMatches<'_>, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, bulk_signers: &mut Vec>, multisigner_ids: &'a mut Vec, ) -> Config<'a> { @@ -101,7 +101,7 @@ impl<'a> Config<'a> { fn extract_multisig_signers( matches: &ArgMatches<'_>, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, bulk_signers: &mut Vec>, multisigner_ids: &'a mut Vec, ) -> Vec<&'a Pubkey> { @@ -121,7 +121,7 @@ impl<'a> Config<'a> { pub(crate) async fn new_with_clients_and_ws_url( matches: &ArgMatches<'_>, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, bulk_signers: &mut Vec>, multisigner_ids: &'a mut Vec, rpc_client: Arc, @@ -305,7 +305,7 @@ impl<'a> Config<'a> { &self, arg_matches: &ArgMatches<'_>, override_name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> Result { let token = pubkey_of_signer(arg_matches, "token", wallet_manager) .map_err(|e| -> Error { e.to_string().into() })?; @@ -324,7 +324,7 @@ impl<'a> Config<'a> { &self, arg_matches: &ArgMatches<'_>, override_name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, token: Option, ) -> Result { if let Some(address) = pubkey_of_signer(arg_matches, override_name, wallet_manager) @@ -355,7 +355,7 @@ impl<'a> Config<'a> { &self, arg_matches: &ArgMatches<'_>, address_name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> Result { if let Some(address) = pubkey_of_signer(arg_matches, address_name, wallet_manager) .map_err(|e| -> Error { e.to_string().into() })? @@ -371,7 +371,7 @@ impl<'a> Config<'a> { &self, arg_matches: &ArgMatches, authority_name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> (Arc, Pubkey) { // If there are `--multisig-signers` on the command line, allow `NullSigner`s to // be returned for multisig account addresses diff --git a/token/cli/src/encryption_keypair.rs b/token/cli/src/encryption_keypair.rs new file mode 100644 index 00000000000..302c21520d9 --- /dev/null +++ b/token/cli/src/encryption_keypair.rs @@ -0,0 +1,75 @@ +//! Temporary ElGamal keypair argument parser. +//! +//! NOTE: this module should be remoeved in the next Solana upgrade. + +use { + base64::{prelude::BASE64_STANDARD, Engine}, + clap::ArgMatches, + spl_token_2022::solana_zk_token_sdk::{ + encryption::elgamal::{ElGamalKeypair, ElGamalPubkey}, + zk_token_elgamal::pod::ElGamalPubkey as PodElGamalPubkey, + }, +}; + +const ELGAMAL_PUBKEY_MAX_BASE64_LEN: usize = 44; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum ElGamalPubkeyOrNone { + ElGamalPubkey(PodElGamalPubkey), + None, +} + +impl From for Option { + fn from(val: ElGamalPubkeyOrNone) -> Self { + match val { + ElGamalPubkeyOrNone::ElGamalPubkey(pubkey) => Some(pubkey), + ElGamalPubkeyOrNone::None => None, + } + } +} + +pub(crate) fn elgamal_pubkey_or_none( + matches: &ArgMatches, + name: &str, +) -> Result { + let arg_str = matches.value_of(name).unwrap(); + if arg_str == "none" { + return Ok(ElGamalPubkeyOrNone::None); + } + let elgamal_pubkey = elgamal_pubkey_of(matches, name)?; + Ok(ElGamalPubkeyOrNone::ElGamalPubkey(elgamal_pubkey)) +} + +pub(crate) fn elgamal_pubkey_of( + matches: &ArgMatches, + name: &str, +) -> Result { + if let Ok(keypair) = elgamal_keypair_of(matches, name) { + let elgamal_pubkey = (*keypair.pubkey()).into(); + Ok(elgamal_pubkey) + } else { + let arg_str = matches.value_of(name).unwrap(); + if let Some(pubkey) = elgamal_pubkey_from_str(arg_str) { + Ok(pubkey) + } else { + Err("failed to read ElGamal pubkey".to_string()) + } + } +} + +pub(crate) fn elgamal_keypair_of( + matches: &ArgMatches, + name: &str, +) -> Result { + let path = matches.value_of(name).unwrap(); + ElGamalKeypair::read_json_file(path).map_err(|e| e.to_string()) +} + +fn elgamal_pubkey_from_str(s: &str) -> Option { + if s.len() > ELGAMAL_PUBKEY_MAX_BASE64_LEN { + return None; + } + let pubkey_vec = BASE64_STANDARD.decode(s).ok()?; + let elgamal_pubkey = ElGamalPubkey::from_bytes(&pubkey_vec)?; + Some(elgamal_pubkey.into()) +} diff --git a/token/cli/src/main.rs b/token/cli/src/main.rs index 5e61e0c31f9..7a8bac38d99 100644 --- a/token/cli/src/main.rs +++ b/token/cli/src/main.rs @@ -1,8 +1,9 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] use clap::{ crate_description, crate_name, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg, - ArgMatches, SubCommand, + ArgGroup, ArgMatches, SubCommand, }; +use futures::try_join; use serde::Serialize; use solana_account_decoder::{ parse_token::{get_token_account_mint, parse_token, TokenAccountType, UiAccountState}, @@ -28,6 +29,7 @@ use solana_cli_output::{ use solana_client::rpc_request::TokenAccountsFilter; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ + instruction::AccountMeta, native_token::*, program_option::COption, pubkey::Pubkey, @@ -37,7 +39,13 @@ use solana_sdk::{ use spl_associated_token_account::get_associated_token_address_with_program_id; use spl_token_2022::{ extension::{ - confidential_transfer::ConfidentialTransferMint, + confidential_transfer::{ + account_info::{ + ApplyPendingBalanceAccountInfo, TransferAccountInfo, WithdrawAccountInfo, + }, + instruction::TransferSplitContextStateAccounts, + ConfidentialTransferAccount, ConfidentialTransferMint, + }, confidential_transfer_fee::ConfidentialTransferFeeConfig, cpi_guard::CpiGuard, default_account_state::DefaultAccountState, @@ -51,14 +59,25 @@ use spl_token_2022::{ BaseStateWithExtensions, ExtensionType, StateWithExtensionsOwned, }, instruction::*, + solana_zk_token_sdk::{ + encryption::{ + auth_encryption::AeKey, + elgamal::{self, ElGamalKeypair}, + }, + zk_token_elgamal::pod::ElGamalPubkey, + }, state::{Account, AccountState, Mint}, }; use spl_token_client::{ client::{ProgramRpcClientSendTransaction, RpcClientResponse}, token::{ExtensionInitializationParams, Token}, }; -use std::{collections::HashMap, fmt, fmt::Display, process::exit, str::FromStr, sync::Arc}; -use strum_macros::{EnumString, IntoStaticStr}; +use spl_token_metadata_interface::state::{Field, TokenMetadata}; +use std::{ + collections::HashMap, fmt, fmt::Display, process::exit, rc::Rc, str::FromStr, sync::Arc, +}; +use strum::IntoEnumIterator; +use strum_macros::{EnumIter, EnumString, IntoStaticStr}; mod config; use config::{Config, MintInfo}; @@ -72,6 +91,10 @@ use sort::{sort_and_parse_token_accounts, AccountFilter}; mod bench; use bench::*; +// NOTE: this submodule should be removed in the next Solana upgrade +mod encryption_keypair; +use encryption_keypair::*; + pub const OWNER_ADDRESS_ARG: ArgConstant<'static> = ArgConstant { name: "owner", long: "owner", @@ -102,6 +125,12 @@ pub const DELEGATE_ADDRESS_ARG: ArgConstant<'static> = ArgConstant { help: "Address of delegate currently assigned to token account. Required by --sign-only", }; +pub const TRANSFER_LAMPORTS_ARG: ArgConstant<'static> = ArgConstant { + name: "transfer_lamports", + long: "transfer-lamports", + help: "Additional lamports to transfer to make account rent-exempt after reallocation. Required by --sign-only", +}; + pub const MULTISIG_SIGNER_ARG: ArgConstant<'static> = ArgConstant { name: "multisig_signer", long: "multisig-signer", @@ -144,9 +173,22 @@ pub enum CommandName { EnableCpiGuard, DisableCpiGuard, UpdateDefaultAccountState, + UpdateMetadataAddress, WithdrawWithheldTokens, SetTransferFee, WithdrawExcessLamports, + SetTransferHookProgram, + InitializeMetadata, + UpdateMetadata, + UpdateConfidentialTransferSettings, + ConfigureConfidentialTransferAccount, + EnableConfidentialCredits, + DisableConfidentialCredits, + EnableNonConfidentialCredits, + DisableNonConfidentialCredits, + DepositConfidentialTokens, + WithdrawConfidentialTokens, + ApplyPendingBalance, } impl fmt::Display for CommandName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -154,6 +196,98 @@ impl fmt::Display for CommandName { } } +#[derive(Debug, Clone, Copy, PartialEq, EnumString, IntoStaticStr)] +#[strum(serialize_all = "kebab-case")] +pub enum AccountMetaRole { + Readonly, + Writable, + ReadonlySigner, + WritableSigner, +} +impl fmt::Display for AccountMetaRole { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} +fn parse_transfer_hook_account(string: T) -> Result +where + T: AsRef + Display, +{ + match string.as_ref().split(':').collect::>().as_slice() { + [address, role] => { + let address = Pubkey::from_str(address).map_err(|e| format!("{e}"))?; + let meta = match AccountMetaRole::from_str(role).map_err(|e| format!("{e}"))? { + AccountMetaRole::Readonly => AccountMeta::new_readonly(address, false), + AccountMetaRole::Writable => AccountMeta::new(address, false), + AccountMetaRole::ReadonlySigner => AccountMeta::new_readonly(address, true), + AccountMetaRole::WritableSigner => AccountMeta::new(address, true), + }; + Ok(meta) + } + _ => Err("Transfer hook account must be present as
:".to_string()), + } +} +fn validate_transfer_hook_account(string: T) -> Result<(), String> +where + T: AsRef + Display, +{ + match string.as_ref().split(':').collect::>().as_slice() { + [address, role] => { + is_valid_pubkey(address)?; + AccountMetaRole::from_str(role) + .map(|_| ()) + .map_err(|e| format!("{e}")) + } + _ => Err("Transfer hook account must be present as
:".to_string()), + } +} + +#[derive(Debug, Clone, PartialEq, EnumIter, EnumString, IntoStaticStr)] +#[strum(serialize_all = "kebab-case")] +pub enum CliAuthorityType { + Mint, + Freeze, + Owner, + Close, + CloseMint, + TransferFeeConfig, + WithheldWithdraw, + InterestRate, + PermanentDelegate, + ConfidentialTransferMint, + TransferHookProgramId, + ConfidentialTransferFee, + MetadataPointer, + Metadata, +} +impl TryFrom for AuthorityType { + type Error = Error; + fn try_from(authority_type: CliAuthorityType) -> Result { + match authority_type { + CliAuthorityType::Mint => Ok(AuthorityType::MintTokens), + CliAuthorityType::Freeze => Ok(AuthorityType::FreezeAccount), + CliAuthorityType::Owner => Ok(AuthorityType::AccountOwner), + CliAuthorityType::Close => Ok(AuthorityType::CloseAccount), + CliAuthorityType::CloseMint => Ok(AuthorityType::CloseMint), + CliAuthorityType::TransferFeeConfig => Ok(AuthorityType::TransferFeeConfig), + CliAuthorityType::WithheldWithdraw => Ok(AuthorityType::WithheldWithdraw), + CliAuthorityType::InterestRate => Ok(AuthorityType::InterestRate), + CliAuthorityType::PermanentDelegate => Ok(AuthorityType::PermanentDelegate), + CliAuthorityType::ConfidentialTransferMint => { + Ok(AuthorityType::ConfidentialTransferMint) + } + CliAuthorityType::TransferHookProgramId => Ok(AuthorityType::TransferHookProgramId), + CliAuthorityType::ConfidentialTransferFee => { + Ok(AuthorityType::ConfidentialTransferFeeConfig) + } + CliAuthorityType::MetadataPointer => Ok(AuthorityType::MetadataPointer), + CliAuthorityType::Metadata => { + Err("Metadata authority does not map to a token authority type".into()) + } + } + } +} + pub fn owner_address_arg<'a, 'b>() -> Arg<'a, 'b> { Arg::with_name(OWNER_ADDRESS_ARG.name) .long(OWNER_ADDRESS_ARG.long) @@ -218,6 +352,15 @@ pub fn delegate_address_arg<'a, 'b>() -> Arg<'a, 'b> { .help(DELEGATE_ADDRESS_ARG.help) } +pub fn transfer_lamports_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(TRANSFER_LAMPORTS_ARG.name) + .long(TRANSFER_LAMPORTS_ARG.long) + .takes_value(true) + .value_name("LAMPORTS") + .validator(is_amount) + .help(TRANSFER_LAMPORTS_ARG.help) +} + pub fn multisig_signer_arg<'a, 'b>() -> Arg<'a, 'b> { Arg::with_name(MULTISIG_SIGNER_ARG.name) .long(MULTISIG_SIGNER_ARG.long) @@ -282,7 +425,7 @@ fn new_throwaway_signer() -> (Arc, Pubkey) { fn get_signer( matches: &ArgMatches<'_>, keypair_name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> Option<(Arc, Pubkey)> { matches.value_of(keypair_name).map(|path| { let signer = signer_from_path(matches, path, keypair_name, wallet_manager) @@ -336,7 +479,7 @@ type SignersOf = Vec<(Arc, Pubkey)>; pub fn signers_of( matches: &ArgMatches<'_>, name: &str, - wallet_manager: &mut Option>, + wallet_manager: &mut Option>, ) -> Result, Box> { if let Some(values) = matches.values_of(name) { let mut results = Vec::new(); @@ -420,6 +563,8 @@ async fn command_create_token( default_account_state: Option, transfer_fee: Option<(u16, u64)>, confidential_transfer_auto_approve: Option, + transfer_hook_program_id: Option, + enable_metadata: bool, bulk_signers: Vec>, ) -> CommandResult { println_display( @@ -484,11 +629,24 @@ async fn command_create_token( }); } + if let Some(program_id) = transfer_hook_program_id { + extensions.push(ExtensionInitializationParams::TransferHook { + authority: Some(authority), + program_id: Some(program_id), + }); + } + if let Some(text) = memo { token.with_memo(text, vec![config.default_signer()?.pubkey()]); } - if metadata_address.is_some() { + // CLI checks that only one is set + if metadata_address.is_some() || enable_metadata { + let metadata_address = if enable_metadata { + Some(token_pubkey) + } else { + metadata_address + }; extensions.push(ExtensionInitializationParams::MetadataPointer { authority: Some(authority), metadata_address, @@ -505,6 +663,18 @@ async fn command_create_token( .await?; let tx_return = finish_tx(config, &res, false).await?; + + if enable_metadata { + println_display( + config, + format!( + "To initialize metadata inside the mint, please run \ + `spl-token initialize-metadata {token_pubkey} `, \ + and sign with the mint authority.", + ), + ); + } + Ok(match tx_return { TransactionReturnData::CliSignature(cli_signature) => format_output( CliCreateToken { @@ -579,6 +749,154 @@ async fn command_set_interest_rate( }) } +async fn command_set_transfer_hook_program( + config: &Config<'_>, + token_pubkey: Pubkey, + authority: Pubkey, + new_program_id: Option, + bulk_signers: Vec>, +) -> CommandResult { + let token = token_client_from_config(config, &token_pubkey, None)?; + + if !config.sign_only { + let mint_account = config.get_account_checked(&token_pubkey).await?; + + let mint_state = StateWithExtensionsOwned::::unpack(mint_account.data) + .map_err(|_| format!("Could not deserialize token mint {}", token_pubkey))?; + + if let Ok(extension) = mint_state.get_extension::() { + let authority_pubkey = Option::::from(extension.authority); + + if authority_pubkey != Some(authority) { + return Err(format!( + "Mint {} has transfer hook authority {}, but {} was provided", + token_pubkey, + authority_pubkey + .map(|pubkey| pubkey.to_string()) + .unwrap_or_else(|| "disabled".to_string()), + authority + ) + .into()); + } + } else { + return Err( + format!("Mint {} does not have permissioned-transfers", token_pubkey).into(), + ); + } + } + + println_display( + config, + format!( + "Setting Transfer Hook Program id for {} to {}", + token_pubkey, + new_program_id + .map(|pubkey| pubkey.to_string()) + .unwrap_or_else(|| "disabled".to_string()) + ), + ); + + let res = token + .update_transfer_hook_program_id(&authority, new_program_id, &bulk_signers) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +#[allow(clippy::too_many_arguments)] +async fn command_initialize_metadata( + config: &Config<'_>, + token_pubkey: Pubkey, + update_authority: Pubkey, + mint_authority: Pubkey, + name: String, + symbol: String, + uri: String, + bulk_signers: Vec>, +) -> CommandResult { + let token = token_client_from_config(config, &token_pubkey, None)?; + + let res = token + .token_metadata_initialize_with_rent_transfer( + &config.fee_payer()?.pubkey(), + &update_authority, + &mint_authority, + name, + symbol, + uri, + &bulk_signers, + ) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +async fn command_update_metadata( + config: &Config<'_>, + token_pubkey: Pubkey, + authority: Pubkey, + field: Field, + value: Option, + transfer_lamports: Option, + bulk_signers: Vec>, +) -> CommandResult { + let token = token_client_from_config(config, &token_pubkey, None)?; + + let res = if let Some(value) = value { + token + .token_metadata_update_field_with_rent_transfer( + &config.fee_payer()?.pubkey(), + &authority, + field, + value, + transfer_lamports, + &bulk_signers, + ) + .await? + } else if let Field::Key(key) = field { + token + .token_metadata_remove_key( + &authority, + key, + true, // idempotent + &bulk_signers, + ) + .await? + } else { + return Err(format!( + "Attempting to remove field {field:?}, which cannot be removed. \ + Please re-run the command with a value of \"\" rather than the `--remove` flag." + ) + .into()); + }; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + async fn command_set_transfer_fee( config: &Config<'_>, token_pubkey: Pubkey, @@ -771,29 +1089,13 @@ async fn command_create_multisig( async fn command_authorize( config: &Config<'_>, account: Pubkey, - authority_type: AuthorityType, + authority_type: CliAuthorityType, authority: Pubkey, new_authority: Option, force_authorize: bool, bulk_signers: BulkSigners, ) -> CommandResult { - let auth_str = match authority_type { - AuthorityType::MintTokens => "mint authority", - AuthorityType::FreezeAccount => "freeze authority", - AuthorityType::AccountOwner => "owner", - AuthorityType::CloseAccount => "close account authority", - AuthorityType::CloseMint => "close mint authority", - AuthorityType::TransferFeeConfig => "transfer fee authority", - AuthorityType::WithheldWithdraw => "withdraw withheld authority", - AuthorityType::InterestRate => "interest rate authority", - AuthorityType::PermanentDelegate => "permanent delegate", - AuthorityType::ConfidentialTransferMint => "confidential transfer mint authority", - AuthorityType::TransferHookProgramId => "transfer hook program id authority", - AuthorityType::ConfidentialTransferFeeConfig => { - "confidential transfer fee config authority" - } - AuthorityType::MetadataPointer => "metadata pointer authority", - }; + let auth_str: &'static str = (&authority_type).into(); let (mint_pubkey, previous_authority) = if !config.sign_only { let target_account = config.get_account_checked(&account).await?; @@ -802,17 +1104,15 @@ async fn command_authorize( StateWithExtensionsOwned::::unpack(target_account.data.clone()) { let previous_authority = match authority_type { - AuthorityType::AccountOwner | AuthorityType::CloseAccount => Err(format!( + CliAuthorityType::Owner | CliAuthorityType::Close => Err(format!( "Authority type `{}` not supported for SPL Token mints", auth_str )), - AuthorityType::MintTokens => Ok(mint.base.mint_authority), - AuthorityType::FreezeAccount => Ok(mint.base.freeze_authority), - AuthorityType::CloseMint => { + CliAuthorityType::Mint => Ok(Option::::from(mint.base.mint_authority)), + CliAuthorityType::Freeze => Ok(Option::::from(mint.base.freeze_authority)), + CliAuthorityType::CloseMint => { if let Ok(mint_close_authority) = mint.get_extension::() { - Ok(COption::::from( - mint_close_authority.close_authority, - )) + Ok(Option::::from(mint_close_authority.close_authority)) } else { Err(format!( "Mint `{}` does not support close authority", @@ -820,35 +1120,35 @@ async fn command_authorize( )) } } - AuthorityType::TransferFeeConfig => { + CliAuthorityType::TransferFeeConfig => { if let Ok(transfer_fee_config) = mint.get_extension::() { - Ok(COption::::from( + Ok(Option::::from( transfer_fee_config.transfer_fee_config_authority, )) } else { Err(format!("Mint `{}` does not support transfer fees", account)) } } - AuthorityType::WithheldWithdraw => { + CliAuthorityType::WithheldWithdraw => { if let Ok(transfer_fee_config) = mint.get_extension::() { - Ok(COption::::from( + Ok(Option::::from( transfer_fee_config.withdraw_withheld_authority, )) } else { Err(format!("Mint `{}` does not support transfer fees", account)) } } - AuthorityType::InterestRate => { + CliAuthorityType::InterestRate => { if let Ok(interest_rate_config) = mint.get_extension::() { - Ok(COption::::from(interest_rate_config.rate_authority)) + Ok(Option::::from(interest_rate_config.rate_authority)) } else { Err(format!("Mint `{}` is not interest-bearing", account)) } } - AuthorityType::PermanentDelegate => { + CliAuthorityType::PermanentDelegate => { if let Ok(permanent_delegate) = mint.get_extension::() { - Ok(COption::::from(permanent_delegate.delegate)) + Ok(Option::::from(permanent_delegate.delegate)) } else { Err(format!( "Mint `{}` does not support permanent delegate", @@ -856,13 +1156,11 @@ async fn command_authorize( )) } } - AuthorityType::ConfidentialTransferMint => { + CliAuthorityType::ConfidentialTransferMint => { if let Ok(confidential_transfer_mint) = mint.get_extension::() { - Ok(COption::::from( - confidential_transfer_mint.authority, - )) + Ok(Option::::from(confidential_transfer_mint.authority)) } else { Err(format!( "Mint `{}` does not support confidential transfers", @@ -870,21 +1168,21 @@ async fn command_authorize( )) } } - AuthorityType::TransferHookProgramId => { - if let Ok(transfer_hook) = mint.get_extension::() { - Ok(COption::::from(transfer_hook.authority)) + CliAuthorityType::TransferHookProgramId => { + if let Ok(extension) = mint.get_extension::() { + Ok(Option::::from(extension.authority)) } else { Err(format!( - "Mint `{}` does not support a transfer hook", + "Mint `{}` does not support a transfer hook program", account )) } } - AuthorityType::ConfidentialTransferFeeConfig => { + CliAuthorityType::ConfidentialTransferFee => { if let Ok(confidential_transfer_fee_config) = mint.get_extension::() { - Ok(COption::::from( + Ok(Option::::from( confidential_transfer_fee_config.authority, )) } else { @@ -894,9 +1192,9 @@ async fn command_authorize( )) } } - AuthorityType::MetadataPointer => { + CliAuthorityType::MetadataPointer => { if let Ok(extension) = mint.get_extension::() { - Ok(COption::::from(extension.authority)) + Ok(Option::::from(extension.authority)) } else { Err(format!( "Mint `{}` does not support a metadata pointer", @@ -904,6 +1202,13 @@ async fn command_authorize( )) } } + CliAuthorityType::Metadata => { + if let Ok(extension) = mint.get_variable_len_extension::() { + Ok(Option::::from(extension.update_authority)) + } else { + Err(format!("Mint `{account}` does not support metadata")) + } + } }?; Ok((account, previous_authority)) @@ -931,27 +1236,27 @@ async fn command_authorize( }; let previous_authority = match authority_type { - AuthorityType::MintTokens - | AuthorityType::FreezeAccount - | AuthorityType::CloseMint - | AuthorityType::TransferFeeConfig - | AuthorityType::WithheldWithdraw - | AuthorityType::InterestRate - | AuthorityType::PermanentDelegate - | AuthorityType::ConfidentialTransferMint - | AuthorityType::TransferHookProgramId - | AuthorityType::ConfidentialTransferFeeConfig - | AuthorityType::MetadataPointer => Err(format!( - "Authority type `{}` not supported for SPL Token accounts", - auth_str + CliAuthorityType::Mint + | CliAuthorityType::Freeze + | CliAuthorityType::CloseMint + | CliAuthorityType::TransferFeeConfig + | CliAuthorityType::WithheldWithdraw + | CliAuthorityType::InterestRate + | CliAuthorityType::PermanentDelegate + | CliAuthorityType::ConfidentialTransferMint + | CliAuthorityType::TransferHookProgramId + | CliAuthorityType::ConfidentialTransferFee + | CliAuthorityType::MetadataPointer + | CliAuthorityType::Metadata => Err(format!( + "Authority type `{auth_str}` not supported for SPL Token accounts", )), - AuthorityType::AccountOwner => { + CliAuthorityType::Owner => { check_associated_token_account()?; - Ok(COption::Some(token_account.base.owner)) + Ok(Some(token_account.base.owner)) } - AuthorityType::CloseAccount => { + CliAuthorityType::Close => { check_associated_token_account()?; - Ok(COption::Some( + Ok(Some( token_account .base .close_authority @@ -968,7 +1273,7 @@ async fn command_authorize( (mint_pubkey, previous_authority) } else { // default is safe here because authorize doesnt use it - (Pubkey::default(), COption::None) + (Pubkey::default(), None) }; let token = token_client_from_config(config, &mint_pubkey, None)?; @@ -993,15 +1298,21 @@ async fn command_authorize( ), ); - let res = token - .set_authority( - &account, - &authority, - new_authority.as_ref(), - authority_type, - &bulk_signers, - ) - .await?; + let res = if let CliAuthorityType::Metadata = authority_type { + token + .token_metadata_update_authority(&authority, new_authority, &bulk_signers) + .await? + } else { + token + .set_authority( + &account, + &authority, + new_authority.as_ref(), + authority_type.try_into()?, + &bulk_signers, + ) + .await? + }; let tx_return = finish_tx(config, &res, false).await?; Ok(match tx_return { @@ -1025,13 +1336,15 @@ async fn command_transfer( allow_unfunded_recipient: bool, fund_recipient: bool, mint_decimals: Option, - recipient_is_ata_owner: bool, + no_recipient_is_ata_owner: bool, use_unchecked_instruction: bool, ui_fee: Option, memo: Option, bulk_signers: BulkSigners, no_wait: bool, allow_non_system_account_recipient: bool, + transfer_hook_accounts: Option>, + confidential_transfer_args: Option<&ConfidentialTransferArgs>, ) -> CommandResult { let mint_info = config.get_mint_info(&token_pubkey, mint_decimals).await?; @@ -1059,7 +1372,12 @@ async fn command_transfer( Some(mint_info.decimals) }; - let token = token_client_from_config(config, &token_pubkey, decimals)?; + let token = if let Some(transfer_hook_accounts) = transfer_hook_accounts { + token_client_from_config(config, &token_pubkey, decimals)? + .with_transfer_hook_accounts(transfer_hook_accounts) + } else { + token_client_from_config(config, &token_pubkey, decimals)? + }; // pubkey of the actual account we are sending from let sender = if let Some(sender) = sender { @@ -1080,14 +1398,19 @@ async fn command_transfer( println_display( config, format!( - "Transfer {} tokens\n Sender: {}\n Recipient: {}", + "{}Transfer {} tokens\n Sender: {}\n Recipient: {}", + if confidential_transfer_args.is_some() { + "Confidential " + } else { + "" + }, spl_token::amount_to_ui_amount(transfer_balance, mint_info.decimals), sender, recipient ), ); - if transfer_balance > sender_balance { + if transfer_balance > sender_balance && confidential_transfer_args.is_none() { return Err(format!( "Error: Sender has insufficient funds, current balance is {}", spl_token_2022::amount_to_ui_amount_string_trimmed( @@ -1165,7 +1488,7 @@ async fn command_transfer( } } else { // in offline mode we gotta trust them - !recipient_is_ata_owner + no_recipient_is_ata_owner }; // now if its a token account, life is ez @@ -1228,7 +1551,13 @@ async fn command_transfer( // and now we determine if we will actually fund it, based on its need and our willingness let fundable_owner = if needs_funding { - if fund_recipient { + if confidential_transfer_args.is_some() { + return Err( + "Error: Recipient's associated token account does not exist. \ + Accounts cannot be funded for confidential transfers." + .into(), + ); + } else if fund_recipient { println_display( config, format!(" Funding recipient: {}", recipient_token_account,), @@ -1254,63 +1583,251 @@ async fn command_transfer( token.with_memo(text, vec![config.default_signer()?.pubkey()]); } - // ...and, finally, the transfer - let res = if let Some(recipient_owner) = fundable_owner { - token - .create_recipient_associated_account_and_transfer( - &sender, - &recipient_token_account, - &recipient_owner, - &sender_owner, - transfer_balance, - maybe_fee, - &bulk_signers, - ) - .await? - } else if let Some(fee) = maybe_fee { - token - .transfer_with_fee( - &sender, - &recipient_token_account, - &sender_owner, - transfer_balance, - fee, - &bulk_signers, - ) - .await? + // fetch confidential transfer info for recipient and auditor + let (recipient_elgamal_pubkey, auditor_elgamal_pubkey) = if let Some(args) = + confidential_transfer_args + { + if !config.sign_only { + // we can use the mint data from the start of the function, but will require + // non-trivial amount of refactoring the code due to ownership; for now, we fetch the mint + // a second time. This can potentially be optimized in the future. + let confidential_transfer_mint = config.get_account_checked(&token_pubkey).await?; + let mint_state = + StateWithExtensionsOwned::::unpack(confidential_transfer_mint.data) + .map_err(|_| format!("Could not deserialize token mint {}", token_pubkey))?; + + let auditor_elgamal_pubkey = if let Ok(confidential_transfer_mint) = + mint_state.get_extension::() + { + let expected_auditor_elgamal_pubkey = Option::::from( + confidential_transfer_mint.auditor_elgamal_pubkey, + ); + + // if auditor ElGamal pubkey is provided, check consistency with the one in the mint + // if auditor ElGamal pubkey is not provided, then use the expected one from the + // mint, which could also be `None` if auditing is disabled + if args.auditor_elgamal_pubkey.is_some() + && expected_auditor_elgamal_pubkey != args.auditor_elgamal_pubkey + { + return Err(format!( + "Mint {} has confidential transfer auditor {}, but {} was provided", + token_pubkey, + expected_auditor_elgamal_pubkey + .map(|pubkey| pubkey.to_string()) + .unwrap_or_else(|| "disabled".to_string()), + args.auditor_elgamal_pubkey.unwrap(), + ) + .into()); + } + + expected_auditor_elgamal_pubkey + } else { + return Err(format!( + "Mint {} does not support confidential transfers", + token_pubkey + ) + .into()); + }; + + let recipient_account = config.get_account_checked(&recipient_token_account).await?; + let recipient_elgamal_pubkey = + StateWithExtensionsOwned::::unpack(recipient_account.data)? + .get_extension::()? + .elgamal_pubkey; + + (Some(recipient_elgamal_pubkey), auditor_elgamal_pubkey) + } else { + let recipient_elgamal_pubkey = args + .recipient_elgamal_pubkey + .expect("Recipient ElGamal pubkey must be provided"); + let auditor_elgamal_pubkey = args + .auditor_elgamal_pubkey + .expect("Auditor ElGamal pubkey must be provided"); + + (Some(recipient_elgamal_pubkey), Some(auditor_elgamal_pubkey)) + } } else { - token - .transfer( - &sender, - &recipient_token_account, - &sender_owner, - transfer_balance, - &bulk_signers, - ) - .await? + (None, None) }; - let tx_return = finish_tx(config, &res, no_wait).await?; - Ok(match tx_return { - TransactionReturnData::CliSignature(signature) => { - config.output_format.formatted_string(&signature) + // ...and, finally, the transfer + let res = match (fundable_owner, maybe_fee, confidential_transfer_args) { + (Some(recipient_owner), None, None) => { + token + .create_recipient_associated_account_and_transfer( + &sender, + &recipient_token_account, + &recipient_owner, + &sender_owner, + transfer_balance, + maybe_fee, + &bulk_signers, + ) + .await? } - TransactionReturnData::CliSignOnlyData(sign_only_data) => { - config.output_format.formatted_string(&sign_only_data) + (Some(_), _, _) => { + panic!("Recipient account cannot be created for transfer with fees or confidential transfers"); } - }) -} + (None, Some(fee), None) => { + token + .transfer_with_fee( + &sender, + &recipient_token_account, + &sender_owner, + transfer_balance, + fee, + &bulk_signers, + ) + .await? + } + (None, None, Some(args)) => { + // deserialize `pod` ElGamal pubkeys + let recipient_elgamal_pubkey: elgamal::ElGamalPubkey = recipient_elgamal_pubkey + .unwrap() + .try_into() + .expect("Invalid recipient ElGamal pubkey"); + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.map(|pubkey| { + let auditor_elgamal_pubkey: elgamal::ElGamalPubkey = + pubkey.try_into().expect("Invalid auditor ElGamal pubkey"); + auditor_elgamal_pubkey + }); -#[allow(clippy::too_many_arguments)] -async fn command_burn( - config: &Config<'_>, - account: Pubkey, - owner: Pubkey, - ui_amount: f64, - mint_address: Option, - mint_decimals: Option, - use_unchecked_instruction: bool, - memo: Option, + let context_state_authority = config.fee_payer()?; + let equality_proof_context_state_account = Keypair::new(); + let equality_proof_pubkey = equality_proof_context_state_account.pubkey(); + let ciphertext_validity_proof_context_state_account = Keypair::new(); + let ciphertext_validity_proof_pubkey = + ciphertext_validity_proof_context_state_account.pubkey(); + let range_proof_context_state_account = Keypair::new(); + let range_proof_pubkey = range_proof_context_state_account.pubkey(); + + let transfer_context_state_accounts = TransferSplitContextStateAccounts { + equality_proof: &equality_proof_pubkey, + ciphertext_validity_proof: &ciphertext_validity_proof_pubkey, + range_proof: &range_proof_pubkey, + authority: &context_state_authority.pubkey(), + no_op_on_uninitialized_split_context_state: false, + close_split_context_state_accounts: None, + }; + + let state = token.get_account_info(&sender).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let ( + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + ) = transfer_account_info + .generate_split_transfer_proof_data( + transfer_balance, + &args.sender_elgamal_keypair, + &args.sender_aes_key, + &recipient_elgamal_pubkey, + auditor_elgamal_pubkey.as_ref(), + ) + .unwrap(); + + // setup proofs + let _ = try_join!( + token.create_range_proof_context_state_for_transfer( + transfer_context_state_accounts, + &range_proof_data, + &range_proof_context_state_account, + ), + token.create_equality_proof_context_state_for_transfer( + transfer_context_state_accounts, + &equality_proof_data, + &equality_proof_context_state_account, + ), + token.create_ciphertext_validity_proof_context_state_for_transfer( + transfer_context_state_accounts, + &ciphertext_validity_proof_data, + &ciphertext_validity_proof_context_state_account, + ) + )?; + + // do the transfer + let transfer_result = token + .confidential_transfer_transfer_with_split_proofs( + &sender, + &recipient_token_account, + &sender_owner, + transfer_context_state_accounts, + transfer_balance, + Some(transfer_account_info), + &args.sender_aes_key, + &source_decrypt_handles, + &bulk_signers, + ) + .await?; + + // close context state accounts + let context_state_authority_pubkey = context_state_authority.pubkey(); + let close_context_state_signers = &[context_state_authority]; + let _ = try_join!( + token.confidential_transfer_close_context_state( + &equality_proof_pubkey, + &sender, + &context_state_authority_pubkey, + close_context_state_signers, + ), + token.confidential_transfer_close_context_state( + &ciphertext_validity_proof_pubkey, + &sender, + &context_state_authority_pubkey, + close_context_state_signers, + ), + token.confidential_transfer_close_context_state( + &range_proof_pubkey, + &sender, + &context_state_authority_pubkey, + close_context_state_signers, + ), + )?; + + transfer_result + } + (None, Some(_), Some(_)) => { + panic!("Confidential transfer with fee is not yet supported."); + } + (None, None, None) => { + token + .transfer( + &sender, + &recipient_token_account, + &sender_owner, + transfer_balance, + &bulk_signers, + ) + .await? + } + }; + + let tx_return = finish_tx(config, &res, no_wait).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +#[allow(clippy::too_many_arguments)] +async fn command_burn( + config: &Config<'_>, + account: Pubkey, + owner: Pubkey, + ui_amount: f64, + mint_address: Option, + mint_decimals: Option, + use_unchecked_instruction: bool, + memo: Option, bulk_signers: BulkSigners, ) -> CommandResult { println_display( @@ -2195,7 +2712,7 @@ async fn command_required_transfer_memos( } else { existing_extensions.push(ExtensionType::MemoTransfer); let needed_account_len = - ExtensionType::try_get_account_len::(&existing_extensions)?; + ExtensionType::try_calculate_account_len::(&existing_extensions)?; if needed_account_len > current_account_len { token .reallocate( @@ -2268,7 +2785,7 @@ async fn command_cpi_guard( } else { existing_extensions.push(ExtensionType::CpiGuard); let required_account_len = - ExtensionType::try_get_account_len::(&existing_extensions)?; + ExtensionType::try_calculate_account_len::(&existing_extensions)?; if required_account_len > current_account_len { token .reallocate( @@ -2302,6 +2819,33 @@ async fn command_cpi_guard( }) } +async fn command_update_metadata_pointer_address( + config: &Config<'_>, + token_pubkey: Pubkey, + authority: Pubkey, + new_metadata_address: Option, + bulk_signers: BulkSigners, +) -> CommandResult { + if config.sign_only { + panic!("Config can not be sign-only for updating metadata pointer address."); + } + + let token = token_client_from_config(config, &token_pubkey, None)?; + let res = token + .update_metadata_address(&authority, new_metadata_address, &bulk_signers) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + async fn command_update_default_account_state( config: &Config<'_>, token_pubkey: Pubkey, @@ -2440,65 +2984,618 @@ async fn command_withdraw_withheld_tokens( Ok(results.join("")) } -struct SignOnlyNeedsFullMintSpec {} -impl offline::ArgsConfig for SignOnlyNeedsFullMintSpec { - fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name]) - } - fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name]) - } -} +async fn command_update_confidential_transfer_settings( + config: &Config<'_>, + token_pubkey: Pubkey, + authority: Pubkey, + auto_approve: Option, + auditor_pubkey: Option, + bulk_signers: Vec>, +) -> CommandResult { + let (new_auto_approve, new_auditor_pubkey) = if !config.sign_only { + let confidential_transfer_account = config.get_account_checked(&token_pubkey).await?; -struct SignOnlyNeedsMintDecimals {} -impl offline::ArgsConfig for SignOnlyNeedsMintDecimals { - fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_DECIMALS_ARG.name]) + let mint_state = + StateWithExtensionsOwned::::unpack(confidential_transfer_account.data) + .map_err(|_| format!("Could not deserialize token mint {}", token_pubkey))?; + + if let Ok(confidential_transfer_mint) = + mint_state.get_extension::() + { + let expected_authority = Option::::from(confidential_transfer_mint.authority); + + if expected_authority != Some(authority) { + return Err(format!( + "Mint {} has confidential transfer authority {}, but {} was provided", + token_pubkey, + expected_authority + .map(|pubkey| pubkey.to_string()) + .unwrap_or_else(|| "disabled".to_string()), + authority + ) + .into()); + } + + let new_auto_approve = if let Some(auto_approve) = auto_approve { + auto_approve + } else { + bool::from(confidential_transfer_mint.auto_approve_new_accounts) + }; + + let new_auditor_pubkey = if let Some(auditor_pubkey) = auditor_pubkey { + auditor_pubkey.into() + } else { + Option::::from(confidential_transfer_mint.auditor_elgamal_pubkey) + }; + + (new_auto_approve, new_auditor_pubkey) + } else { + return Err(format!( + "Mint {} does not support confidential transfers", + token_pubkey + ) + .into()); + } + } else { + let new_auto_approve = auto_approve.expect("The approve policy must be provided"); + let new_auditor_pubkey = auditor_pubkey + .expect("The auditor encryption pubkey must be provided") + .into(); + + (new_auto_approve, new_auditor_pubkey) + }; + + println_display( + config, + format!( + "Updating confidential transfer settings for {}:", + token_pubkey, + ), + ); + + if auto_approve.is_some() { + println_display( + config, + format!( + " approve policy set to {}", + if new_auto_approve { "auto" } else { "manual" } + ), + ); } - fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_DECIMALS_ARG.name]) + + if auditor_pubkey.is_some() { + if let Some(new_auditor_pubkey) = new_auditor_pubkey { + println_display( + config, + format!(" auditor encryption pubkey set to {}", new_auditor_pubkey,), + ); + } else { + println_display(config, " auditability disabled".to_string()) + } } + + let token = token_client_from_config(config, &token_pubkey, None)?; + let res = token + .confidential_transfer_update_mint( + &authority, + new_auto_approve, + new_auditor_pubkey, + &bulk_signers, + ) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) } -struct SignOnlyNeedsMintAddress {} -impl offline::ArgsConfig for SignOnlyNeedsMintAddress { - fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_ADDRESS_ARG.name]) +#[allow(clippy::too_many_arguments)] +async fn command_configure_confidential_transfer_account( + config: &Config<'_>, + maybe_token: Option, + owner: Pubkey, + maybe_account: Option, + maximum_credit_counter: Option, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + bulk_signers: BulkSigners, +) -> CommandResult { + if config.sign_only { + panic!("Sign-only is not yet supported."); } - fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[MINT_ADDRESS_ARG.name]) + + let token_account_address = if let Some(account) = maybe_account { + account + } else { + let token_pubkey = + maybe_token.expect("Either a valid token or account address must be provided"); + let token = token_client_from_config(config, &token_pubkey, None)?; + token.get_associated_token_address(&owner) + }; + + let account = config.get_account_checked(&token_account_address).await?; + let current_account_len = account.data.len(); + + let state_with_extension = StateWithExtensionsOwned::::unpack(account.data)?; + let token = token_client_from_config(config, &state_with_extension.base.mint, None)?; + + // Reallocation (if needed) + let mut existing_extensions: Vec = state_with_extension.get_extension_types()?; + if !existing_extensions.contains(&ExtensionType::ConfidentialTransferAccount) { + existing_extensions.push(ExtensionType::ConfidentialTransferAccount); + let needed_account_len = + ExtensionType::try_calculate_account_len::(&existing_extensions)?; + if needed_account_len > current_account_len { + token + .reallocate( + &token_account_address, + &owner, + &[ExtensionType::ConfidentialTransferAccount], + &bulk_signers, + ) + .await?; + } } + + let res = token + .confidential_transfer_configure_token_account( + &token_account_address, + &owner, + None, + maximum_credit_counter, + elgamal_keypair, + aes_key, + &bulk_signers, + ) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) } -struct SignOnlyNeedsDelegateAddress {} -impl offline::ArgsConfig for SignOnlyNeedsDelegateAddress { - fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[DELEGATE_ADDRESS_ARG.name]) +async fn command_enable_disable_confidential_transfers( + config: &Config<'_>, + maybe_token: Option, + owner: Pubkey, + maybe_account: Option, + bulk_signers: BulkSigners, + allow_confidential_credits: Option, + allow_non_confidential_credits: Option, +) -> CommandResult { + if config.sign_only { + panic!("Sign-only is not yet supported."); } - fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { - arg.requires_all(&[DELEGATE_ADDRESS_ARG.name]) + + let token_account_address = if let Some(account) = maybe_account { + account + } else { + let token_pubkey = + maybe_token.expect("Either a valid token or account address must be provided"); + let token = token_client_from_config(config, &token_pubkey, None)?; + token.get_associated_token_address(&owner) + }; + + let account = config.get_account_checked(&token_account_address).await?; + + let state_with_extension = StateWithExtensionsOwned::::unpack(account.data)?; + let token = token_client_from_config(config, &state_with_extension.base.mint, None)?; + + let existing_extensions: Vec = state_with_extension.get_extension_types()?; + if !existing_extensions.contains(&ExtensionType::ConfidentialTransferAccount) { + panic!( + "Confidential transfer is not yet configured for this account. \ + Use `configure-confidential-transfer-account` command instead." + ); } -} -fn minimum_signers_help_string() -> String { - format!( - "The minimum number of signers required to allow the operation. [{} <= M <= N]", - MIN_SIGNERS - ) -} + let res = if let Some(allow_confidential_credits) = allow_confidential_credits { + let extension_state = state_with_extension + .get_extension::()? + .allow_confidential_credits + .into(); -fn multisig_member_help_string() -> String { - format!( - "The public keys for each of the N signing members of this account. [{} <= N <= {}]", - MIN_SIGNERS, MAX_SIGNERS - ) -} + if extension_state == allow_confidential_credits { + return Ok(format!( + "Confidential transfers are already {}", + if extension_state { + "enabled" + } else { + "disabled" + } + )); + } -fn app<'a, 'b>( - default_decimals: &'a str, - minimum_signers_help: &'b str, - multisig_member_help: &'b str, -) -> App<'a, 'b> { + if allow_confidential_credits { + token + .confidential_transfer_enable_confidential_credits( + &token_account_address, + &owner, + &bulk_signers, + ) + .await + } else { + token + .confidential_transfer_disable_confidential_credits( + &token_account_address, + &owner, + &bulk_signers, + ) + .await + } + } else { + let allow_non_confidential_credits = + allow_non_confidential_credits.expect("Nothing to be done"); + let extension_state = state_with_extension + .get_extension::()? + .allow_non_confidential_credits + .into(); + + if extension_state == allow_non_confidential_credits { + return Ok(format!( + "Non-confidential transfers are already {}", + if extension_state { + "enabled" + } else { + "disabled" + } + )); + } + + if allow_non_confidential_credits { + token + .confidential_transfer_enable_non_confidential_credits( + &token_account_address, + &owner, + &bulk_signers, + ) + .await + } else { + token + .confidential_transfer_disable_non_confidential_credits( + &token_account_address, + &owner, + &bulk_signers, + ) + .await + } + }?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +#[derive(PartialEq, Eq)] +enum ConfidentialInstructionType { + Deposit, + Withdraw, +} + +#[allow(clippy::too_many_arguments)] +async fn command_deposit_withdraw_confidential_tokens( + config: &Config<'_>, + token_pubkey: Pubkey, + owner: Pubkey, + maybe_account: Option, + bulk_signers: BulkSigners, + ui_amount: Option, + mint_decimals: Option, + instruction_type: ConfidentialInstructionType, + elgamal_keypair: Option<&ElGamalKeypair>, + aes_key: Option<&AeKey>, +) -> CommandResult { + if config.sign_only { + panic!("Sign-only is not yet supported."); + } + + // check if mint decimals provided is consistent + let mint_info = config.get_mint_info(&token_pubkey, mint_decimals).await?; + + if !config.sign_only && mint_decimals.is_some() && mint_decimals != Some(mint_info.decimals) { + return Err(format!( + "Decimals {} was provided, but actual value is {}", + mint_decimals.unwrap(), + mint_info.decimals + ) + .into()); + } + + let decimals = if let Some(decimals) = mint_decimals { + decimals + } else { + mint_info.decimals + }; + + // derive ATA if account address not provided + let token_account_address = if let Some(account) = maybe_account { + account + } else { + let token = token_client_from_config(config, &token_pubkey, Some(decimals))?; + token.get_associated_token_address(&owner) + }; + + let account = config.get_account_checked(&token_account_address).await?; + + let state_with_extension = StateWithExtensionsOwned::::unpack(account.data)?; + let token = token_client_from_config(config, &state_with_extension.base.mint, None)?; + + // the amount the user wants to deposit or withdraw, as an f64 + let maybe_amount = + ui_amount.map(|ui_amount| spl_token::ui_amount_to_amount(ui_amount, mint_info.decimals)); + + // the amount we will deposit or withdraw, as a u64 + let amount = if !config.sign_only && instruction_type == ConfidentialInstructionType::Deposit { + let current_balance = state_with_extension.base.amount; + let deposit_amount = maybe_amount.unwrap_or(current_balance); + + println_display( + config, + format!( + "Depositing {} confidential tokens", + spl_token::amount_to_ui_amount(deposit_amount, mint_info.decimals), + ), + ); + + if deposit_amount > current_balance { + return Err(format!( + "Error: Insufficient funds, current balance is {}", + spl_token_2022::amount_to_ui_amount_string_trimmed( + current_balance, + mint_info.decimals + ) + ) + .into()); + } + + deposit_amount + } else if !config.sign_only && instruction_type == ConfidentialInstructionType::Withdraw { + // // TODO: expose account balance decryption in token + // let aes_key = aes_key.expect("AES key must be provided"); + // let current_balance = token + // .confidential_transfer_get_available_balance_with_key(&token_account_address, aes_key) + // .await?; + let withdraw_amount = + maybe_amount.expect("ALL keyword is not currently supported for withdraw"); + + println_display( + config, + format!( + "Withdrawing {} confidential tokens", + spl_token::amount_to_ui_amount(withdraw_amount, mint_info.decimals) + ), + ); + + withdraw_amount + } else { + maybe_amount.unwrap() + }; + + let res = match instruction_type { + ConfidentialInstructionType::Deposit => { + token + .confidential_transfer_deposit( + &token_account_address, + &owner, + amount, + decimals, + &bulk_signers, + ) + .await? + } + ConfidentialInstructionType::Withdraw => { + let elgamal_keypair = elgamal_keypair.expect("ElGamal keypair must be provided"); + let aes_key = aes_key.expect("AES key must be provided"); + + let extension_state = + state_with_extension.get_extension::()?; + let withdraw_account_info = WithdrawAccountInfo::new(extension_state); + + let context_state_authority = config.fee_payer()?; + let context_state_keypair = Keypair::new(); + let context_state_pubkey = context_state_keypair.pubkey(); + + let withdraw_proof_data = + withdraw_account_info.generate_proof_data(amount, elgamal_keypair, aes_key)?; + + // setup proof + token + .create_withdraw_proof_context_state( + &context_state_pubkey, + &context_state_authority.pubkey(), + &withdraw_proof_data, + &context_state_keypair, + ) + .await?; + + // do the withdrawal + token + .confidential_transfer_withdraw( + &token_account_address, + &owner, + Some(&context_state_pubkey), + amount, + decimals, + Some(withdraw_account_info), + elgamal_keypair, + aes_key, + &bulk_signers, + ) + .await?; + + // close context state account + let context_state_authority_pubkey = context_state_authority.pubkey(); + let close_context_state_signers = &[context_state_authority]; + token + .confidential_transfer_close_context_state( + &context_state_pubkey, + &token_account_address, + &context_state_authority_pubkey, + close_context_state_signers, + ) + .await? + } + }; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +#[allow(clippy::too_many_arguments)] +async fn command_apply_pending_balance( + config: &Config<'_>, + maybe_token: Option, + owner: Pubkey, + maybe_account: Option, + bulk_signers: BulkSigners, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, +) -> CommandResult { + if config.sign_only { + panic!("Sign-only is not yet supported."); + } + + // derive ATA if account address not provided + let token_account_address = if let Some(account) = maybe_account { + account + } else { + let token_pubkey = + maybe_token.expect("Either a valid token or account address must be provided"); + let token = token_client_from_config(config, &token_pubkey, None)?; + token.get_associated_token_address(&owner) + }; + + let account = config.get_account_checked(&token_account_address).await?; + + let state_with_extension = StateWithExtensionsOwned::::unpack(account.data)?; + let token = token_client_from_config(config, &state_with_extension.base.mint, None)?; + + let extension_state = state_with_extension.get_extension::()?; + let account_info = ApplyPendingBalanceAccountInfo::new(extension_state); + + let res = token + .confidential_transfer_apply_pending_balance( + &token_account_address, + &owner, + Some(account_info), + elgamal_keypair.secret(), + aes_key, + &bulk_signers, + ) + .await?; + + let tx_return = finish_tx(config, &res, false).await?; + Ok(match tx_return { + TransactionReturnData::CliSignature(signature) => { + config.output_format.formatted_string(&signature) + } + TransactionReturnData::CliSignOnlyData(sign_only_data) => { + config.output_format.formatted_string(&sign_only_data) + } + }) +} + +struct SignOnlyNeedsFullMintSpec {} +impl offline::ArgsConfig for SignOnlyNeedsFullMintSpec { + fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name]) + } + fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_ADDRESS_ARG.name, MINT_DECIMALS_ARG.name]) + } +} + +struct SignOnlyNeedsMintDecimals {} +impl offline::ArgsConfig for SignOnlyNeedsMintDecimals { + fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_DECIMALS_ARG.name]) + } + fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_DECIMALS_ARG.name]) + } +} + +struct SignOnlyNeedsMintAddress {} +impl offline::ArgsConfig for SignOnlyNeedsMintAddress { + fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_ADDRESS_ARG.name]) + } + fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[MINT_ADDRESS_ARG.name]) + } +} + +struct SignOnlyNeedsDelegateAddress {} +impl offline::ArgsConfig for SignOnlyNeedsDelegateAddress { + fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[DELEGATE_ADDRESS_ARG.name]) + } + fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[DELEGATE_ADDRESS_ARG.name]) + } +} + +struct SignOnlyNeedsTransferLamports {} +impl offline::ArgsConfig for SignOnlyNeedsTransferLamports { + fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[TRANSFER_LAMPORTS_ARG.name]) + } + fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> { + arg.requires_all(&[TRANSFER_LAMPORTS_ARG.name]) + } +} + +struct ConfidentialTransferArgs { + sender_elgamal_keypair: ElGamalKeypair, + sender_aes_key: AeKey, + recipient_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, +} + +fn minimum_signers_help_string() -> String { + format!( + "The minimum number of signers required to allow the operation. [{} <= M <= N]", + MIN_SIGNERS + ) +} + +fn multisig_member_help_string() -> String { + format!( + "The public keys for each of the N signing members of this account. [{} <= N <= {}]", + MIN_SIGNERS, MAX_SIGNERS + ) +} + +fn app<'a, 'b>( + default_decimals: &'a str, + minimum_signers_help: &'b str, + multisig_member_help: &'b str, +) -> App<'a, 'b> { App::new(crate_name!()) .about(crate_description!()) .version(crate_version!()) @@ -2539,210 +3636,795 @@ fn app<'a, 'b>( .validator(is_valid_token_program_id) .help("SPL Token program id"), ) - .arg( - Arg::with_name("json_rpc_url") - .short("u") - .long("url") - .value_name("URL_OR_MONIKER") - .takes_value(true) - .global(true) - .validator(is_url_or_moniker) - .help( - "URL for Solana's JSON RPC or moniker (or their first letter): \ - [mainnet-beta, testnet, devnet, localhost] \ - Default from the configuration file." - ), + .arg( + Arg::with_name("json_rpc_url") + .short("u") + .long("url") + .value_name("URL_OR_MONIKER") + .takes_value(true) + .global(true) + .validator(is_url_or_moniker) + .help( + "URL for Solana's JSON RPC or moniker (or their first letter): \ + [mainnet-beta, testnet, devnet, localhost] \ + Default from the configuration file." + ), + ) + .arg(fee_payer_arg().global(true)) + .arg( + Arg::with_name("use_unchecked_instruction") + .long("use-unchecked-instruction") + .takes_value(false) + .global(true) + .hidden(true) + .help("Use unchecked instruction if appropriate. Supports transfer, burn, mint, and approve."), + ) + .bench_subcommand() + .subcommand(SubCommand::with_name(CommandName::CreateToken.into()).about("Create a new token") + .arg( + Arg::with_name("token_keypair") + .value_name("TOKEN_KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .index(1) + .help( + "Specify the token keypair. \ + This may be a keypair file or the ASK keyword. \ + [default: randomly generated keypair]" + ), + ) + .arg( + Arg::with_name("mint_authority") + .long("mint-authority") + .alias("owner") + .value_name("ADDRESS") + .validator(is_valid_pubkey) + .takes_value(true) + .help( + "Specify the mint authority address. \ + Defaults to the client keypair address." + ), + ) + .arg( + Arg::with_name("decimals") + .long("decimals") + .validator(is_mint_decimals) + .value_name("DECIMALS") + .takes_value(true) + .default_value(default_decimals) + .help("Number of base 10 digits to the right of the decimal place"), + ) + .arg( + Arg::with_name("enable_freeze") + .long("enable-freeze") + .takes_value(false) + .help( + "Enable the mint authority to freeze token accounts for this mint" + ), + ) + .arg( + Arg::with_name("enable_close") + .long("enable-close") + .takes_value(false) + .help( + "Enable the mint authority to close this mint" + ), + ) + .arg( + Arg::with_name("interest_rate") + .long("interest-rate") + .value_name("RATE_BPS") + .takes_value(true) + .help( + "Specify the interest rate in basis points. \ + Rate authority defaults to the mint authority." + ), + ) + .arg( + Arg::with_name("metadata_address") + .long("metadata-address") + .value_name("ADDRESS") + .takes_value(true) + .conflicts_with("enable_metadata") + .help( + "Specify address that stores token metadata." + ), + ) + .arg( + Arg::with_name("enable_non_transferable") + .long("enable-non-transferable") + .alias("enable-nontransferable") + .takes_value(false) + .help( + "Permanently force tokens to be non-transferable. Thay may still be burned." + ), + ) + .arg( + Arg::with_name("default_account_state") + .long("default-account-state") + .requires("enable_freeze") + .takes_value(true) + .possible_values(&["initialized", "frozen"]) + .help("Specify that accounts have a default state. \ + Note: specifying \"initialized\" adds an extension, which gives \ + the option of specifying default frozen accounts in the future. \ + This behavior is not the same as the default, which makes it \ + impossible to specify a default account state in the future."), + ) + .arg( + Arg::with_name("transfer_fee") + .long("transfer-fee") + .value_names(&["FEE_IN_BASIS_POINTS", "MAXIMUM_FEE"]) + .takes_value(true) + .number_of_values(2) + .help( + "Add a transfer fee to the mint. \ + The mint authority can set the fee and withdraw collected fees.", + ), + ) + .arg( + Arg::with_name("enable_permanent_delegate") + .long("enable-permanent-delegate") + .takes_value(false) + .help( + "Enable the mint authority to be permanent delegate for this mint" + ), + ) + .arg( + Arg::with_name("enable_confidential_transfers") + .long("enable-confidential-transfers") + .value_names(&["APPROVE-POLICY"]) + .takes_value(true) + .possible_values(&["auto", "manual"]) + .help( + "Enable accounts to make confidential transfers. If \"auto\" \ + is selected, then accounts are automatically approved to make \ + confidential transfers. If \"manual\" is selected, then the \ + confidential transfer mint authority must approve each account \ + before it can make confidential transfers." + ) + ) + .arg( + Arg::with_name("transfer_hook") + .long("transfer-hook") + .value_name("TRANSFER_HOOK_PROGRAM_ID") + .validator(is_valid_pubkey) + .takes_value(true) + .help("Enable the mint authority to set the transfer hook program for this mint"), + ) + .arg( + Arg::with_name("enable_metadata") + .long("enable-metadata") + .conflicts_with("metadata_address") + .takes_value(false) + .help("Enables metadata in the mint. The mint authority must initialize the metadata."), + ) + .nonce_args(true) + .arg(memo_arg()) + ) + .subcommand( + SubCommand::with_name(CommandName::SetInterestRate.into()) + .about("Set the interest rate for an interest-bearing token") + .arg( + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .required(true) + .help("The interest-bearing token address"), + ) + .arg( + Arg::with_name("rate") + .value_name("RATE") + .takes_value(true) + .required(true) + .help("The new interest rate in basis points"), + ) + .arg( + Arg::with_name("rate_authority") + .long("rate-authority") + .validator(is_valid_signer) + .value_name("SIGNER") + .takes_value(true) + .help( + "Specify the rate authority keypair. \ + Defaults to the client keypair address." + ) + ) + ) + .subcommand( + SubCommand::with_name(CommandName::SetTransferHookProgram.into()) + .about("Set the transfer hook program id for a token") + .arg( + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .required(true) + .index(1) + .help("The token address with an existing transfer hook"), + ) + .arg( + Arg::with_name("new_program_id") + .validator(is_valid_pubkey) + .value_name("NEW_PROGRAM_ID") + .takes_value(true) + .required_unless("disable") + .index(2) + .help("The new transfer hook program id to set for this mint"), + ) + .arg( + Arg::with_name("disable") + .long("disable") + .takes_value(false) + .conflicts_with("new_program_id") + .help("Disable transfer hook functionality by setting the program id to None.") + ) + .arg( + Arg::with_name("program_authority") + .long("program-authority") + .validator(is_valid_signer) + .value_name("SIGNER") + .takes_value(true) + .help("Specify the authority keypair. Defaults to the client keypair address.") + ) + ) + .subcommand( + SubCommand::with_name(CommandName::InitializeMetadata.into()) + .about("Initialize metadata extension on a token mint") + .arg( + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .required(true) + .index(1) + .help("The token address with no metadata present"), + ) + .arg( + Arg::with_name("name") + .value_name("TOKEN_NAME") + .takes_value(true) + .required(true) + .index(2) + .help("The name of the token to set in metadata"), + ) + .arg( + Arg::with_name("symbol") + .value_name("TOKEN_SYMBOL") + .takes_value(true) + .required(true) + .index(3) + .help("The symbol of the token to set in metadata"), + ) + .arg( + Arg::with_name("uri") + .value_name("TOKEN_URI") + .takes_value(true) + .required(true) + .index(4) + .help("The URI of the token to set in metadata"), + ) + .arg( + Arg::with_name("mint_authority") + .long("mint-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .help( + "Specify the mint authority keypair. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair." + ), + ) + .arg( + Arg::with_name("update_authority") + .long("update-authority") + .value_name("ADDRESS") + .validator(is_valid_pubkey) + .takes_value(true) + .help( + "Specify the update authority address. \ + Defaults to the client keypair address." + ), + ) ) - .arg(fee_payer_arg().global(true)) - .arg( - Arg::with_name("use_unchecked_instruction") - .long("use-unchecked-instruction") - .takes_value(false) - .global(true) - .hidden(true) - .help("Use unchecked instruction if appropriate. Supports transfer, burn, mint, and approve."), + .subcommand( + SubCommand::with_name(CommandName::UpdateMetadata.into()) + .about("Update metadata on a token mint that has the extension") + .arg( + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .required(true) + .index(1) + .help("The token address with no metadata present"), + ) + .arg( + Arg::with_name("field") + .value_name("FIELD_NAME") + .takes_value(true) + .required(true) + .index(2) + .help("The name of the field to update. Can be a base field (\"name\", \"symbol\", or \"uri\") or any new field to add."), + ) + .arg( + Arg::with_name("value") + .value_name("VALUE_STRING") + .takes_value(true) + .index(3) + .required_unless("remove") + .help("The value for the field"), + ) + .arg( + Arg::with_name("remove") + .long("remove") + .takes_value(false) + .conflicts_with("value") + .help("Remove the key and value for the given field. Does not work with base fields: \"name\", \"symbol\", or \"uri\".") + ) + .arg( + Arg::with_name("authority") + .long("authority") + .validator(is_valid_signer) + .value_name("SIGNER") + .takes_value(true) + .help("Specify the metadata update authority keypair. Defaults to the client keypair.") + ) + .nonce_args(true) + .arg(transfer_lamports_arg()) + .offline_args_config(&SignOnlyNeedsTransferLamports{}), ) - .bench_subcommand() - .subcommand(SubCommand::with_name(CommandName::CreateToken.into()).about("Create a new token") + .subcommand( + SubCommand::with_name(CommandName::CreateAccount.into()) + .about("Create a new token account") .arg( - Arg::with_name("token_keypair") - .value_name("TOKEN_KEYPAIR") + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The token that the account will hold"), + ) + .arg( + Arg::with_name("account_keypair") + .value_name("ACCOUNT_KEYPAIR") .validator(is_valid_signer) .takes_value(true) + .index(2) + .help( + "Specify the account keypair. \ + This may be a keypair file or the ASK keyword. \ + [default: associated token account for --owner]" + ), + ) + .arg( + Arg::with_name("immutable") + .long("immutable") + .takes_value(false) + .help( + "Lock the owner of this token account from ever being changed" + ), + ) + .arg(owner_address_arg()) + .nonce_args(true) + ) + .subcommand( + SubCommand::with_name(CommandName::CreateMultisig.into()) + .about("Create a new account describing an M:N multisignature") + .arg( + Arg::with_name("minimum_signers") + .value_name("MINIMUM_SIGNERS") + .validator(is_multisig_minimum_signers) + .takes_value(true) .index(1) + .required(true) + .help(minimum_signers_help), + ) + .arg( + Arg::with_name("multisig_member") + .value_name("MULTISIG_MEMBER_PUBKEY") + .validator(is_valid_pubkey) + .takes_value(true) + .index(2) + .required(true) + .min_values(MIN_SIGNERS as u64) + .max_values(MAX_SIGNERS as u64) + .help(multisig_member_help), + ) + .arg( + Arg::with_name("address_keypair") + .long("address-keypair") + .value_name("ADDRESS_KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) .help( - "Specify the token keypair. \ + "Specify the address keypair. \ This may be a keypair file or the ASK keyword. \ [default: randomly generated keypair]" ), ) + .nonce_args(true) + ) + .subcommand( + SubCommand::with_name(CommandName::Authorize.into()) + .about("Authorize a new signing keypair to a token or token account") + .arg( + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The address of the token mint or account"), + ) + .arg( + Arg::with_name("authority_type") + .value_name("AUTHORITY_TYPE") + .takes_value(true) + .possible_values(&CliAuthorityType::iter().map(Into::into).collect::>()) + .index(2) + .required(true) + .help("The new authority type. \ + Token mints support `mint`, `freeze`, and mint extension authorities; \ + Token accounts support `owner`, `close`, and account extension \ + authorities."), + ) + .arg( + Arg::with_name("new_authority") + .validator(is_valid_pubkey) + .value_name("AUTHORITY_ADDRESS") + .takes_value(true) + .index(3) + .required_unless("disable") + .help("The address of the new authority"), + ) + .arg( + Arg::with_name("authority") + .long("authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .help( + "Specify the current authority keypair. \ + Defaults to the client keypair." + ), + ) + .arg( + Arg::with_name("disable") + .long("disable") + .takes_value(false) + .conflicts_with("new_authority") + .help("Disable mint, freeze, or close functionality by setting authority to None.") + ) + .arg( + Arg::with_name("force") + .long("force") + .hidden(true) + .help("Force re-authorize the wallet's associate token account. Don't use this flag"), + ) + .arg(multisig_signer_arg()) + .nonce_args(true) + .offline_args(), + ) + .subcommand( + SubCommand::with_name(CommandName::Transfer.into()) + .about("Transfer tokens between accounts") + .arg( + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("Token to transfer"), + ) + .arg( + Arg::with_name("amount") + .validator(is_amount_or_all) + .value_name("TOKEN_AMOUNT") + .takes_value(true) + .index(2) + .required(true) + .help("Amount to send, in tokens; accepts keyword ALL"), + ) + .arg( + Arg::with_name("recipient") + .validator(is_valid_pubkey) + .value_name("RECIPIENT_WALLET_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .index(3) + .required(true) + .help("If a token account address is provided, use it as the recipient. \ + Otherwise assume the recipient address is a user wallet and transfer to \ + the associated token account") + ) .arg( - Arg::with_name("mint_authority") - .long("mint-authority") - .alias("owner") - .value_name("ADDRESS") + Arg::with_name("from") .validator(is_valid_pubkey) + .value_name("SENDER_TOKEN_ACCOUNT_ADDRESS") .takes_value(true) + .long("from") + .help("Specify the sending token account \ + [default: owner's associated token account]") + ) + .arg(owner_keypair_arg_with_value_name("SENDER_TOKEN_OWNER_KEYPAIR") .help( - "Specify the mint authority address. \ - Defaults to the client keypair address." + "Specify the owner of the sending token account. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair.", ), ) .arg( - Arg::with_name("decimals") - .long("decimals") - .validator(is_mint_decimals) - .value_name("DECIMALS") - .takes_value(true) - .default_value(default_decimals) - .help("Number of base 10 digits to the right of the decimal place"), + Arg::with_name("allow_unfunded_recipient") + .long("allow-unfunded-recipient") + .takes_value(false) + .help("Complete the transfer even if the recipient address is not funded") ) .arg( - Arg::with_name("enable_freeze") - .long("enable-freeze") + Arg::with_name("allow_empty_recipient") + .long("allow-empty-recipient") .takes_value(false) - .help( - "Enable the mint authority to freeze token accounts for this mint" - ), + .hidden(true) // Deprecated, use --allow-unfunded-recipient instead ) .arg( - Arg::with_name("enable_close") - .long("enable-close") + Arg::with_name("fund_recipient") + .long("fund-recipient") .takes_value(false) - .help( - "Enable the mint authority to close this mint" - ), + .conflicts_with("confidential") + .help("Create the associated token account for the recipient if doesn't already exist") ) .arg( - Arg::with_name("interest_rate") - .long("interest-rate") - .value_name("RATE_BPS") + Arg::with_name("no_wait") + .long("no-wait") + .takes_value(false) + .help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"), + ) + .arg( + Arg::with_name("allow_non_system_account_recipient") + .long("allow-non-system-account-recipient") + .takes_value(false) + .help("Send tokens to the recipient even if the recipient is not a wallet owned by System Program."), + ) + .arg( + Arg::with_name("no_recipient_is_ata_owner") + .long("no-recipient-is-ata-owner") + .takes_value(false) + .requires("sign_only") + .help("In sign-only mode, specifies that the recipient is the owner of the associated token account rather than an actual token account"), + ) + .arg( + Arg::with_name("recipient_is_ata_owner") + .long("recipient-is-ata-owner") + .takes_value(false) + .hidden(true) + .conflicts_with("no_recipient_is_ata_owner") + .requires("sign_only") + .help("recipient-is-ata-owner is now the default behavior. The option has been deprecated and will be removed in a future release."), + ) + .arg( + Arg::with_name("expected_fee") + .long("expected-fee") + .validator(is_amount) + .value_name("TOKEN_AMOUNT") .takes_value(true) - .help( - "Specify the interest rate in basis points. \ - Rate authority defaults to the mint authority." - ), + .help("Expected fee amount collected during the transfer"), ) .arg( - Arg::with_name("metadata_address") - .long("metadata-address") - .value_name("ADDRESS") + Arg::with_name("transfer_hook_account") + .long("transfer-hook-account") + .validator(validate_transfer_hook_account) + .value_name("PUBKEY:ROLE") .takes_value(true) - .help( - "Specify address that stores token metadata." - ), + .multiple(true) + .min_values(0u64) + .help("Additional pubkey(s) required for a transfer hook and their \ + role, in the format \":\". The role must be \ + \"readonly\", \"writable\". \"readonly-signer\", or \"writable-signer\".\ + Used for offline transaction creation and signing.") ) .arg( - Arg::with_name("enable_non_transferable") - .long("enable-non-transferable") - .alias("enable-nontransferable") + Arg::with_name("confidential") + .long("confidential") .takes_value(false) - .help( - "Permanently force tokens to be non-transferable. Thay may still be burned." - ), + .conflicts_with("fund_recipient") + .help("Send tokens confidentially. Both sender and recipient accounts must \ + be pre-configured for confidential transfers.") ) + .arg(multisig_signer_arg()) + .arg(mint_decimals_arg()) + .nonce_args(true) + .arg(memo_arg()) + .offline_args_config(&SignOnlyNeedsMintDecimals{}), + ) + .subcommand( + SubCommand::with_name(CommandName::Burn.into()) + .about("Burn tokens from an account") .arg( - Arg::with_name("default_account_state") - .long("default-account-state") - .requires("enable_freeze") + Arg::with_name("account") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .possible_values(&["initialized", "frozen"]) - .help("Specify that accounts have a default state. \ - Note: specifying \"initialized\" adds an extension, which gives \ - the option of specifying default frozen accounts in the future. \ - This behavior is not the same as the default, which makes it \ - impossible to specify a default account state in the future."), + .index(1) + .required(true) + .help("The token account address to burn from"), ) .arg( - Arg::with_name("transfer_fee") - .long("transfer-fee") - .value_names(&["FEE_IN_BASIS_POINTS", "MAXIMUM_FEE"]) + Arg::with_name("amount") + .validator(is_amount) + .value_name("TOKEN_AMOUNT") .takes_value(true) - .number_of_values(2) + .index(2) + .required(true) + .help("Amount to burn, in tokens"), + ) + .arg(owner_keypair_arg_with_value_name("TOKEN_OWNER_KEYPAIR") .help( - "Add a transfer fee to the mint. \ - The mint authority can set the fee and withdraw collected fees.", + "Specify the burnt token owner account. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair.", ), ) + .arg(multisig_signer_arg()) + .mint_args() + .nonce_args(true) + .arg(memo_arg()) + .offline_args_config(&SignOnlyNeedsFullMintSpec{}), + ) + .subcommand( + SubCommand::with_name(CommandName::Mint.into()) + .about("Mint new tokens") .arg( - Arg::with_name("enable_permanent_delegate") - .long("enable-permanent-delegate") - .takes_value(false) - .help( - "Enable the mint authority to be permanent delegate for this mint" - ), + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The token to mint"), ) .arg( - Arg::with_name("enable_confidential_transfers") - .long("enable-confidential-transfers") - .value_names(&["APPROVE-POLICY"]) + Arg::with_name("amount") + .validator(is_amount) + .value_name("TOKEN_AMOUNT") + .takes_value(true) + .index(2) + .required(true) + .help("Amount to mint, in tokens"), + ) + .arg( + Arg::with_name("recipient") + .validator(is_valid_pubkey) + .value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .conflicts_with("recipient_owner") + .index(3) + .help("The token account address of recipient \ + [default: associated token account for --mint-authority]"), + ) + .arg( + Arg::with_name("recipient_owner") + .long("recipient-owner") + .validator(is_valid_pubkey) + .value_name("RECIPIENT_WALLET_ADDRESS") + .takes_value(true) + .conflicts_with("recipient") + .help("The owner of the recipient associated token account"), + ) + .arg( + Arg::with_name("mint_authority") + .long("mint-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) .takes_value(true) - .possible_values(&["auto", "manual"]) .help( - "Enable accounts to make confidential transfers. If \"auto\" \ - is selected, then accounts are automatically approved to make \ - confidential transfers. If \"manual\" is selected, then the \ - confidential transfer mint authority must approve each account \ - before it can make confidential transfers." - ) + "Specify the mint authority keypair. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair." + ), ) + .arg(mint_decimals_arg()) + .arg(multisig_signer_arg()) .nonce_args(true) .arg(memo_arg()) + .offline_args_config(&SignOnlyNeedsMintDecimals{}), ) .subcommand( - SubCommand::with_name(CommandName::SetInterestRate.into()) - .about("Set the interest rate for an interest-bearing token") + SubCommand::with_name(CommandName::Freeze.into()) + .about("Freeze a token account") .arg( - Arg::with_name("token") + Arg::with_name("account") .validator(is_valid_pubkey) - .value_name("TOKEN_MINT_ADDRESS") + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) + .index(1) .required(true) - .help("The interest-bearing token address"), + .help("The address of the token account to freeze"), ) .arg( - Arg::with_name("rate") - .value_name("RATE") + Arg::with_name("freeze_authority") + .long("freeze-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .help( + "Specify the freeze authority keypair. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair." + ), + ) + .arg(mint_address_arg()) + .arg(multisig_signer_arg()) + .nonce_args(true) + .offline_args_config(&SignOnlyNeedsMintAddress{}), + ) + .subcommand( + SubCommand::with_name(CommandName::Thaw.into()) + .about("Thaw a token account") + .arg( + Arg::with_name("account") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) + .index(1) .required(true) - .help("The new interest rate in basis points"), + .help("The address of the token account to thaw"), ) .arg( - Arg::with_name("rate_authority") - .long("rate-authority") - .validator(is_valid_signer) - .value_name("SIGNER") - .takes_value(true) - .help( - "Specify the rate authority keypair. \ - Defaults to the client keypair address." - ) + Arg::with_name("freeze_authority") + .long("freeze-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .help( + "Specify the freeze authority keypair. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair." + ), ) + .arg(mint_address_arg()) + .arg(multisig_signer_arg()) + .nonce_args(true) + .offline_args_config(&SignOnlyNeedsMintAddress{}), ) .subcommand( - SubCommand::with_name(CommandName::CreateAccount.into()) - .about("Create a new token account") + SubCommand::with_name(CommandName::Wrap.into()) + .about("Wrap native SOL in a SOL token account") .arg( - Arg::with_name("token") - .validator(is_valid_pubkey) - .value_name("TOKEN_MINT_ADDRESS") + Arg::with_name("amount") + .validator(is_amount) + .value_name("AMOUNT") .takes_value(true) .index(1) .required(true) - .help("The token that the account will hold"), + .help("Amount of SOL to wrap"), ) .arg( - Arg::with_name("account_keypair") - .value_name("ACCOUNT_KEYPAIR") + Arg::with_name("wallet_keypair") + .alias("owner") + .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) - .index(2) .help( - "Specify the account keypair. \ + "Specify the keypair for the wallet which will have its native SOL wrapped. \ + This wallet will be assigned as the owner of the wrapped SOL token account. \ This may be a keypair file or the ASK keyword. \ - [default: associated token account for --owner]" + Defaults to the client keypair." ), ) + .arg( + Arg::with_name("create_aux_account") + .takes_value(false) + .long("create-aux-account") + .help("Wrap SOL in an auxiliary account instead of associated token account"), + ) .arg( Arg::with_name("immutable") .long("immutable") @@ -2751,439 +4433,397 @@ fn app<'a, 'b>( "Lock the owner of this token account from ever being changed" ), ) - .arg(owner_address_arg()) .nonce_args(true) + .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::CreateMultisig.into()) - .about("Create a new account describing an M:N multisignature") - .arg( - Arg::with_name("minimum_signers") - .value_name("MINIMUM_SIGNERS") - .validator(is_multisig_minimum_signers) - .takes_value(true) - .index(1) - .required(true) - .help(minimum_signers_help), - ) + SubCommand::with_name(CommandName::Unwrap.into()) + .about("Unwrap a SOL token account") .arg( - Arg::with_name("multisig_member") - .value_name("MULTISIG_MEMBER_PUBKEY") + Arg::with_name("account") .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .index(2) - .required(true) - .min_values(MIN_SIGNERS as u64) - .max_values(MAX_SIGNERS as u64) - .help(multisig_member_help), + .index(1) + .help("The address of the auxiliary token account to unwrap \ + [default: associated token account for --owner]"), ) .arg( - Arg::with_name("address_keypair") - .long("address-keypair") - .value_name("ADDRESS_KEYPAIR") + Arg::with_name("wallet_keypair") + .alias("owner") + .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) .help( - "Specify the address keypair. \ + "Specify the keypair for the wallet which owns the wrapped SOL. \ + This wallet will receive the unwrapped SOL. \ This may be a keypair file or the ASK keyword. \ - [default: randomly generated keypair]" + Defaults to the client keypair." ), ) + .arg(owner_address_arg()) + .arg(multisig_signer_arg()) .nonce_args(true) + .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::Authorize.into()) - .about("Authorize a new signing keypair to a token or token account") + SubCommand::with_name(CommandName::Approve.into()) + .about("Approve a delegate for a token account") .arg( - Arg::with_name("address") + Arg::with_name("account") .validator(is_valid_pubkey) - .value_name("TOKEN_ADDRESS") + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) .index(1) .required(true) - .help("The address of the token mint or account"), + .help("The address of the token account to delegate"), ) .arg( - Arg::with_name("authority_type") - .value_name("AUTHORITY_TYPE") + Arg::with_name("amount") + .validator(is_amount) + .value_name("TOKEN_AMOUNT") .takes_value(true) - .possible_values(&[ - "mint", "freeze", "owner", "close", - "close-mint", "transfer-fee-config", "withheld-withdraw", - "interest-rate", "permanent-delegate", "confidential-transfer-mint" - ]) .index(2) .required(true) - .help("The new authority type. \ - Token mints support `mint`, `freeze`, and mint extension authorities; \ - Token accounts support `owner`, `close`, and account extension \ - authorities."), + .help("Amount to approve, in tokens"), ) .arg( - Arg::with_name("new_authority") + Arg::with_name("delegate") .validator(is_valid_pubkey) - .value_name("AUTHORITY_ADDRESS") + .value_name("DELEGATE_TOKEN_ACCOUNT_ADDRESS") .takes_value(true) .index(3) - .required_unless("disable") - .help("The address of the new authority"), + .required(true) + .help("The token account address of delegate"), ) .arg( - Arg::with_name("authority") - .long("authority") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) - .help( - "Specify the current authority keypair. \ - Defaults to the client keypair." - ), + owner_keypair_arg() ) + .arg(multisig_signer_arg()) + .mint_args() + .nonce_args(true) + .offline_args_config(&SignOnlyNeedsFullMintSpec{}), + ) + .subcommand( + SubCommand::with_name(CommandName::Revoke.into()) + .about("Revoke a delegate's authority") .arg( - Arg::with_name("disable") - .long("disable") - .takes_value(false) - .conflicts_with("new_authority") - .help("Disable mint, freeze, or close functionality by setting authority to None.") + Arg::with_name("account") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The address of the token account"), ) - .arg( - Arg::with_name("force") - .long("force") - .hidden(true) - .help("Force re-authorize the wallet's associate token account. Don't use this flag"), + .arg(owner_keypair_arg() ) + .arg(delegate_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) - .offline_args(), + .offline_args_config(&SignOnlyNeedsDelegateAddress{}), ) .subcommand( - SubCommand::with_name(CommandName::Transfer.into()) - .about("Transfer tokens between accounts") + SubCommand::with_name(CommandName::Close.into()) + .about("Close a token account") .arg( Arg::with_name("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("Token to transfer"), - ) - .arg( - Arg::with_name("amount") - .validator(is_amount_or_all) - .value_name("TOKEN_AMOUNT") - .takes_value(true) - .index(2) - .required(true) - .help("Amount to send, in tokens; accepts keyword ALL"), + .required_unless("address") + .help("Token of the associated account to close. \ + To close a specific account, use the `--address` parameter instead"), ) .arg( Arg::with_name("recipient") + .long("recipient") .validator(is_valid_pubkey) - .value_name("RECIPIENT_WALLET_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS") + .value_name("REFUND_ACCOUNT_ADDRESS") .takes_value(true) - .index(3) - .required(true) - .help("If a token account address is provided, use it as the recipient. \ - Otherwise assume the recipient address is a user wallet and transfer to \ - the associated token account") + .help("The address of the account to receive remaining SOL [default: --owner]"), ) .arg( - Arg::with_name("from") - .validator(is_valid_pubkey) - .value_name("SENDER_TOKEN_ACCOUNT_ADDRESS") + Arg::with_name("close_authority") + .long("close-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) .takes_value(true) - .long("from") - .help("Specify the sending token account \ - [default: owner's associated token account]") - ) - .arg(owner_keypair_arg_with_value_name("SENDER_TOKEN_OWNER_KEYPAIR") .help( - "Specify the owner of the sending token account. \ + "Specify the token's close authority if it has one, \ + otherwise specify the token's owner keypair. \ This may be a keypair file or the ASK keyword. \ Defaults to the client keypair.", ), ) .arg( - Arg::with_name("allow_unfunded_recipient") - .long("allow-unfunded-recipient") - .takes_value(false) - .help("Complete the transfer even if the recipient address is not funded") - ) - .arg( - Arg::with_name("allow_empty_recipient") - .long("allow-empty-recipient") - .takes_value(false) - .hidden(true) // Deprecated, use --allow-unfunded-recipient instead - ) - .arg( - Arg::with_name("fund_recipient") - .long("fund-recipient") - .takes_value(false) - .help("Create the associated token account for the recipient if doesn't already exist") - ) - .arg( - Arg::with_name("no_wait") - .long("no-wait") - .takes_value(false) - .help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"), - ) - .arg( - Arg::with_name("allow_non_system_account_recipient") - .long("allow-non-system-account-recipient") - .takes_value(false) - .help("Send tokens to the recipient even if the recipient is not a wallet owned by System Program."), - ) - .arg( - Arg::with_name("recipient_is_ata_owner") - .long("recipient-is-ata-owner") - .takes_value(false) - .requires("sign_only") - .help("In sign-only mode, specifies that the recipient is the owner of the associated token account rather than an actual token account"), - ) - .arg( - Arg::with_name("expected_fee") - .long("expected-fee") - .validator(is_amount) - .value_name("TOKEN_AMOUNT") + Arg::with_name("address") + .long("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .help("Expected fee amount collected during the transfer"), + .conflicts_with("token") + .help("Specify the token account to close \ + [default: owner's associated token account]"), ) + .arg(owner_address_arg()) .arg(multisig_signer_arg()) - .arg(mint_decimals_arg()) .nonce_args(true) - .arg(memo_arg()) - .offline_args_config(&SignOnlyNeedsMintDecimals{}), ) .subcommand( - SubCommand::with_name(CommandName::Burn.into()) - .about("Burn tokens from an account") + SubCommand::with_name(CommandName::CloseMint.into()) + .about("Close a token mint") .arg( - Arg::with_name("account") + Arg::with_name("token") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) .required(true) - .help("The token account address to burn from"), + .help("Token to close"), ) .arg( - Arg::with_name("amount") - .validator(is_amount) - .value_name("TOKEN_AMOUNT") + Arg::with_name("recipient") + .long("recipient") + .validator(is_valid_pubkey) + .value_name("REFUND_ACCOUNT_ADDRESS") .takes_value(true) - .index(2) - .required(true) - .help("Amount to burn, in tokens"), + .help("The address of the account to receive remaining SOL [default: --owner]"), ) - .arg(owner_keypair_arg_with_value_name("TOKEN_OWNER_KEYPAIR") + .arg( + Arg::with_name("close_authority") + .long("close-authority") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) .help( - "Specify the burnt token owner account. \ + "Specify the token's close authority. \ This may be a keypair file or the ASK keyword. \ Defaults to the client keypair.", ), ) + .arg(owner_address_arg()) .arg(multisig_signer_arg()) - .mint_args() .nonce_args(true) - .arg(memo_arg()) - .offline_args_config(&SignOnlyNeedsFullMintSpec{}), + .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::Mint.into()) - .about("Mint new tokens") + SubCommand::with_name(CommandName::Balance.into()) + .about("Get token account balance") .arg( Arg::with_name("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("The token to mint"), + .required_unless("address") + .help("Token of associated account. To query a specific account, use the `--address` parameter instead"), ) + .arg(owner_address_arg().conflicts_with("address")) .arg( - Arg::with_name("amount") - .validator(is_amount) - .value_name("TOKEN_AMOUNT") + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .index(2) - .required(true) - .help("Amount to mint, in tokens"), - ) + .long("address") + .conflicts_with("token") + .help("Specify the token account to query \ + [default: owner's associated token account]"), + ), + ) + .subcommand( + SubCommand::with_name(CommandName::Supply.into()) + .about("Get token supply") .arg( - Arg::with_name("recipient") + Arg::with_name("token") .validator(is_valid_pubkey) - .value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) - .conflicts_with("recipient_owner") - .index(3) - .help("The token account address of recipient \ - [default: associated token account for --mint-authority]"), - ) + .index(1) + .required(true) + .help("The token address"), + ), + ) + .subcommand( + SubCommand::with_name(CommandName::Accounts.into()) + .about("List all token accounts by owner") .arg( - Arg::with_name("recipient_owner") - .long("recipient-owner") + Arg::with_name("token") .validator(is_valid_pubkey) - .value_name("RECIPIENT_WALLET_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) - .conflicts_with("recipient") - .help("The owner of the recipient associated token account"), + .index(1) + .help("Limit results to the given token. [Default: list accounts for all tokens]"), ) .arg( - Arg::with_name("mint_authority") - .long("mint-authority") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) + Arg::with_name("delegated") + .long("delegated") + .takes_value(false) + .conflicts_with("externally_closeable") .help( - "Specify the mint authority keypair. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." + "Limit results to accounts with transfer delegations" ), ) - .arg(mint_decimals_arg()) - .arg(multisig_signer_arg()) - .nonce_args(true) - .arg(memo_arg()) - .offline_args_config(&SignOnlyNeedsMintDecimals{}), - ) - .subcommand( - SubCommand::with_name(CommandName::Freeze.into()) - .about("Freeze a token account") .arg( - Arg::with_name("account") - .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") - .takes_value(true) - .index(1) - .required(true) - .help("The address of the token account to freeze"), + Arg::with_name("externally_closeable") + .long("externally-closeable") + .takes_value(false) + .conflicts_with("delegated") + .help( + "Limit results to accounts with external close authorities" + ), ) .arg( - Arg::with_name("freeze_authority") - .long("freeze-authority") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) + Arg::with_name("addresses_only") + .long("addresses-only") + .takes_value(false) + .conflicts_with("verbose") + .conflicts_with("output_format") .help( - "Specify the freeze authority keypair. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." + "Print token account addresses only" ), ) - .arg(mint_address_arg()) - .arg(multisig_signer_arg()) - .nonce_args(true) - .offline_args_config(&SignOnlyNeedsMintAddress{}), + .arg(owner_address_arg()) ) .subcommand( - SubCommand::with_name(CommandName::Thaw.into()) - .about("Thaw a token account") + SubCommand::with_name(CommandName::Address.into()) + .about("Get wallet address") .arg( - Arg::with_name("account") + Arg::with_name("token") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) - .index(1) - .required(true) - .help("The address of the token account to thaw"), + .long("token") + .requires("verbose") + .help("Return the associated token address for the given token. \ + [Default: return the client keypair address]") ) .arg( - Arg::with_name("freeze_authority") - .long("freeze-authority") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) - .help( - "Specify the freeze authority keypair. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." - ), - ) - .arg(mint_address_arg()) - .arg(multisig_signer_arg()) - .nonce_args(true) - .offline_args_config(&SignOnlyNeedsMintAddress{}), + owner_address_arg() + .requires("token") + .help("Return the associated token address for the given owner. \ + [Default: return the associated token address for the client keypair]"), + ), ) .subcommand( - SubCommand::with_name(CommandName::Wrap.into()) - .about("Wrap native SOL in a SOL token account") + SubCommand::with_name(CommandName::AccountInfo.into()) + .about("Query details of an SPL Token account by address (DEPRECATED: use `spl-token display`)") + .setting(AppSettings::Hidden) .arg( - Arg::with_name("amount") - .validator(is_amount) - .value_name("AMOUNT") + Arg::with_name("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("Amount of SOL to wrap"), + .conflicts_with("address") + .required_unless("address") + .help("Token of associated account. \ + To query a specific account, use the `--address` parameter instead"), ) .arg( - Arg::with_name("wallet_keypair") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) + Arg::with_name(OWNER_ADDRESS_ARG.name) .takes_value(true) - .help( - "Specify the keypair for the wallet which will have its native SOL wrapped. \ - This wallet will be assigned as the owner of the wrapped SOL token account. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." - ), + .value_name("OWNER_ADDRESS") + .validator(is_valid_signer) + .help(OWNER_ADDRESS_ARG.help) + .index(2) + .conflicts_with("address") + .help("Owner of the associated account for the specified token. \ + To query a specific account, use the `--address` parameter instead. \ + Defaults to the client keypair."), ) .arg( - Arg::with_name("create_aux_account") - .takes_value(false) - .long("create-aux-account") - .help("Wrap SOL in an auxiliary account instead of associated token account"), + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .long("address") + .conflicts_with("token") + .help("Specify the token account to query"), + ), + ) + .subcommand( + SubCommand::with_name(CommandName::MultisigInfo.into()) + .about("Query details of an SPL Token multisig account by address (DEPRECATED: use `spl-token display`)") + .setting(AppSettings::Hidden) + .arg( + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("MULTISIG_ACCOUNT_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The address of the SPL Token multisig account to query"), + ), + ) + .subcommand( + SubCommand::with_name(CommandName::Display.into()) + .about("Query details of an SPL Token mint, account, or multisig by address") + .arg( + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ADDRESS") + .takes_value(true) + .index(1) + .required(true) + .help("The address of the SPL Token mint, account, or multisig to query"), + ), + ) + .subcommand( + SubCommand::with_name(CommandName::Gc.into()) + .about("Cleanup unnecessary token accounts") + .arg(owner_keypair_arg()) + .arg( + Arg::with_name("close_empty_associated_accounts") + .long("close-empty-associated-accounts") + .takes_value(false) + .help("close all empty associated token accounts (to get SOL back)") ) + ) + .subcommand( + SubCommand::with_name(CommandName::SyncNative.into()) + .about("Sync a native SOL token account to its underlying lamports") .arg( - Arg::with_name("immutable") - .long("immutable") - .takes_value(false) - .help( - "Lock the owner of this token account from ever being changed" - ), + owner_address_arg() + .index(1) + .conflicts_with("address") + .help("Owner of the associated account for the native token. \ + To query a specific account, use the `--address` parameter instead. \ + Defaults to the client keypair."), ) - .nonce_args(true) - .offline_args(), + .arg( + Arg::with_name("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .long("address") + .conflicts_with("owner") + .help("Specify the specific token account address to sync"), + ), ) .subcommand( - SubCommand::with_name(CommandName::Unwrap.into()) - .about("Unwrap a SOL token account") + SubCommand::with_name(CommandName::EnableRequiredTransferMemos.into()) + .about("Enable required transfer memos for token account") .arg( Arg::with_name("account") .validator(is_valid_pubkey) .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) .index(1) - .help("The address of the auxiliary token account to unwrap \ - [default: associated token account for --owner]"), + .required(true) + .help("The address of the token account to require transfer memos for") ) .arg( - Arg::with_name("wallet_keypair") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) - .help( - "Specify the keypair for the wallet which owns the wrapped SOL. \ - This wallet will receive the unwrapped SOL. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." - ), + owner_address_arg() ) - .arg(owner_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) - .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::Approve.into()) - .about("Approve a delegate for a token account") + SubCommand::with_name(CommandName::DisableRequiredTransferMemos.into()) + .about("Disable required transfer memos for token account") .arg( Arg::with_name("account") .validator(is_valid_pubkey) @@ -3191,37 +4831,35 @@ fn app<'a, 'b>( .takes_value(true) .index(1) .required(true) - .help("The address of the token account to delegate"), + .help("The address of the token account to stop requiring transfer memos for"), ) .arg( - Arg::with_name("amount") - .validator(is_amount) - .value_name("TOKEN_AMOUNT") - .takes_value(true) - .index(2) - .required(true) - .help("Amount to approve, in tokens"), + owner_address_arg() ) + .arg(multisig_signer_arg()) + .nonce_args(true) + ) + .subcommand( + SubCommand::with_name(CommandName::EnableCpiGuard.into()) + .about("Enable CPI Guard for token account") .arg( - Arg::with_name("delegate") + Arg::with_name("account") .validator(is_valid_pubkey) - .value_name("DELEGATE_TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .index(3) + .index(1) .required(true) - .help("The token account address of delegate"), + .help("The address of the token account to enable CPI Guard for") ) .arg( - owner_keypair_arg() + owner_address_arg() ) .arg(multisig_signer_arg()) - .mint_args() .nonce_args(true) - .offline_args_config(&SignOnlyNeedsFullMintSpec{}), ) .subcommand( - SubCommand::with_name(CommandName::Revoke.into()) - .about("Revoke a delegate's authority") + SubCommand::with_name(CommandName::DisableCpiGuard.into()) + .about("Disable CPI Guard for token account") .arg( Arg::with_name("account") .validator(is_valid_pubkey) @@ -3229,67 +4867,55 @@ fn app<'a, 'b>( .takes_value(true) .index(1) .required(true) - .help("The address of the token account"), + .help("The address of the token account to disable CPI Guard for"), ) - .arg(owner_keypair_arg() + .arg( + owner_address_arg() ) - .arg(delegate_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) - .offline_args_config(&SignOnlyNeedsDelegateAddress{}), ) .subcommand( - SubCommand::with_name(CommandName::Close.into()) - .about("Close a token account") + SubCommand::with_name(CommandName::UpdateDefaultAccountState.into()) + .about("Updates default account state for the mint. Requires the default account state extension.") .arg( Arg::with_name("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required_unless("address") - .help("Token of the associated account to close. \ - To close a specific account, use the `--address` parameter instead"), + .required(true) + .help("The address of the token mint to update default account state"), ) .arg( - Arg::with_name("recipient") - .long("recipient") - .validator(is_valid_pubkey) - .value_name("REFUND_ACCOUNT_ADDRESS") + Arg::with_name("state") + .value_name("STATE") .takes_value(true) - .help("The address of the account to receive remaining SOL [default: --owner]"), + .possible_values(&["initialized", "frozen"]) + .index(2) + .required(true) + .help("The new default account state."), ) .arg( - Arg::with_name("close_authority") - .long("close-authority") - .alias("owner") + Arg::with_name("freeze_authority") + .long("freeze-authority") .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) .help( - "Specify the token's close authority if it has one, \ - otherwise specify the token's owner keypair. \ + "Specify the token's freeze authority. \ This may be a keypair file or the ASK keyword. \ Defaults to the client keypair.", ), ) - .arg( - Arg::with_name("address") - .long("address") - .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") - .takes_value(true) - .conflicts_with("token") - .help("Specify the token account to close \ - [default: owner's associated token account]"), - ) .arg(owner_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) + .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::CloseMint.into()) - .about("Close a token mint") + SubCommand::with_name(CommandName::UpdateMetadataAddress.into()) + .about("Updates metadata pointer address for the mint. Requires the metadata pointer extension.") .arg( Arg::with_name("token") .validator(is_valid_pubkey) @@ -3297,234 +4923,267 @@ fn app<'a, 'b>( .takes_value(true) .index(1) .required(true) - .help("Token to close"), + .help("The address of the token mint to update the metadata pointer address"), ) .arg( - Arg::with_name("recipient") - .long("recipient") + Arg::with_name("metadata_address") + .index(2) .validator(is_valid_pubkey) - .value_name("REFUND_ACCOUNT_ADDRESS") + .value_name("METADATA_ADDRESS") .takes_value(true) - .help("The address of the account to receive remaining SOL [default: --owner]"), + .required_unless("disable") + .help("Specify address that stores token's metadata-pointer"), ) .arg( - Arg::with_name("close_authority") - .long("close-authority") + Arg::with_name("disable") + .long("disable") + .takes_value(false) + .conflicts_with("metadata_address") + .help("Unset metadata pointer address.") + ) + .arg( + Arg::with_name("authority") + .long("authority") .value_name("KEYPAIR") .validator(is_valid_signer) .takes_value(true) .help( - "Specify the token's close authority. \ + "Specify the token's metadata-pointer authority. \ This may be a keypair file or the ASK keyword. \ Defaults to the client keypair.", ), ) - .arg(owner_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) - .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::Balance.into()) - .about("Get token account balance") + SubCommand::with_name(CommandName::WithdrawWithheldTokens.into()) + .about("Withdraw withheld transfer fee tokens from mint and / or account(s)") .arg( - Arg::with_name("token") + Arg::with_name("account") .validator(is_valid_pubkey) - .value_name("TOKEN_MINT_ADDRESS") + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) .index(1) - .required_unless("address") - .help("Token of associated account. To query a specific account, use the `--address` parameter instead"), + .required(true) + .help("The address of the token account to receive withdrawn tokens"), ) - .arg(owner_address_arg().conflicts_with("address")) .arg( - Arg::with_name("address") + Arg::with_name("source") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("ACCOUNT_ADDRESS") .takes_value(true) - .long("address") - .conflicts_with("token") - .help("Specify the token account to query \ - [default: owner's associated token account]"), - ), + .multiple(true) + .min_values(0u64) + .help("The token accounts to withdraw from") + ) + .arg( + Arg::with_name("include_mint") + .long("include-mint") + .takes_value(false) + .help("Also withdraw withheld tokens from the mint"), + ) + .arg( + Arg::with_name("withdraw_withheld_authority") + .long("withdraw-withheld-authority") + .alias("owner") + .value_name("KEYPAIR") + .validator(is_valid_signer) + .takes_value(true) + .help( + "Specify the withdraw withheld authority keypair. \ + This may be a keypair file or the ASK keyword. \ + Defaults to the client keypair." + ), + ) + .arg(owner_address_arg()) + .arg(multisig_signer_arg()) ) .subcommand( - SubCommand::with_name(CommandName::Supply.into()) - .about("Get token supply") + SubCommand::with_name(CommandName::SetTransferFee.into()) + .about("Set the transfer fee for a token with a configured transfer fee") .arg( Arg::with_name("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) - .index(1) .required(true) - .help("The token address"), - ), + .help("The interest-bearing token address"), + ) + .arg( + Arg::with_name("transfer_fee_basis_points") + .value_name("FEE_IN_BASIS_POINTS") + .takes_value(true) + .required(true) + .help("The new transfer fee in basis points"), + ) + .arg( + Arg::with_name("maximum_fee") + .value_name("TOKEN_AMOUNT") + .validator(is_amount) + .takes_value(true) + .required(true) + .help("The new maximum transfer fee in UI amount"), + ) + .arg( + Arg::with_name("transfer_fee_authority") + .long("transfer-fee-authority") + .validator(is_valid_signer) + .value_name("SIGNER") + .takes_value(true) + .help( + "Specify the rate authority keypair. \ + Defaults to the client keypair address." + ) + ) + .arg(mint_decimals_arg()) + .offline_args_config(&SignOnlyNeedsMintDecimals{}) + ) + .subcommand( + SubCommand::with_name(CommandName::WithdrawExcessLamports.into()) + .about("Withdraw lamports from a Token Program owned account") + .arg( + Arg::with_name("from") + .validator(is_valid_pubkey) + .value_name("SOURCE_ACCOUNT_ADDRESS") + .takes_value(true) + .required(true) + .help("Specify the address of the account to recover lamports from"), + ) + .arg( + Arg::with_name("recipient") + .validator(is_valid_pubkey) + .value_name("REFUND_ACCOUNT_ADDRESS") + .takes_value(true) + .required(true) + .help("Specify the address of the account to send lamports to"), + ) + .arg(owner_address_arg()) + .arg(multisig_signer_arg()) ) .subcommand( - SubCommand::with_name(CommandName::Accounts.into()) - .about("List all token accounts by owner") + SubCommand::with_name(CommandName::UpdateConfidentialTransferSettings.into()) + .about("Update confidential transfer configuation for a token") .arg( Arg::with_name("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .help("Limit results to the given token. [Default: list accounts for all tokens]"), + .required(true) + .help("The address of the token mint to update confidential transfer configuration for") ) .arg( - Arg::with_name("delegated") - .long("delegated") - .takes_value(false) - .conflicts_with("externally_closeable") + Arg::with_name("approve_policy") + .long("approve-policy") + .value_name("APPROVE_POLICY") + .takes_value(true) + .possible_values(&["auto", "manual"]) .help( - "Limit results to accounts with transfer delegations" - ), + "Policy for enabling accounts to make confidential transfers. If \"auto\" \ + is selected, then accounts are automatically approved to make \ + confidential transfers. If \"manual\" is selected, then the \ + confidential transfer mint authority must approve each account \ + before it can make confidential transfers." + ) ) .arg( - Arg::with_name("externally_closeable") - .long("externally-closeable") - .takes_value(false) - .conflicts_with("delegated") + Arg::with_name("auditor_pubkey") + .long("auditor-pubkey") + .value_name("AUDITOR_PUBKEY") + .takes_value(true) .help( - "Limit results to accounts with external close authorities" - ), + "The auditor encryption public key. The corresponding private key for \ + this auditor public key can be used to decrypt all confidential \ + transfers involving tokens from this mint. Currently, the auditor \ + public key can only be specified as a direct *base64* encoding of \ + an ElGamal public key. More methods of specifying the auditor public \ + key will be supported in a future version. To disable auditability \ + feature for the token, use \"none\"." + ) ) - .arg( - Arg::with_name("addresses_only") - .long("addresses-only") - .takes_value(false) - .conflicts_with("verbose") - .conflicts_with("output_format") - .help( - "Print token account addresses only" - ), + .group( + ArgGroup::with_name("update_fields").args(&["approve_policy", "auditor_pubkey"]) + .required(true) ) - .arg(owner_address_arg()) - ) - .subcommand( - SubCommand::with_name(CommandName::Address.into()) - .about("Get wallet address") .arg( - Arg::with_name("token") - .validator(is_valid_pubkey) - .value_name("TOKEN_MINT_ADDRESS") + Arg::with_name("confidential_transfer_authority") + .long("confidential-transfer-authority") + .validator(is_valid_signer) + .value_name("SIGNER") .takes_value(true) - .long("token") - .requires("verbose") - .help("Return the associated token address for the given token. \ - [Default: return the client keypair address]") + .help( + "Specify the confidential transfer authority keypair. \ + Defaults to the client keypair address." + ) ) - .arg( - owner_address_arg() - .requires("token") - .help("Return the associated token address for the given owner. \ - [Default: return the associated token address for the client keypair]"), - ), + .nonce_args(true) + .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::AccountInfo.into()) - .about("Query details of an SPL Token account by address (DEPRECATED: use `spl-token display`)") - .setting(AppSettings::Hidden) + SubCommand::with_name(CommandName::ConfigureConfidentialTransferAccount.into()) + .about("Configure confidential transfers for token account") .arg( Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .conflicts_with("address") .required_unless("address") - .help("Token of associated account. \ - To query a specific account, use the `--address` parameter instead"), - ) - .arg( - owner_address_arg() - .index(2) - .conflicts_with("address") - .help("Owner of the associated account for the specified token. \ - To query a specific account, use the `--address` parameter instead. \ - Defaults to the client keypair."), + .help("The token address with confidential transfers enabled"), ) .arg( Arg::with_name("address") + .long("address") .validator(is_valid_pubkey) .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .long("address") .conflicts_with("token") - .help("Specify the token account to query"), - ), - ) - .subcommand( - SubCommand::with_name(CommandName::MultisigInfo.into()) - .about("Query details of an SPL Token multisig account by address (DEPRECATED: use `spl-token display`)") - .setting(AppSettings::Hidden) - .arg( - Arg::with_name("address") - .validator(is_valid_pubkey) - .value_name("MULTISIG_ACCOUNT_ADDRESS") - .takes_value(true) - .index(1) - .required(true) - .help("The address of the SPL Token multisig account to query"), - ), - ) - .subcommand( - SubCommand::with_name(CommandName::Display.into()) - .about("Query details of an SPL Token mint, account, or multisig by address") + .help("The address of the token account to configure confidential transfers for \ + [default: owner's associated token account]") + ) .arg( - Arg::with_name("address") - .validator(is_valid_pubkey) - .value_name("TOKEN_ADDRESS") - .takes_value(true) - .index(1) - .required(true) - .help("The address of the SPL Token mint, account, or multisig to query"), - ), - ) - .subcommand( - SubCommand::with_name(CommandName::Gc.into()) - .about("Cleanup unnecessary token accounts") - .arg(owner_keypair_arg()) + owner_address_arg() + ) .arg( - Arg::with_name("close_empty_associated_accounts") - .long("close-empty-associated-accounts") - .takes_value(false) - .help("close all empty associated token accounts (to get SOL back)") + Arg::with_name("maximum_pending_balance_credit_counter") + .long("maximum-pending-balance-credit-counter") + .value_name("MAXIMUM-CREDIT-COUNTER") + .takes_value(true) + .help( + "The maximum pending balance credit counter. \ + This parameter limits the number of confidential transfers that a token account \ + can receive to facilitate decryption of the encrypted balance. \ + Defaults to 65536 (2^16)" + ) ) + .arg(multisig_signer_arg()) + .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::SyncNative.into()) - .about("Sync a native SOL token account to its underlying lamports") + SubCommand::with_name(CommandName::EnableConfidentialCredits.into()) + .about("Enable confidential transfers for token account. To enable confidential transfers \ + for the first time, use `configure-confidential-transfer-account` instead.") .arg( - owner_address_arg() + Arg::with_name("token") + .long("token") + .validator(is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) .index(1) - .conflicts_with("address") - .help("Owner of the associated account for the native token. \ - To query a specific account, use the `--address` parameter instead. \ - Defaults to the client keypair."), + .required_unless("address") + .help("The token address with confidential transfers enabled"), ) .arg( Arg::with_name("address") - .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") - .takes_value(true) .long("address") - .conflicts_with("owner") - .help("Specify the specific token account address to sync"), - ), - ) - .subcommand( - SubCommand::with_name(CommandName::EnableRequiredTransferMemos.into()) - .about("Enable required transfer memos for token account") - .arg( - Arg::with_name("account") .validator(is_valid_pubkey) .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .index(1) - .required(true) - .help("The address of the token account to require transfer memos for") + .conflicts_with("token") + .help("The address of the token account to enable confidential transfers for \ + [default: owner's associated token account]") ) .arg( owner_address_arg() @@ -3533,34 +5192,27 @@ fn app<'a, 'b>( .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::DisableRequiredTransferMemos.into()) - .about("Disable required transfer memos for token account") + SubCommand::with_name(CommandName::DisableConfidentialCredits.into()) + .about("Disable confidential transfers for token account") .arg( - Arg::with_name("account") + Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("The address of the token account to stop requiring transfer memos for"), - ) - .arg( - owner_address_arg() + .required_unless("address") + .help("The token address with confidential transfers enabled"), ) - .arg(multisig_signer_arg()) - .nonce_args(true) - ) - .subcommand( - SubCommand::with_name(CommandName::EnableCpiGuard.into()) - .about("Enable CPI Guard for token account") .arg( - Arg::with_name("account") + Arg::with_name("address") + .long("address") .validator(is_valid_pubkey) .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .index(1) - .required(true) - .help("The address of the token account to enable CPI Guard for") + .conflicts_with("token") + .help("The address of the token account to disable confidential transfers for \ + [default: owner's associated token account]") ) .arg( owner_address_arg() @@ -3569,16 +5221,27 @@ fn app<'a, 'b>( .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::DisableCpiGuard.into()) - .about("Disable CPI Guard for token account") + SubCommand::with_name(CommandName::EnableNonConfidentialCredits.into()) + .about("Enable non-confidential transfers for token account.") .arg( - Arg::with_name("account") + Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("The address of the token account to disable CPI Guard for"), + .required_unless("address") + .help("The token address with confidential transfers enabled"), + ) + .arg( + Arg::with_name("address") + .long("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") + .takes_value(true) + .conflicts_with("token") + .help("The address of the token account to enable non-confidential transfers for \ + [default: owner's associated token account]") ) .arg( owner_address_arg() @@ -3587,147 +5250,137 @@ fn app<'a, 'b>( .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::UpdateDefaultAccountState.into()) - .about("Updates default account state for the mint. Requires the default account state extension.") + SubCommand::with_name(CommandName::DisableNonConfidentialCredits.into()) + .about("Disable non-confidential transfers for token account") .arg( Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) - .required(true) - .help("The address of the token mint to update default account state"), + .required_unless("address") + .help("The token address with confidential transfers enabled"), ) .arg( - Arg::with_name("state") - .value_name("STATE") + Arg::with_name("address") + .long("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .possible_values(&["initialized", "frozen"]) - .index(2) - .required(true) - .help("The new default account state."), + .conflicts_with("token") + .help("The address of the token account to disable non-confidential transfers for \ + [default: owner's associated token account]") ) .arg( - Arg::with_name("freeze_authority") - .long("freeze-authority") - .value_name("KEYPAIR") - .validator(is_valid_signer) - .takes_value(true) - .help( - "Specify the token's freeze authority. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair.", - ), + owner_address_arg() ) - .arg(owner_address_arg()) .arg(multisig_signer_arg()) .nonce_args(true) - .offline_args(), ) .subcommand( - SubCommand::with_name(CommandName::WithdrawWithheldTokens.into()) - .about("Withdraw withheld transfer fee tokens from mint and / or account(s)") + SubCommand::with_name(CommandName::DepositConfidentialTokens.into()) + .about("Deposit amounts for confidential transfers") .arg( - Arg::with_name("account") + Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) - .value_name("TOKEN_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) .index(1) .required(true) - .help("The address of the token account to receive withdrawn tokens"), + .help("The token address with confidential transfers enabled"), ) .arg( - Arg::with_name("source") - .validator(is_valid_pubkey) - .value_name("ACCOUNT_ADDRESS") + Arg::with_name("amount") + .validator(is_amount_or_all) + .value_name("TOKEN_AMOUNT") .takes_value(true) - .multiple(true) - .min_values(0u64) - .help("The token accounts to withdraw from") - ) - .arg( - Arg::with_name("include_mint") - .long("include-mint") - .takes_value(false) - .help("Also withdraw withheld tokens from the mint"), + .index(2) + .required(true) + .help("Amount to deposit; accepts keyword ALL"), ) .arg( - Arg::with_name("withdraw_withheld_authority") - .long("withdraw-withheld-authority") - .alias("owner") - .value_name("KEYPAIR") - .validator(is_valid_signer) + Arg::with_name("address") + .long("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .help( - "Specify the withdraw withheld authority keypair. \ - This may be a keypair file or the ASK keyword. \ - Defaults to the client keypair." - ), + .help("The address of the token account to configure confidential transfers for \ + [default: owner's associated token account]") + ) + .arg( + owner_address_arg() ) - .arg(owner_address_arg()) .arg(multisig_signer_arg()) + .arg(mint_decimals_arg()) + .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::SetTransferFee.into()) - .about("Set the transfer fee for a token with a configured transfer fee") + SubCommand::with_name(CommandName::WithdrawConfidentialTokens.into()) + .about("Withdraw amounts for confidential transfers") .arg( Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) + .index(1) .required(true) - .help("The interest-bearing token address"), + .help("The token address with confidential transfers enabled"), ) .arg( - Arg::with_name("transfer_fee_basis_points") - .value_name("FEE_IN_BASIS_POINTS") + Arg::with_name("amount") + .validator(is_amount_or_all) + .value_name("TOKEN_AMOUNT") .takes_value(true) + .index(2) .required(true) - .help("The new transfer fee in basis points"), + .help("Amount to deposit; accepts keyword ALL"), ) .arg( - Arg::with_name("maximum_fee") - .value_name("TOKEN_AMOUNT") - .validator(is_amount) + Arg::with_name("address") + .long("address") + .validator(is_valid_pubkey) + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .required(true) - .help("The new maximum transfer fee in UI amount"), + .help("The address of the token account to configure confidential transfers for \ + [default: owner's associated token account]") ) .arg( - Arg::with_name("transfer_fee_authority") - .long("transfer-fee-authority") - .validator(is_valid_signer) - .value_name("SIGNER") - .takes_value(true) - .help( - "Specify the rate authority keypair. \ - Defaults to the client keypair address." - ) + owner_address_arg() ) + .arg(multisig_signer_arg()) .arg(mint_decimals_arg()) - .offline_args_config(&SignOnlyNeedsMintDecimals{}) + .nonce_args(true) ) .subcommand( - SubCommand::with_name(CommandName::WithdrawExcessLamports.into()) - .about("Withdraw lamports from a Token Program owned account") + SubCommand::with_name(CommandName::ApplyPendingBalance.into()) + .about("Collect confidential tokens from pending to available balance") .arg( - Arg::with_name("from") + Arg::with_name("token") + .long("token") .validator(is_valid_pubkey) - .value_name("SOURCE_ACCOUNT_ADDRESS") + .value_name("TOKEN_MINT_ADDRESS") .takes_value(true) - .required(true) - .help("Specify the address of the account to recover lamports from"), + .index(1) + .required_unless("address") + .help("The token address with confidential transfers enabled"), ) .arg( - Arg::with_name("recipient") + Arg::with_name("address") + .long("address") .validator(is_valid_pubkey) - .value_name("REFUND_ACCOUNT_ADDRESS") + .value_name("TOKEN_ACCOUNT_ADDRESS") .takes_value(true) - .required(true) - .help("Specify the address of the account to send lamports to"), + .help("The address of the token account to configure confidential transfers for \ + [default: owner's associated token account]") + ) + .arg( + owner_address_arg() ) - .arg(owner_address_arg()) .arg(multisig_signer_arg()) + .nonce_args(true) ) } @@ -3770,7 +5423,7 @@ async fn process_command<'a>( sub_command: &CommandName, sub_matches: &ArgMatches<'_>, config: &Config<'a>, - mut wallet_manager: Option>, + mut wallet_manager: Option>, mut bulk_signers: Vec>, ) -> CommandResult { match (sub_command, sub_matches) { @@ -3816,6 +5469,8 @@ async fn process_command<'a>( "frozen" => AccountState::Frozen, _ => unreachable!(), }); + let transfer_hook_program_id = + pubkey_of_signer(arg_matches, "transfer_hook", &mut wallet_manager).unwrap(); let confidential_transfer_auto_approve = arg_matches .value_of("enable_confidential_transfers") @@ -3836,6 +5491,8 @@ async fn process_command<'a>( default_account_state, transfer_fee, confidential_transfer_auto_approve, + transfer_hook_program_id, + arg_matches.is_present("enable_metadata"), bulk_signers, ) .await @@ -3858,6 +5515,78 @@ async fn process_command<'a>( ) .await } + (CommandName::SetTransferHookProgram, arg_matches) => { + let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + let new_program_id = + pubkey_of_signer(arg_matches, "new_program_id", &mut wallet_manager).unwrap(); + let (authority_signer, authority_pubkey) = + config.signer_or_default(arg_matches, "authority", &mut wallet_manager); + let bulk_signers = vec![authority_signer]; + + command_set_transfer_hook_program( + config, + token_pubkey, + authority_pubkey, + new_program_id, + bulk_signers, + ) + .await + } + (CommandName::InitializeMetadata, arg_matches) => { + let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + let name = arg_matches.value_of("name").unwrap().to_string(); + let symbol = arg_matches.value_of("symbol").unwrap().to_string(); + let uri = arg_matches.value_of("uri").unwrap().to_string(); + let (mint_authority_signer, mint_authority) = + config.signer_or_default(arg_matches, "mint_authority", &mut wallet_manager); + let bulk_signers = vec![mint_authority_signer]; + let update_authority = + config.pubkey_or_default(arg_matches, "update_authority", &mut wallet_manager)?; + + command_initialize_metadata( + config, + token_pubkey, + update_authority, + mint_authority, + name, + symbol, + uri, + bulk_signers, + ) + .await + } + (CommandName::UpdateMetadata, arg_matches) => { + let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + let (authority_signer, authority) = + config.signer_or_default(arg_matches, "authority", &mut wallet_manager); + let field = arg_matches.value_of("field").unwrap(); + let field = match field.to_lowercase().as_str() { + "name" => Field::Name, + "symbol" => Field::Symbol, + "uri" => Field::Uri, + _ => Field::Key(field.to_string()), + }; + let value = arg_matches.value_of("value").map(|v| v.to_string()); + let transfer_lamports = value_of::(arg_matches, TRANSFER_LAMPORTS_ARG.name); + let bulk_signers = vec![authority_signer]; + + command_update_metadata( + config, + token_pubkey, + authority, + field, + value, + transfer_lamports, + bulk_signers, + ) + .await + } (CommandName::CreateAccount, arg_matches) => { let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) .unwrap() @@ -3906,22 +5635,7 @@ async fn process_command<'a>( .unwrap() .unwrap(); let authority_type = arg_matches.value_of("authority_type").unwrap(); - let authority_type = match authority_type { - "mint" => AuthorityType::MintTokens, - "freeze" => AuthorityType::FreezeAccount, - "owner" => AuthorityType::AccountOwner, - "close" => AuthorityType::CloseAccount, - "close-mint" => AuthorityType::CloseMint, - "transfer-fee-config" => AuthorityType::TransferFeeConfig, - "withheld-withdraw" => AuthorityType::WithheldWithdraw, - "interest-rate" => AuthorityType::InterestRate, - "permanent-delegate" => AuthorityType::PermanentDelegate, - "confidential-transfer-mint" => AuthorityType::ConfidentialTransferMint, - "transfer-hook-program-id" => AuthorityType::TransferHookProgramId, - "confidential-transfer-fee" => AuthorityType::ConfidentialTransferFeeConfig, - "metadata-pointer" => AuthorityType::MetadataPointer, - _ => unreachable!(), - }; + let authority_type = CliAuthorityType::from_str(authority_type)?; let (authority_signer, authority) = config.signer_or_default(arg_matches, "authority", &mut wallet_manager); @@ -3958,6 +5672,29 @@ async fn process_command<'a>( let (owner_signer, owner) = config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + + let confidential_transfer_args = if arg_matches.is_present("confidential") { + // Deriving ElGamal and AES key from signer. Custom ElGamal and AES keys will be + // supported in the future once upgrading to clap-v3. + // + // NOTE:: Seed bytes are hardcoded to be empty bytes for now. They will be updated + // once custom ElGamal and AES keys are supported. + let sender_elgamal_keypair = + ElGamalKeypair::new_from_signer(&*owner_signer, b"").unwrap(); + let sender_aes_key = AeKey::new_from_signer(&*owner_signer, b"").unwrap(); + + // Sign-only mode is not yet supported for confidential transfers, so set + // recipient and auditor ElGamal public to `None` by default. + Some(ConfidentialTransferArgs { + sender_elgamal_keypair, + sender_aes_key, + recipient_elgamal_pubkey: None, + auditor_elgamal_pubkey: None, + }) + } else { + None + }; + if config.multisigner_pubkeys.is_empty() { push_signer_with_dedup(owner_signer, &mut bulk_signers); } @@ -3968,9 +5705,19 @@ async fn process_command<'a>( || arg_matches.is_present("allow_unfunded_recipient"); let recipient_is_ata_owner = arg_matches.is_present("recipient_is_ata_owner"); + let no_recipient_is_ata_owner = + arg_matches.is_present("no_recipient_is_ata_owner") || !recipient_is_ata_owner; + if recipient_is_ata_owner { + println_display(config, "recipient-is-ata-owner is now the default behavior. The option has been deprecated and will be removed in a future release.".to_string()); + } let use_unchecked_instruction = arg_matches.is_present("use_unchecked_instruction"); let expected_fee = value_of::(arg_matches, "expected_fee"); let memo = value_t!(arg_matches, "memo", String).ok(); + let transfer_hook_accounts = arg_matches.values_of("transfer_hook_account").map(|v| { + v.into_iter() + .map(|s| parse_transfer_hook_account(s).unwrap()) + .collect::>() + }); command_transfer( config, @@ -3982,13 +5729,15 @@ async fn process_command<'a>( allow_unfunded_recipient, fund_recipient, mint_decimals, - recipient_is_ata_owner, + no_recipient_is_ata_owner, use_unchecked_instruction, expected_fee, memo, bulk_signers, arg_matches.is_present("no_wait"), arg_matches.is_present("allow_non_system_account_recipient"), + transfer_hook_accounts, + confidential_transfer_args.as_ref(), ) .await } @@ -4387,6 +6136,28 @@ async fn process_command<'a>( ) .await } + (CommandName::UpdateMetadataAddress, arg_matches) => { + // Since account is required argument it will always be present + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + + let (authority_signer, authority) = + config.signer_or_default(arg_matches, "authority", &mut wallet_manager); + if config.multisigner_pubkeys.is_empty() { + push_signer_with_dedup(authority_signer, &mut bulk_signers); + } + let metadata_address = value_t!(arg_matches, "metadata_address", Pubkey).ok(); + + command_update_metadata_pointer_address( + config, + token, + authority, + metadata_address, + bulk_signers, + ) + .await + } (CommandName::WithdrawWithheldTokens, arg_matches) => { let (authority_signer, authority) = config.signer_or_default( arg_matches, @@ -4429,30 +6200,225 @@ async fn process_command<'a>( let mint_decimals = value_of::(arg_matches, MINT_DECIMALS_ARG.name); let bulk_signers = vec![transfer_fee_authority_signer]; - command_set_transfer_fee( + command_set_transfer_fee( + config, + token_pubkey, + transfer_fee_authority_pubkey, + transfer_fee_basis_points, + maximum_fee, + mint_decimals, + bulk_signers, + ) + .await + } + (CommandName::WithdrawExcessLamports, arg_matches) => { + let (signer, authority) = + config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + if config.multisigner_pubkeys.is_empty() { + push_signer_with_dedup(signer, &mut bulk_signers); + } + + let source = config.pubkey_or_default(arg_matches, "from", &mut wallet_manager)?; + let destination = + config.pubkey_or_default(arg_matches, "recipient", &mut wallet_manager)?; + + command_withdraw_excess_lamports(config, source, destination, authority, bulk_signers) + .await + } + (CommandName::UpdateConfidentialTransferSettings, arg_matches) => { + let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + + let auto_approve = arg_matches.value_of("approve_policy").map(|b| b == "auto"); + + let auditor_encryption_pubkey = if arg_matches.is_present("auditor_pubkey") { + Some(elgamal_pubkey_or_none(arg_matches, "auditor_pubkey")?) + } else { + None + }; + + let (authority_signer, authority_pubkey) = config.signer_or_default( + arg_matches, + "confidential_transfer_authority", + &mut wallet_manager, + ); + let bulk_signers = vec![authority_signer]; + + command_update_confidential_transfer_settings( + config, + token_pubkey, + authority_pubkey, + auto_approve, + auditor_encryption_pubkey, + bulk_signers, + ) + .await + } + (CommandName::ConfigureConfidentialTransferAccount, arg_matches) => { + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager).unwrap(); + + let (owner_signer, owner) = + config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + + let account = pubkey_of_signer(arg_matches, "address", &mut wallet_manager).unwrap(); + + // Deriving ElGamal and AES key from signer. Custom ElGamal and AES keys will be + // supported in the future once upgrading to clap-v3. + // + // NOTE:: Seed bytes are hardcoded to be empty bytes for now. They will be updated + // once custom ElGamal and AES keys are supported. + let elgamal_keypair = ElGamalKeypair::new_from_signer(&*owner_signer, b"").unwrap(); + let aes_key = AeKey::new_from_signer(&*owner_signer, b"").unwrap(); + + if config.multisigner_pubkeys.is_empty() { + push_signer_with_dedup(owner_signer, &mut bulk_signers); + } + + let maximum_credit_counter = + if arg_matches.is_present("maximum_pending_balance_credit_counter") { + let maximum_credit_counter = value_t_or_exit!( + arg_matches.value_of("maximum_pending_balance_credit_counter"), + u64 + ); + Some(maximum_credit_counter) + } else { + None + }; + + command_configure_confidential_transfer_account( + config, + token, + owner, + account, + maximum_credit_counter, + &elgamal_keypair, + &aes_key, + bulk_signers, + ) + .await + } + (c @ CommandName::EnableConfidentialCredits, arg_matches) + | (c @ CommandName::DisableConfidentialCredits, arg_matches) + | (c @ CommandName::EnableNonConfidentialCredits, arg_matches) + | (c @ CommandName::DisableNonConfidentialCredits, arg_matches) => { + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager).unwrap(); + + let (owner_signer, owner) = + config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + + let account = pubkey_of_signer(arg_matches, "address", &mut wallet_manager).unwrap(); + + if config.multisigner_pubkeys.is_empty() { + push_signer_with_dedup(owner_signer, &mut bulk_signers); + } + + let (allow_confidential_credits, allow_non_confidential_credits) = match c { + CommandName::EnableConfidentialCredits => (Some(true), None), + CommandName::DisableConfidentialCredits => (Some(false), None), + CommandName::EnableNonConfidentialCredits => (None, Some(true)), + CommandName::DisableNonConfidentialCredits => (None, Some(false)), + _ => (None, None), + }; + + command_enable_disable_confidential_transfers( + config, + token, + owner, + account, + bulk_signers, + allow_confidential_credits, + allow_non_confidential_credits, + ) + .await + } + (c @ CommandName::DepositConfidentialTokens, arg_matches) + | (c @ CommandName::WithdrawConfidentialTokens, arg_matches) => { + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + let amount = match arg_matches.value_of("amount").unwrap() { + "ALL" => None, + amount => Some(amount.parse::().unwrap()), + }; + let account = pubkey_of_signer(arg_matches, "address", &mut wallet_manager).unwrap(); + + let (owner_signer, owner) = + config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + + let mint_decimals = value_of::(arg_matches, MINT_DECIMALS_ARG.name); + + let (instruction_type, elgamal_keypair, aes_key) = match c { + CommandName::DepositConfidentialTokens => { + (ConfidentialInstructionType::Deposit, None, None) + } + CommandName::WithdrawConfidentialTokens => { + // Deriving ElGamal and AES key from signer. Custom ElGamal and AES keys will be + // supported in the future once upgrading to clap-v3. + // + // NOTE:: Seed bytes are hardcoded to be empty bytes for now. They will be updated + // once custom ElGamal and AES keys are supported. + let elgamal_keypair = + ElGamalKeypair::new_from_signer(&*owner_signer, b"").unwrap(); + let aes_key = AeKey::new_from_signer(&*owner_signer, b"").unwrap(); + + ( + ConfidentialInstructionType::Withdraw, + Some(elgamal_keypair), + Some(aes_key), + ) + } + _ => panic!("Instruction not supported"), + }; + + if config.multisigner_pubkeys.is_empty() { + push_signer_with_dedup(owner_signer, &mut bulk_signers); + } + + command_deposit_withdraw_confidential_tokens( config, - token_pubkey, - transfer_fee_authority_pubkey, - transfer_fee_basis_points, - maximum_fee, - mint_decimals, + token, + owner, + account, bulk_signers, + amount, + mint_decimals, + instruction_type, + elgamal_keypair.as_ref(), + aes_key.as_ref(), ) .await } - (CommandName::WithdrawExcessLamports, arg_matches) => { - let (signer, authority) = + (CommandName::ApplyPendingBalance, arg_matches) => { + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager).unwrap(); + + let (owner_signer, owner) = config.signer_or_default(arg_matches, "owner", &mut wallet_manager); + + let account = pubkey_of_signer(arg_matches, "address", &mut wallet_manager).unwrap(); + + // Deriving ElGamal and AES key from signer. Custom ElGamal and AES keys will be + // supported in the future once upgrading to clap-v3. + // + // NOTE:: Seed bytes are hardcoded to be empty bytes for now. They will be updated + // once custom ElGamal and AES keys are supported. + let elgamal_keypair = ElGamalKeypair::new_from_signer(&*owner_signer, b"").unwrap(); + let aes_key = AeKey::new_from_signer(&*owner_signer, b"").unwrap(); + if config.multisigner_pubkeys.is_empty() { - push_signer_with_dedup(signer, &mut bulk_signers); + push_signer_with_dedup(owner_signer, &mut bulk_signers); } - let source = config.pubkey_or_default(arg_matches, "from", &mut wallet_manager)?; - let destination = - config.pubkey_or_default(arg_matches, "recipient", &mut wallet_manager)?; - - command_withdraw_excess_lamports(config, source, destination, authority, bulk_signers) - .await + command_apply_pending_balance( + config, + token, + owner, + account, + bulk_signers, + &elgamal_keypair, + &aes_key, + ) + .await } } } @@ -4505,6 +6471,10 @@ async fn finish_tx<'a>( signature: signature.to_string(), })) } + RpcClientResponse::Simulation(_) => { + // Implement this once the CLI supports dry-running / simulation + unreachable!() + } } } @@ -4514,7 +6484,7 @@ mod tests { super::*, serial_test::serial, solana_sdk::{ - bpf_loader_upgradeable, + bpf_loader_upgradeable, feature_set, hash::Hash, program_pack::Pack, signature::{write_keypair_file, Keypair, Signer}, @@ -4559,6 +6529,9 @@ mod tests { upgrade_authority: Pubkey::new_unique(), }, ]); + // TODO Remove this once the Range Proof cost goes under 200k compute units + test_validator_genesis + .deactivate_features(&[feature_set::native_programs_consume_cu::id()]); test_validator_genesis.start_async().await } @@ -4688,6 +6661,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -4720,6 +6695,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -6106,6 +8083,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -6158,6 +8137,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -6234,6 +8215,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -6489,7 +8472,7 @@ mod tests { let result = command_authorize( &config, account, - AuthorityType::AccountOwner, + CliAuthorityType::Owner, payer.pubkey(), Some(new_owner), true, @@ -6521,7 +8504,7 @@ mod tests { let result = command_authorize( &config, aux_pubkey, - AuthorityType::AccountOwner, + CliAuthorityType::Owner, payer.pubkey(), Some(new_owner), true, @@ -6554,7 +8537,7 @@ mod tests { let result = command_authorize( &config, Pubkey::from_str(&accounts[0].pubkey).unwrap(), - AuthorityType::AccountOwner, + CliAuthorityType::Owner, payer.pubkey(), Some(new_owner), true, @@ -6591,6 +8574,8 @@ mod tests { None, None, None, + None, + false, bulk_signers, ) .await @@ -6649,6 +8634,8 @@ mod tests { Some(AccountState::Frozen), None, None, + None, + false, bulk_signers, ) .await @@ -6720,6 +8707,8 @@ mod tests { None, Some((transfer_fee_basis_points, maximum_fee)), None, + None, + false, bulk_signers, ) .await @@ -6962,58 +8951,288 @@ mod tests { #[tokio::test] #[serial] async fn confidential_transfer() { - use spl_token_2022::solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey; + use spl_token_2022::solana_zk_token_sdk::encryption::elgamal::ElGamalKeypair; let (test_validator, payer) = new_validator_for_test().await; let config = test_config_with_default_signer(&test_validator, &payer, &spl_token_2022::id()); - let token_keypair = Keypair::new(); - let token_pubkey = token_keypair.pubkey(); - let bulk_signers: Vec> = - vec![Arc::new(clone_keypair(&payer)), Arc::new(token_keypair)]; - let confidential_transfer_mint_authority = payer.pubkey(); - let auto_approve = true; + // create token with confidential transfers enabled + let token_keypair = Keypair::new(); + let token_pubkey = token_keypair.pubkey(); + let bulk_signers: Vec> = + vec![Arc::new(clone_keypair(&payer)), Arc::new(token_keypair)]; + let confidential_transfer_mint_authority = payer.pubkey(); + let auto_approve = false; + + command_create_token( + &config, + TEST_DECIMALS, + token_pubkey, + payer.pubkey(), + false, + false, + false, + false, + None, + None, + None, + None, + None, + Some(auto_approve), + None, + false, + bulk_signers.clone(), + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_pubkey).await.unwrap(); + let test_mint = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = test_mint + .get_extension::() + .unwrap(); + + assert_eq!( + Option::::from(extension.authority), + Some(confidential_transfer_mint_authority), + ); + assert_eq!( + bool::from(extension.auto_approve_new_accounts), + auto_approve, + ); + assert_eq!( + Option::::from(extension.auditor_elgamal_pubkey), + None, + ); + + // update confidential transfer mint settings + let auditor_keypair = ElGamalKeypair::new_rand(); + let auditor_pubkey: ElGamalPubkey = (*auditor_keypair.pubkey()).into(); + let new_auto_approve = true; + + command_update_confidential_transfer_settings( + &config, + token_pubkey, + confidential_transfer_mint_authority, + Some(new_auto_approve), + Some(ElGamalPubkeyOrNone::ElGamalPubkey(auditor_pubkey)), // auditor pubkey + bulk_signers.clone(), + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_pubkey).await.unwrap(); + let test_mint = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = test_mint + .get_extension::() + .unwrap(); + + assert_eq!( + bool::from(extension.auto_approve_new_accounts), + new_auto_approve, + ); + assert_eq!( + Option::::from(extension.auditor_elgamal_pubkey), + Some(auditor_pubkey), + ); + + // create a confidential transfer account + let token_account = + create_associated_account(&config, &payer, &token_pubkey, &payer.pubkey()).await; + + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::ConfigureConfidentialTransferAccount.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_account).await.unwrap(); + let account_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = account_state + .get_extension::() + .unwrap(); + assert!(bool::from(extension.approved)); + assert!(bool::from(extension.allow_confidential_credits)); + assert!(bool::from(extension.allow_non_confidential_credits)); + + // disable and enable confidential transfers for an account + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::DisableConfidentialCredits.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_account).await.unwrap(); + let account_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = account_state + .get_extension::() + .unwrap(); + assert!(!bool::from(extension.allow_confidential_credits)); + + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::EnableConfidentialCredits.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_account).await.unwrap(); + let account_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = account_state + .get_extension::() + .unwrap(); + assert!(bool::from(extension.allow_confidential_credits)); + + // disable and eanble non-confidential transfers for an account + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::DisableNonConfidentialCredits.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_account).await.unwrap(); + let account_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = account_state + .get_extension::() + .unwrap(); + assert!(!bool::from(extension.allow_non_confidential_credits)); + + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::EnableNonConfidentialCredits.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_account).await.unwrap(); + let account_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = account_state + .get_extension::() + .unwrap(); + assert!(bool::from(extension.allow_non_confidential_credits)); + + // deposit confidential tokens + let deposit_amount = 100.0; + mint_tokens(&config, &payer, token_pubkey, deposit_amount, token_account).await; + + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::DepositConfidentialTokens.into(), + &token_pubkey.to_string(), + &deposit_amount.to_string(), + ], + ) + .await + .unwrap(); + + // apply pending balance + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::ApplyPendingBalance.into(), + &token_pubkey.to_string(), + ], + ) + .await + .unwrap(); + + // confidential transfer + let destination_account = create_auxiliary_account(&config, &payer, token_pubkey).await; + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::ConfigureConfidentialTransferAccount.into(), + "--address", + &destination_account.to_string(), + ], + ) + .await + .unwrap(); // configure destination account for confidential transfers first + + let transfer_amount = 100.0; + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::Transfer.into(), + &token_pubkey.to_string(), + &transfer_amount.to_string(), + &destination_account.to_string(), + "--confidential", + ], + ) + .await + .unwrap(); + + // withdraw confidential tokens + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::ApplyPendingBalance.into(), + "--address", + &destination_account.to_string(), + ], + ) + .await + .unwrap(); // apply pending balance first + + let withdraw_amount = 100.0; - command_create_token( + process_test_command( &config, - TEST_DECIMALS, - token_pubkey, - payer.pubkey(), - false, - false, - false, - false, - None, - None, - None, - None, - None, - Some(auto_approve), - bulk_signers, + &payer, + &[ + "spl-token", + CommandName::WithdrawConfidentialTokens.into(), + &token_pubkey.to_string(), + &withdraw_amount.to_string(), + "--address", + &destination_account.to_string(), + ], ) .await .unwrap(); - let account = config.rpc_client.get_account(&token_pubkey).await.unwrap(); - let test_mint = StateWithExtensionsOwned::::unpack(account.data).unwrap(); - let extension = test_mint - .get_extension::() - .unwrap(); - - assert_eq!( - Option::::from(extension.authority), - Some(confidential_transfer_mint_authority), - ); - assert_eq!( - bool::from(extension.auto_approve_new_accounts), - auto_approve, - ); - assert_eq!( - Option::::from(extension.auditor_elgamal_pubkey), - None, - ); - + // disable confidential transfers for mint process_test_command( &config, &payer, @@ -7489,5 +9708,344 @@ mod tests { extension.metadata_address, Some(metadata_address).try_into().unwrap() ); + + let new_metadata_address = Pubkey::new_unique(); + + let _new_result = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadataAddress.into(), + &mint.to_string(), + &new_metadata_address.to_string(), + ], + ) + .await; + + let new_account = config.rpc_client.get_account(&mint).await.unwrap(); + let new_mint_state = StateWithExtensionsOwned::::unpack(new_account.data).unwrap(); + + let new_extension = new_mint_state.get_extension::().unwrap(); + + assert_eq!( + new_extension.metadata_address, + Some(new_metadata_address).try_into().unwrap() + ); + + let _result_with_disable = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadataAddress.into(), + &mint.to_string(), + "--disable", + ], + ) + .await; + + let new_account_disbale = config.rpc_client.get_account(&mint).await.unwrap(); + let new_mint_state_disable = + StateWithExtensionsOwned::::unpack(new_account_disbale.data).unwrap(); + + let new_extension_disable = new_mint_state_disable + .get_extension::() + .unwrap(); + + assert_eq!( + new_extension_disable.metadata_address, + None.try_into().unwrap() + ); + } + + #[tokio::test] + #[serial] + async fn transfer_hook() { + let (test_validator, payer) = new_validator_for_test().await; + let program_id = spl_token_2022::id(); + let mut config = test_config_with_default_signer(&test_validator, &payer, &program_id); + let transfer_hook_program_id = Pubkey::new_unique(); + + let result = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::CreateToken.into(), + "--program-id", + &program_id.to_string(), + "--transfer-hook", + &transfer_hook_program_id.to_string(), + ], + ) + .await; + + let value: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); + let mint = Pubkey::from_str(value["commandOutput"]["address"].as_str().unwrap()).unwrap(); + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = mint_state.get_extension::().unwrap(); + + assert_eq!( + extension.program_id, + Some(transfer_hook_program_id).try_into().unwrap() + ); + + let new_transfer_hook_program_id = Pubkey::new_unique(); + + let _new_result = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::SetTransferHookProgram.into(), + &mint.to_string(), + &new_transfer_hook_program_id.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = mint_state.get_extension::().unwrap(); + + assert_eq!( + extension.program_id, + Some(new_transfer_hook_program_id).try_into().unwrap() + ); + + // Make sure that parsing transfer hook accounts works + let real_program_client = config.program_client; + let blockhash = Hash::default(); + let program_client: Arc> = Arc::new( + ProgramOfflineClient::new(blockhash, ProgramRpcClientSendTransaction), + ); + config.program_client = program_client; + let _result = exec_test_cmd( + &config, + &[ + "spl-token", + CommandName::Transfer.into(), + &mint.to_string(), + "10", + &Pubkey::new_unique().to_string(), + "--blockhash", + &blockhash.to_string(), + "--nonce", + &Pubkey::new_unique().to_string(), + "--nonce-authority", + &Pubkey::new_unique().to_string(), + "--sign-only", + "--mint-decimals", + &format!("{}", TEST_DECIMALS), + "--from", + &Pubkey::new_unique().to_string(), + "--owner", + &Pubkey::new_unique().to_string(), + "--transfer-hook-account", + &format!("{}:readonly", Pubkey::new_unique()), + "--transfer-hook-account", + &format!("{}:writable", Pubkey::new_unique()), + "--transfer-hook-account", + &format!("{}:readonly-signer", Pubkey::new_unique()), + "--transfer-hook-account", + &format!("{}:writable-signer", Pubkey::new_unique()), + ], + ) + .await + .unwrap(); + + config.program_client = real_program_client; + let _result_with_disable = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::SetTransferHookProgram.into(), + &mint.to_string(), + "--disable", + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = mint_state.get_extension::().unwrap(); + + assert_eq!(extension.program_id, None.try_into().unwrap()); + } + + #[tokio::test] + #[serial] + async fn metadata() { + let (test_validator, payer) = new_validator_for_test().await; + let program_id = spl_token_2022::id(); + let config = test_config_with_default_signer(&test_validator, &payer, &program_id); + let name = "this"; + let symbol = "is"; + let uri = "METADATA!"; + + let result = process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::CreateToken.into(), + "--program-id", + &program_id.to_string(), + "--enable-metadata", + ], + ) + .await; + + let value: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); + let mint = Pubkey::from_str(value["commandOutput"]["address"].as_str().unwrap()).unwrap(); + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + + let extension = mint_state.get_extension::().unwrap(); + assert_eq!(extension.metadata_address, Some(mint).try_into().unwrap()); + + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::InitializeMetadata.into(), + &mint.to_string(), + name, + symbol, + uri, + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let fetched_metadata = mint_state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(fetched_metadata.name, name); + assert_eq!(fetched_metadata.symbol, symbol); + assert_eq!(fetched_metadata.uri, uri); + assert_eq!(fetched_metadata.mint, mint); + assert_eq!( + fetched_metadata.update_authority, + Some(payer.pubkey()).try_into().unwrap() + ); + assert_eq!(fetched_metadata.additional_metadata, []); + + // update canonical field + let new_value = "THIS!"; + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadata.into(), + &mint.to_string(), + "NAME", + new_value, + ], + ) + .await + .unwrap(); + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let fetched_metadata = mint_state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(fetched_metadata.name, new_value); + + // add new field + let field = "My field!"; + let value = "Try and stop me"; + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadata.into(), + &mint.to_string(), + field, + value, + ], + ) + .await + .unwrap(); + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let fetched_metadata = mint_state + .get_variable_len_extension::() + .unwrap(); + assert_eq!( + fetched_metadata.additional_metadata, + [(field.to_string(), value.to_string())] + ); + + // remove it + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadata.into(), + &mint.to_string(), + field, + "--remove", + ], + ) + .await + .unwrap(); + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let fetched_metadata = mint_state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(fetched_metadata.additional_metadata, []); + + // fail to remove name + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::UpdateMetadata.into(), + &mint.to_string(), + "name", + "--remove", + ], + ) + .await + .unwrap_err(); + + // update authority + process_test_command( + &config, + &payer, + &[ + "spl-token", + CommandName::Authorize.into(), + &mint.to_string(), + "metadata", + &mint.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&mint).await.unwrap(); + let mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let fetched_metadata = mint_state + .get_variable_len_extension::() + .unwrap(); + assert_eq!( + fetched_metadata.update_authority, + Some(mint).try_into().unwrap() + ); } } diff --git a/token/cli/src/output.rs b/token/cli/src/output.rs index 6bb0799992d..2d0d0d9d52b 100644 --- a/token/cli/src/output.rs +++ b/token/cli/src/output.rs @@ -4,8 +4,11 @@ use serde::{Deserialize, Serialize, Serializer}; use solana_account_decoder::{ parse_token::{UiAccountState, UiMint, UiMultisig, UiTokenAccount, UiTokenAmount}, parse_token_extension::{ - UiCpiGuard, UiDefaultAccountState, UiExtension, UiInterestBearingConfig, UiMemoTransfer, - UiMintCloseAuthority, UiPermanentDelegate, UiTransferFeeAmount, UiTransferFeeConfig, + UiConfidentialTransferAccount, UiConfidentialTransferFeeAmount, + UiConfidentialTransferFeeConfig, UiConfidentialTransferMint, UiCpiGuard, + UiDefaultAccountState, UiExtension, UiInterestBearingConfig, UiMemoTransfer, + UiMetadataPointer, UiMintCloseAuthority, UiPermanentDelegate, UiTokenMetadata, + UiTransferFeeAmount, UiTransferFeeConfig, UiTransferHook, UiTransferHookAccount, }, }; use solana_cli_output::{display::writeln_name_value, OutputFormat, QuietDisplay, VerboseDisplay}; @@ -643,7 +646,9 @@ fn display_ui_extension( "Not required" }, ), - UiExtension::NonTransferable => writeln!(f, " {}", style("Non-transferable").bold()), + UiExtension::NonTransferable | UiExtension::NonTransferableAccount => { + writeln!(f, " {}", style("Non-transferable").bold()) + } UiExtension::InterestBearingConfig(UiInterestBearingConfig { rate_authority, pre_update_average_rate, @@ -681,21 +686,228 @@ fn display_ui_extension( Ok(()) } } + UiExtension::ConfidentialTransferAccount(UiConfidentialTransferAccount { + approved, + elgamal_pubkey, + pending_balance_lo, + pending_balance_hi, + available_balance, + decryptable_available_balance, + allow_confidential_credits, + allow_non_confidential_credits, + pending_balance_credit_counter, + maximum_pending_balance_credit_counter, + expected_pending_balance_credit_counter, + actual_pending_balance_credit_counter, + }) => { + writeln!(f, " {}", style("Confidential transfer:").bold())?; + writeln_name_value(f, " Approved:", &format!("{approved}"))?; + writeln_name_value(f, " Encryption key:", elgamal_pubkey)?; + writeln_name_value(f, " Pending Balance Low:", pending_balance_lo)?; + writeln_name_value(f, " Pending Balance High:", pending_balance_hi)?; + writeln_name_value(f, " Available Balance:", available_balance)?; + writeln_name_value( + f, + " Decryptable Available Balance:", + decryptable_available_balance, + )?; + writeln_name_value( + f, + " Confidential Credits:", + if *allow_confidential_credits { + "Enabled" + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Non-Confidential Credits:", + if *allow_non_confidential_credits { + "Enabled" + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Pending Balance Credit Counter:", + &format!("{pending_balance_credit_counter}"), + )?; + writeln_name_value( + f, + " Maximum Pending Balance Credit Counter:", + &format!("{maximum_pending_balance_credit_counter}"), + )?; + writeln_name_value( + f, + " Expected Pending Balance Credit Counter:", + &format!("{expected_pending_balance_credit_counter}"), + )?; + writeln_name_value( + f, + " Actual Pending Balance Credit Counter:", + &format!("{actual_pending_balance_credit_counter}"), + ) + } + UiExtension::ConfidentialTransferMint(UiConfidentialTransferMint { + authority, + auto_approve_new_accounts, + auditor_elgamal_pubkey, + }) => { + writeln!(f, " {}", style("Confidential transfer:").bold())?; + writeln!( + f, + " {}: {}", + style("Authority").bold(), + if let Some(authority) = authority.as_ref() { + authority + } else { + "authority disabled" + } + )?; + writeln!( + f, + " {}: {}", + style("Account approve policy").bold(), + if *auto_approve_new_accounts { + "auto" + } else { + "manual" + }, + )?; + writeln!( + f, + " {}: {}", + style("Audit key").bold(), + if let Some(auditor_pubkey) = auditor_elgamal_pubkey.as_ref() { + auditor_pubkey + } else { + "audits are disabled" + } + ) + } + UiExtension::ConfidentialTransferFeeConfig(UiConfidentialTransferFeeConfig { + authority, + withdraw_withheld_authority_elgamal_pubkey, + harvest_to_mint_enabled, + withheld_amount, + }) => { + writeln!(f, " {}", style("Confidential transfer fee:").bold())?; + writeln_name_value( + f, + " Authority:", + if let Some(pubkey) = authority { + pubkey + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Withdraw Withheld Encryption key:", + if let Some(pubkey) = withdraw_withheld_authority_elgamal_pubkey { + pubkey + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Harvest to mint:", + if *harvest_to_mint_enabled { + "Enabled" + } else { + "Disabled" + }, + )?; + writeln_name_value(f, " Withheld Amount:", withheld_amount) + } + UiExtension::ConfidentialTransferFeeAmount(UiConfidentialTransferFeeAmount { + withheld_amount, + }) => writeln_name_value(f, " Confidential Transfer Fee Amount:", withheld_amount), + UiExtension::TransferHook(UiTransferHook { + authority, + program_id, + }) => { + writeln!(f, " {}", style("Transfer Hook:").bold())?; + writeln_name_value( + f, + " Authority:", + if let Some(pubkey) = authority { + pubkey + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Program Id:", + if let Some(pubkey) = program_id { + pubkey + } else { + "Disabled" + }, + ) + } + // don't display the "transferring" flag, since it's just for internal use + UiExtension::TransferHookAccount(UiTransferHookAccount { .. }) => Ok(()), + UiExtension::MetadataPointer(UiMetadataPointer { + authority, + metadata_address, + }) => { + writeln!(f, " {}", style("Metadata Pointer:").bold())?; + writeln_name_value( + f, + " Authority:", + if let Some(pubkey) = authority { + pubkey + } else { + "Disabled" + }, + )?; + writeln_name_value( + f, + " Metadata address:", + if let Some(pubkey) = metadata_address { + pubkey + } else { + "Disabled" + }, + ) + } + UiExtension::TokenMetadata(UiTokenMetadata { + update_authority, + mint, + name, + symbol, + uri, + additional_metadata, + }) => { + writeln!(f, " {}", style("Metadata:").bold())?; + writeln_name_value( + f, + " Update Authority:", + if let Some(pubkey) = update_authority { + pubkey + } else { + "Disabled" + }, + )?; + writeln_name_value(f, " Mint:", mint)?; + writeln_name_value(f, " Name:", name)?; + writeln_name_value(f, " Symbol:", symbol)?; + writeln_name_value(f, " URI:", uri)?; + for (key, value) in additional_metadata { + writeln_name_value(f, &format!(" {key}:"), value)?; + } + Ok(()) + } // ExtensionType::Uninitialized is a hack to ensure a mint/account is never the same length as a multisig UiExtension::Uninitialized => Ok(()), - UiExtension::ConfidentialTransferMint(_) => writeln_name_value( - f, - " Unparseable extension:", - "ConfidentialTransferMint is not presently supported", - ), - UiExtension::ConfidentialTransferAccount(_) => writeln_name_value( - f, - " Unparseable extension:", - "ConfidentialTransferAccount is not presently supported", - ), - _ => writeln_name_value( + UiExtension::UnparseableExtension => writeln_name_value( f, - " Unparseable extension:", + " Unparseable extension:", "Consider upgrading to a newer version of spl-token", ), } diff --git a/token/client/Cargo.toml b/token/client/Cargo.toml index 297678d0531..3d8975a582e 100644 --- a/token/client/Cargo.toml +++ b/token/client/Cargo.toml @@ -5,25 +5,29 @@ edition = "2021" license = "Apache-2.0" name = "spl-token-client" repository = "https://github.com/solana-labs/solana-program-library" -version = "0.5.0" +version = "0.8.0" [dependencies] async-trait = "0.1" +curve25519-dalek = "3.2.1" +futures = "0.3.29" futures-util = "0.3" -solana-cli-output = { version = "=1.16.1", optional = true } -solana-client = "=1.16.1" -solana-program-test = "=1.16.1" -solana-sdk = "=1.16.1" +solana-banks-interface = "1.17.2" +solana-cli-output = { version = "1.17.2", optional = true } +solana-program-test = "1.17.2" +solana-rpc-client = "1.17.2" +solana-rpc-client-api = "1.17.2" +solana-sdk = "1.17.2" # We never want the entrypoint for ATA, but we want the entrypoint for token when # testing token spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program", features = ["no-entrypoint"] } spl-memo = { version = "4.0.0", path = "../../memo/program", features = ["no-entrypoint"] } spl-token = { version = "4.0", path="../program", features = [ "no-entrypoint" ] } -spl-token-2022 = { version = "0.7", path="../program-2022" } -spl-transfer-hook-interface = { version = "0.1", path="../transfer-hook-interface" } +spl-token-2022 = { version = "0.9", path="../program-2022" } +spl-token-metadata-interface = { version = "0.2", path="../../token-metadata/interface" } +spl-transfer-hook-interface = { version = "0.3", path="../transfer-hook/interface" } thiserror = "1.0" [features] default = ["display"] display = ["dep:solana-cli-output"] -proof-program = ["spl-token-2022/proof-program"] diff --git a/token/client/src/client.rs b/token/client/src/client.rs index fb7bd23fc8c..d926c7de3d6 100644 --- a/token/client/src/client.rs +++ b/token/client/src/client.rs @@ -1,7 +1,9 @@ use { async_trait::async_trait, - solana_client::nonblocking::rpc_client::RpcClient, + solana_banks_interface::BanksTransactionResultWithSimulation, solana_program_test::{tokio::sync::Mutex, BanksClient, ProgramTestContext}, + solana_rpc_client::nonblocking::rpc_client::RpcClient, + solana_rpc_client_api::response::RpcSimulateTransactionResult, solana_sdk::{ account::Account, hash::Hash, pubkey::Pubkey, signature::Signature, transaction::Transaction, @@ -16,6 +18,11 @@ pub trait SendTransaction { type Output; } +/// Basic trait for simulating transactions in a validator. +pub trait SimulateTransaction { + type SimulationOutput; +} + /// Extends basic `SendTransaction` trait with function `send` where client is `&mut BanksClient`. /// Required for `ProgramBanksClient`. pub trait SendTransactionBanksClient: SendTransaction { @@ -26,6 +33,16 @@ pub trait SendTransactionBanksClient: SendTransaction { ) -> BoxFuture<'a, ProgramClientResult>; } +/// Extends basic `SimulateTransaction` trait with function `simulation` where client is `&mut BanksClient`. +/// Required for `ProgramBanksClient`. +pub trait SimulateTransactionBanksClient: SimulateTransaction { + fn simulate<'a>( + &self, + client: &'a mut BanksClient, + transaction: Transaction, + ) -> BoxFuture<'a, ProgramClientResult>; +} + /// Send transaction to validator using `BanksClient::process_transaction`. #[derive(Debug, Clone, Copy, Default)] pub struct ProgramBanksClientProcessTransaction; @@ -49,6 +66,25 @@ impl SendTransactionBanksClient for ProgramBanksClientProcessTransaction { } } +impl SimulateTransaction for ProgramBanksClientProcessTransaction { + type SimulationOutput = BanksTransactionResultWithSimulation; +} + +impl SimulateTransactionBanksClient for ProgramBanksClientProcessTransaction { + fn simulate<'a>( + &self, + client: &'a mut BanksClient, + transaction: Transaction, + ) -> BoxFuture<'a, ProgramClientResult> { + Box::pin(async move { + client + .simulate_transaction(transaction) + .await + .map_err(Into::into) + }) + } +} + /// Extends basic `SendTransaction` trait with function `send` where client is `&RpcClient`. /// Required for `ProgramRpcClient`. pub trait SendTransactionRpc: SendTransaction { @@ -59,6 +95,16 @@ pub trait SendTransactionRpc: SendTransaction { ) -> BoxFuture<'a, ProgramClientResult>; } +/// Extends basic `SimulateTransaction` trait with function `simulate` where client is `&RpcClient`. +/// Required for `ProgramRpcClient`. +pub trait SimulateTransactionRpc: SimulateTransaction { + fn simulate<'a>( + &self, + client: &'a RpcClient, + transaction: &'a Transaction, + ) -> BoxFuture<'a, ProgramClientResult>; +} + #[derive(Debug, Clone, Copy, Default)] pub struct ProgramRpcClientSendTransaction; @@ -66,6 +112,7 @@ pub struct ProgramRpcClientSendTransaction; pub enum RpcClientResponse { Signature(Signature), Transaction(Transaction), + Simulation(RpcSimulateTransactionResult), } impl SendTransaction for ProgramRpcClientSendTransaction { @@ -92,6 +139,26 @@ impl SendTransactionRpc for ProgramRpcClientSendTransaction { } } +impl SimulateTransaction for ProgramRpcClientSendTransaction { + type SimulationOutput = RpcClientResponse; +} + +impl SimulateTransactionRpc for ProgramRpcClientSendTransaction { + fn simulate<'a>( + &self, + client: &'a RpcClient, + transaction: &'a Transaction, + ) -> BoxFuture<'a, ProgramClientResult> { + Box::pin(async move { + client + .simulate_transaction(transaction) + .await + .map(|r| RpcClientResponse::Simulation(r.value)) + .map_err(Into::into) + }) + } +} + pub type ProgramClientError = Box; pub type ProgramClientResult = Result; @@ -99,7 +166,7 @@ pub type ProgramClientResult = Result; #[async_trait] pub trait ProgramClient where - ST: SendTransaction, + ST: SendTransaction + SimulateTransaction, { async fn get_minimum_balance_for_rent_exemption( &self, @@ -111,6 +178,11 @@ where async fn send_transaction(&self, transaction: &Transaction) -> ProgramClientResult; async fn get_account(&self, address: Pubkey) -> ProgramClientResult>; + + async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> ProgramClientResult; } enum ProgramBanksClientContext { @@ -163,7 +235,7 @@ impl ProgramBanksClient { #[async_trait] impl ProgramClient for ProgramBanksClient where - ST: SendTransactionBanksClient + Send + Sync, + ST: SendTransactionBanksClient + SimulateTransactionBanksClient + Send + Sync, { async fn get_minimum_balance_for_rent_exemption( &self, @@ -193,6 +265,17 @@ where .await } + async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> ProgramClientResult { + self.run_in_lock(|client| { + let transaction = transaction.clone(); + self.send.simulate(client, transaction) + }) + .await + } + async fn get_account(&self, address: Pubkey) -> ProgramClientResult> { self.run_in_lock(|client| { Box::pin(async move { client.get_account(address).await.map_err(Into::into) }) @@ -222,7 +305,7 @@ impl ProgramRpcClient { #[async_trait] impl ProgramClient for ProgramRpcClient where - ST: SendTransactionRpc + Send + Sync, + ST: SendTransactionRpc + SimulateTransactionRpc + Send + Sync, { async fn get_minimum_balance_for_rent_exemption( &self, @@ -242,6 +325,13 @@ where self.send.send(&self.client, transaction).await } + async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> ProgramClientResult { + self.send.simulate(&self.client, transaction).await + } + async fn get_account(&self, address: Pubkey) -> ProgramClientResult> { Ok(self .client @@ -275,7 +365,10 @@ impl ProgramOfflineClient { #[async_trait] impl ProgramClient for ProgramOfflineClient where - ST: SendTransaction + Send + Sync, + ST: SendTransaction + + SimulateTransaction + + Send + + Sync, { async fn get_minimum_balance_for_rent_exemption( &self, @@ -292,6 +385,13 @@ where Ok(RpcClientResponse::Transaction(transaction.clone())) } + async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> ProgramClientResult { + Ok(RpcClientResponse::Transaction(transaction.clone())) + } + async fn get_account(&self, _address: Pubkey) -> ProgramClientResult> { Err("Unable to fetch account in offline mode".into()) } diff --git a/token/client/src/lib.rs b/token/client/src/lib.rs index 7e77eee2cd7..850cbbd3f8f 100644 --- a/token/client/src/lib.rs +++ b/token/client/src/lib.rs @@ -1,6 +1,12 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] pub mod client; pub mod output; pub mod token; +/// Helper functions to generate split zero-knowledge proofs for confidential transfers. +/// +/// The logic in this submodule should belong to the `solana-zk-token-sdk` and will be removed with +/// an upgrade to the Solana program in the future. +pub mod proof_generation; + pub use spl_token_2022; diff --git a/token/client/src/output.rs b/token/client/src/output.rs index 6c8cdbaaeee..e3436ca435d 100644 --- a/token/client/src/output.rs +++ b/token/client/src/output.rs @@ -10,6 +10,11 @@ impl fmt::Display for RpcClientResponse { writeln!(f, "Transaction:")?; writeln_transaction(f, &transaction.clone().into(), None, " ", None, None) } + RpcClientResponse::Simulation(result) => { + writeln!(f, "Simulation:")?; + // maybe implement another formatter on simulation result? + writeln!(f, "{result:?}") + } } } } @@ -31,7 +36,7 @@ mod tests { #[test] fn display_signature() { let signature_bytes = [202u8; SIGNATURE_BYTES]; - let signature = RpcClientResponse::Signature(Signature::new(&signature_bytes)); + let signature = RpcClientResponse::Signature(Signature::from(signature_bytes)); println!("{}", signature); } diff --git a/token/client/src/proof_generation.rs b/token/client/src/proof_generation.rs new file mode 100644 index 00000000000..32c90fd73e6 --- /dev/null +++ b/token/client/src/proof_generation.rs @@ -0,0 +1,376 @@ +//! Helper functions to generate split zero-knowledge proofs for confidential transfers in the +//! Confidential Transfer Extension. +//! +//! The logic in this submodule should belong to the `solana-zk-token-sdk` and will be removed with +//! an upgrade to the Solana program. + +use { + curve25519_dalek::scalar::Scalar, + spl_token_2022::{ + error::TokenError, + extension::confidential_transfer::{ + ciphertext_extraction::{transfer_amount_source_ciphertext, SourceDecryptHandles}, + processor::verify_and_split_deposit_amount, + }, + solana_zk_token_sdk::{ + encryption::{ + auth_encryption::{AeCiphertext, AeKey}, + elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + grouped_elgamal::GroupedElGamal, + pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, + }, + instruction::{ + transfer::{ + combine_lo_hi_commitments, combine_lo_hi_openings, FeeEncryption, + FeeParameters, TransferAmountCiphertext, + }, + BatchedGroupedCiphertext2HandlesValidityProofData, BatchedRangeProofU256Data, + CiphertextCommitmentEqualityProofData, FeeSigmaProofData, + }, + zk_token_elgamal::ops::subtract_with_lo_hi, + }, + }, +}; + +/// The main logic to create the five split proof data for a transfer with fee. +#[allow(clippy::too_many_arguments)] +pub fn transfer_with_fee_split_proof_data( + current_available_balance: &ElGamalCiphertext, + current_decryptable_available_balance: &AeCiphertext, + transfer_amount: u64, + source_elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + transfer_fee_parameters: &FeeParameters, +) -> Result< + ( + CiphertextCommitmentEqualityProofData, + BatchedGroupedCiphertext2HandlesValidityProofData, + FeeSigmaProofData, + BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedRangeProofU256Data, + SourceDecryptHandles, + ), + TokenError, +> { + let default_auditor_pubkey = ElGamalPubkey::default(); + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); + + // Split the transfer amount into the low and high bit components. + let (transfer_amount_lo, transfer_amount_hi) = + verify_and_split_deposit_amount(transfer_amount)?; + + // Encrypt the `lo` and `hi` transfer amounts. + let (transfer_amount_grouped_ciphertext_lo, transfer_amount_opening_lo) = + TransferAmountCiphertext::new( + transfer_amount_lo, + source_elgamal_keypair.pubkey(), + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ); + + let (transfer_amount_grouped_ciphertext_hi, transfer_amount_opening_hi) = + TransferAmountCiphertext::new( + transfer_amount_hi, + source_elgamal_keypair.pubkey(), + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ); + + // Decrypt the current available balance at the source + let current_decrypted_available_balance = current_decryptable_available_balance + .decrypt(aes_key) + .ok_or(TokenError::AccountDecryption)?; + + // Compute the remaining balance at the source + let new_decrypted_available_balance = current_decrypted_available_balance + .checked_sub(transfer_amount) + .ok_or(TokenError::InsufficientFunds)?; + + // Create a new Pedersen commitment for the remaining balance at the source + let (new_available_balance_commitment, new_source_opening) = + Pedersen::new(new_decrypted_available_balance); + + // Compute the remaining balance at the source as ElGamal ciphertexts + let transfer_amount_source_ciphertext_lo = + transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_lo.into()); + let transfer_amount_source_ciphertext_hi = + transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_hi.into()); + + let current_available_balance = (*current_available_balance).into(); + let new_available_balance_ciphertext = subtract_with_lo_hi( + ¤t_available_balance, + &transfer_amount_source_ciphertext_lo, + &transfer_amount_source_ciphertext_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + let new_available_balance_ciphertext: ElGamalCiphertext = new_available_balance_ciphertext + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + + // generate equality proof data + let equality_proof_data = CiphertextCommitmentEqualityProofData::new( + source_elgamal_keypair, + &new_available_balance_ciphertext, + &new_available_balance_commitment, + &new_source_opening, + new_decrypted_available_balance, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // create source decrypt handle + let source_decrypt_handle_lo = + DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_lo); + let source_decrypt_handle_hi = + DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_hi); + + let source_decrypt_handles = SourceDecryptHandles { + lo: source_decrypt_handle_lo.into(), + hi: source_decrypt_handle_hi.into(), + }; + + // encrypt the transfer amount under the destination and auditor ElGamal public key + let transfer_amount_destination_auditor_ciphertext_lo = GroupedElGamal::encrypt_with( + [destination_elgamal_pubkey, auditor_elgamal_pubkey], + transfer_amount_lo, + &transfer_amount_opening_lo, + ); + let transfer_amount_destination_auditor_ciphertext_hi = GroupedElGamal::encrypt_with( + [destination_elgamal_pubkey, auditor_elgamal_pubkey], + transfer_amount_hi, + &transfer_amount_opening_hi, + ); + + // generate transfer amount ciphertext validity data + let transfer_amount_ciphertext_validity_proof_data = + BatchedGroupedCiphertext2HandlesValidityProofData::new( + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + &transfer_amount_destination_auditor_ciphertext_lo, + &transfer_amount_destination_auditor_ciphertext_hi, + transfer_amount_lo, + transfer_amount_hi, + &transfer_amount_opening_lo, + &transfer_amount_opening_hi, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // calculate fee + let transfer_fee_basis_points = transfer_fee_parameters.fee_rate_basis_points; + let transfer_fee_maximum_fee = transfer_fee_parameters.maximum_fee; + let (raw_fee_amount, delta_fee) = + calculate_raw_fee_and_delta(transfer_amount, transfer_fee_basis_points) + .ok_or(TokenError::Overflow)?; + + // if raw fee is greater than the maximum fee, then use the maximum fee for the fee amount + let fee_amount = std::cmp::min(transfer_fee_maximum_fee, raw_fee_amount); + + // split and encrypt fee + let (fee_amount_lo, fee_amount_hi) = verify_and_split_deposit_amount(fee_amount)?; + let (fee_ciphertext_lo, fee_opening_lo) = FeeEncryption::new( + fee_amount_lo, + destination_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + ); + let (fee_ciphertext_hi, fee_opening_hi) = FeeEncryption::new( + fee_amount_hi, + destination_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + ); + + // create combined commitments and openings to be used to generate proofs + const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 16; + let combined_transfer_amount_commitment = combine_lo_hi_commitments( + transfer_amount_grouped_ciphertext_lo.get_commitment(), + transfer_amount_grouped_ciphertext_hi.get_commitment(), + TRANSFER_AMOUNT_LO_BIT_LENGTH, + ); + let combined_transfer_amount_opening = combine_lo_hi_openings( + &transfer_amount_opening_lo, + &transfer_amount_opening_hi, + TRANSFER_AMOUNT_LO_BIT_LENGTH, + ); + + const FEE_AMOUNT_LO_BIT_LENGTH: usize = 16; + let combined_fee_commitment = combine_lo_hi_commitments( + fee_ciphertext_lo.get_commitment(), + fee_ciphertext_hi.get_commitment(), + FEE_AMOUNT_LO_BIT_LENGTH, + ); + let combined_fee_opening = + combine_lo_hi_openings(&fee_opening_lo, &fee_opening_hi, FEE_AMOUNT_LO_BIT_LENGTH); + + // compute claimed and real delta commitment + let (claimed_commitment, claimed_opening) = Pedersen::new(delta_fee); + let (delta_commitment, delta_opening) = compute_delta_commitment_and_opening( + ( + &combined_transfer_amount_commitment, + &combined_transfer_amount_opening, + ), + (&combined_fee_commitment, &combined_fee_opening), + transfer_fee_basis_points, + ); + + // generate fee sigma proof + let fee_sigma_proof_data = FeeSigmaProofData::new( + &combined_fee_commitment, + &delta_commitment, + &claimed_commitment, + &combined_fee_opening, + &delta_opening, + &claimed_opening, + fee_amount, + delta_fee, + transfer_fee_maximum_fee, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // encrypt the fee amount under the destination and withdraw withheld authority ElGamal public key + let fee_destination_withdraw_withheld_authority_ciphertext_lo = GroupedElGamal::encrypt_with( + [ + destination_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + ], + fee_amount_lo, + &fee_opening_lo, + ); + let fee_destination_withdraw_withheld_authority_ciphertext_hi = GroupedElGamal::encrypt_with( + [ + destination_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + ], + fee_amount_hi, + &fee_opening_hi, + ); + + // generate fee ciphertext validity data + let fee_ciphertext_validity_proof_data = + BatchedGroupedCiphertext2HandlesValidityProofData::new( + destination_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + &fee_destination_withdraw_withheld_authority_ciphertext_lo, + &fee_destination_withdraw_withheld_authority_ciphertext_hi, + fee_amount_lo, + fee_amount_hi, + &fee_opening_lo, + &fee_opening_hi, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // generate range proof data + const REMAINING_BALANCE_BIT_LENGTH: usize = 64; + const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32; + const DELTA_BIT_LENGTH: usize = 48; + const FEE_AMOUNT_HI_BIT_LENGTH: usize = 32; + const MAX_FEE_BASIS_POINTS: u64 = 10_000; + + let delta_fee_complement = MAX_FEE_BASIS_POINTS - delta_fee; + + let max_fee_basis_points_commitment = + Pedersen::with(MAX_FEE_BASIS_POINTS, &PedersenOpening::default()); + let claimed_complement_commitment = max_fee_basis_points_commitment - claimed_commitment; + let claimed_complement_opening = PedersenOpening::default() - &claimed_opening; + + let range_proof_data = BatchedRangeProofU256Data::new( + vec![ + &new_available_balance_commitment, + transfer_amount_grouped_ciphertext_lo.get_commitment(), + transfer_amount_grouped_ciphertext_hi.get_commitment(), + &claimed_commitment, + &claimed_complement_commitment, + fee_ciphertext_lo.get_commitment(), + fee_ciphertext_hi.get_commitment(), + ], + vec![ + new_decrypted_available_balance, + transfer_amount_lo, + transfer_amount_hi, + delta_fee, + delta_fee_complement, + fee_amount_lo, + fee_amount_hi, + ], + vec![ + REMAINING_BALANCE_BIT_LENGTH, + TRANSFER_AMOUNT_LO_BIT_LENGTH, + TRANSFER_AMOUNT_HI_BIT_LENGTH, + DELTA_BIT_LENGTH, + DELTA_BIT_LENGTH, + FEE_AMOUNT_LO_BIT_LENGTH, + FEE_AMOUNT_HI_BIT_LENGTH, + ], + vec![ + &new_source_opening, + &transfer_amount_opening_lo, + &transfer_amount_opening_hi, + &claimed_opening, + &claimed_complement_opening, + &fee_opening_lo, + &fee_opening_hi, + ], + ) + .map_err(|_| TokenError::ProofGeneration)?; + + Ok(( + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + fee_sigma_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + )) +} + +/// Calculate transfer fee and the "delta" value. The function returns the raw fee, which could be +/// greater than the maximum fee amount of a fee parameter. +/// +/// The "delta" value is a number that captures the round-off value when the fee is computed. The +/// fee is computed according to the formula `fee = transfer_amount * fee_rate_basis_points / +/// 10_000`. If no rounding occurred, then we must have `fee * 10_000 - transfer_amount * +/// fee_rate_basis_points = 0`. If there is rounding involved (`10_000` does not divide cleanly), +/// then the difference `fee * 10_000 - transfer_amount * fee_rate_basis_points` can be a non-zero +/// number between `0` and `9_999` inclusively. We call this number the "delta" value. +fn calculate_raw_fee_and_delta( + transfer_amount: u64, + fee_rate_basis_points: u16, +) -> Option<(u64, u64)> { + const ONE_IN_BASIS_POINTS: u128 = 10_000_u128; + + // compute `transfer_amount * fee_rate_basis_points` + let numerator = (transfer_amount as u128).checked_mul(fee_rate_basis_points as u128)?; + + // compute fee as `transfer_amount * fee_rate_basis_points / 10_000 ` + let fee = numerator + .checked_add(ONE_IN_BASIS_POINTS)? + .checked_sub(1)? + .checked_div(ONE_IN_BASIS_POINTS)?; + + // compute the delta fee as `fee * 10_000 - fee_rate_basis_points` + let delta_fee = fee + .checked_mul(ONE_IN_BASIS_POINTS)? + .checked_sub(numerator)?; + + Some((fee as u64, delta_fee as u64)) +} + +/// Calculate the "delta" commitment-opening pair from a transfer amount and fee commitment-opening +/// pairs. +fn compute_delta_commitment_and_opening( + (transfer_amount_commitment, transfer_amount_opening): (&PedersenCommitment, &PedersenOpening), + (fee_commitment, fee_opening): (&PedersenCommitment, &PedersenOpening), + fee_rate_basis_points: u16, +) -> (PedersenCommitment, PedersenOpening) { + const ONE_IN_BASIS_POINTS: u128 = 10_000_u128; + + let one_in_basis_points_scalar = Scalar::from(ONE_IN_BASIS_POINTS); + let fee_rate_scalar = Scalar::from(fee_rate_basis_points); + + let delta_commitment = + fee_commitment * one_in_basis_points_scalar - transfer_amount_commitment * fee_rate_scalar; + let delta_opening = + fee_opening * one_in_basis_points_scalar - transfer_amount_opening * fee_rate_scalar; + + (delta_commitment, delta_opening) +} diff --git a/token/client/src/token.rs b/token/client/src/token.rs index d101d7314f7..f5d467ad04b 100644 --- a/token/client/src/token.rs +++ b/token/client/src/token.rs @@ -1,5 +1,9 @@ use { - crate::client::{ProgramClient, ProgramClientError, SendTransaction}, + crate::{ + client::{ProgramClient, ProgramClientError, SendTransaction, SimulateTransaction}, + proof_generation::transfer_with_fee_split_proof_data, + }, + futures::{future::join_all, try_join}, futures_util::TryFutureExt, solana_program_test::tokio::time, solana_sdk::{ @@ -20,30 +24,50 @@ use { }, spl_token_2022::{ extension::{ - confidential_transfer, cpi_guard, default_account_state, interest_bearing_mint, - memo_transfer, metadata_pointer, transfer_fee, transfer_hook, BaseStateWithExtensions, - ExtensionType, StateWithExtensionsOwned, + confidential_transfer::{ + self, + account_info::{ + ApplyPendingBalanceAccountInfo, EmptyAccountAccountInfo, TransferAccountInfo, + WithdrawAccountInfo, + }, + ciphertext_extraction::SourceDecryptHandles, + instruction::{ + TransferSplitContextStateAccounts, TransferWithFeeSplitContextStateAccounts, + }, + ConfidentialTransferAccount, DecryptableBalance, + }, + confidential_transfer_fee::{ + self, account_info::WithheldTokensInfo, ConfidentialTransferFeeAmount, + ConfidentialTransferFeeConfig, + }, + cpi_guard, default_account_state, group_pointer, interest_bearing_mint, memo_transfer, + metadata_pointer, transfer_fee, transfer_hook, BaseStateWithExtensions, ExtensionType, + StateWithExtensionsOwned, }, instruction, offchain, - solana_zk_token_sdk::{errors::ProofError, zk_token_elgamal::pod::ElGamalPubkey}, + proof::ProofLocation, + solana_zk_token_sdk::{ + encryption::{ + auth_encryption::AeKey, + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey}, + }, + instruction::*, + zk_token_elgamal::pod::ElGamalPubkey as PodElGamalPubkey, + zk_token_proof_instruction::{self, ContextStateInfo, ProofInstruction}, + zk_token_proof_program, + zk_token_proof_state::ProofContextState, + }, state::{Account, AccountState, Mint, Multisig}, }, + spl_token_metadata_interface::state::{Field, TokenMetadata}, std::{ fmt, io, + mem::size_of, sync::{Arc, RwLock}, time::{Duration, Instant}, }, thiserror::Error, }; -#[cfg(feature = "proof-program")] -use { - solana_sdk::epoch_info::EpochInfo, - spl_token_2022::solana_zk_token_sdk::{ - encryption::{auth_encryption::*, elgamal::*}, - instruction::transfer_with_fee::FeeParameters, - }, - std::convert::TryInto, -}; #[derive(Error, Debug)] pub enum TokenError { @@ -61,8 +85,8 @@ pub enum TokenError { AccountInvalidAssociatedAddress, #[error("invalid auxiliary account address")] AccountInvalidAuxiliaryAddress, - #[error("proof error: {0}")] - Proof(ProofError), + #[error("proof generation")] + ProofGeneration, #[error("maximum deposit transfer amount exceeded")] MaximumDepositTransferAmountExceeded, #[error("encryption key error")] @@ -90,6 +114,7 @@ impl PartialEq for TokenError { (Self::AccountInvalidMint, Self::AccountInvalidMint) => true, (Self::AccountInvalidAssociatedAddress, Self::AccountInvalidAssociatedAddress) => true, (Self::AccountInvalidAuxiliaryAddress, Self::AccountInvalidAuxiliaryAddress) => true, + (Self::ProofGeneration, Self::ProofGeneration) => true, ( Self::MaximumDepositTransferAmountExceeded, Self::MaximumDepositTransferAmountExceeded, @@ -110,7 +135,7 @@ pub enum ExtensionInitializationParams { ConfidentialTransferMint { authority: Option, auto_approve_new_accounts: bool, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, }, DefaultAccountState { state: AccountState, @@ -140,6 +165,14 @@ pub enum ExtensionInitializationParams { authority: Option, metadata_address: Option, }, + ConfidentialTransferFeeConfig { + authority: Option, + withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey, + }, + GroupPointer { + authority: Option, + group_address: Option, + }, } impl ExtensionInitializationParams { /// Get the extension type associated with the init params @@ -154,6 +187,10 @@ impl ExtensionInitializationParams { Self::PermanentDelegate { .. } => ExtensionType::PermanentDelegate, Self::TransferHook { .. } => ExtensionType::TransferHook, Self::MetadataPointer { .. } => ExtensionType::MetadataPointer, + Self::ConfidentialTransferFeeConfig { .. } => { + ExtensionType::ConfidentialTransferFeeConfig + } + Self::GroupPointer { .. } => ExtensionType::GroupPointer, } } /// Generate an appropriate initialization instruction for the given mint @@ -234,6 +271,26 @@ impl ExtensionInitializationParams { authority, metadata_address, ), + Self::ConfidentialTransferFeeConfig { + authority, + withdraw_withheld_authority_elgamal_pubkey, + } => { + confidential_transfer_fee::instruction::initialize_confidential_transfer_fee_config( + token_program_id, + mint, + authority, + withdraw_withheld_authority_elgamal_pubkey, + ) + } + Self::GroupPointer { + authority, + group_address, + } => group_pointer::instruction::initialize( + token_program_id, + mint, + authority, + group_address, + ), } } } @@ -264,7 +321,7 @@ pub struct Token { nonce_authority: Option>, nonce_blockhash: Option, memo: Arc>>, - transfer_hook_accounts: Option>, + transfer_hook_accounts: Option>, } impl fmt::Debug for Token { @@ -308,7 +365,7 @@ fn native_mint_decimals(program_id: &Pubkey) -> u8 { impl Token where - T: SendTransaction, + T: SendTransaction + SimulateTransaction, { pub fn new( client: Arc>, @@ -372,7 +429,7 @@ where self } - pub fn with_transfer_hook_accounts(mut self, transfer_hook_accounts: Vec) -> Self { + pub fn with_transfer_hook_accounts(mut self, transfer_hook_accounts: Vec) -> Self { self.transfer_hook_accounts = Some(transfer_hook_accounts); self } @@ -501,6 +558,21 @@ where Ok(transaction) } + pub async fn simulate_ixs( + &self, + token_instructions: &[Instruction], + signing_keypairs: &S, + ) -> TokenResult { + let transaction = self + .construct_tx(token_instructions, signing_keypairs) + .await?; + + self.client + .simulate_transaction(&transaction) + .await + .map_err(TokenError::Client) + } + pub async fn process_ixs( &self, token_instructions: &[Instruction], @@ -530,7 +602,7 @@ where .iter() .map(|e| e.extension()) .collect::>(); - let space = ExtensionType::try_get_account_len::(&extension_types)?; + let space = ExtensionType::try_calculate_account_len::(&extension_types)?; let mut instructions = vec![system_instruction::create_account( &self.payer.pubkey(), @@ -652,7 +724,7 @@ where required_extensions.push(extension_type); } } - let space = ExtensionType::try_get_account_len::(&required_extensions)?; + let space = ExtensionType::try_calculate_account_len::(&required_extensions)?; let mut instructions = vec![system_instruction::create_account( &self.payer.pubkey(), &account.pubkey(), @@ -690,9 +762,10 @@ where .ok_or(TokenError::AccountNotFound) } - /// Retrive mint information. - pub async fn get_mint_info(&self) -> TokenResult> { - let account = self.get_account(self.pubkey).await?; + fn unpack_mint_info( + &self, + account: BaseAccount, + ) -> TokenResult> { if account.owner != self.program_id { return Err(TokenError::AccountInvalidOwner); } @@ -709,6 +782,12 @@ where mint_result } + /// Retrive mint information. + pub async fn get_mint_info(&self) -> TokenResult> { + let account = self.get_account(self.pubkey).await?; + self.unpack_mint_info(account) + } + /// Retrieve account information. pub async fn get_account_info( &self, @@ -840,13 +919,10 @@ where )? }; if let Some(transfer_hook_accounts) = &self.transfer_hook_accounts { - let additional_account_metas = transfer_hook_accounts - .iter() - .map(|p| AccountMeta::new_readonly(*p, false)); - instruction.accounts.extend(additional_account_metas); + instruction.accounts.extend(transfer_hook_accounts.clone()); } else { - offchain::get_extra_transfer_account_metas( - &mut instruction.accounts, + offchain::resolve_extra_transfer_account_metas( + &mut instruction, |address| { self.client .get_account(address) @@ -1258,7 +1334,7 @@ where } else { vec![] }; - let space = ExtensionType::try_get_account_len::(&extensions)?; + let space = ExtensionType::try_calculate_account_len::(&extensions)?; instructions.push(system_instruction::create_account( &self.payer.pubkey(), @@ -1595,925 +1671,1679 @@ where .await } + /// Update group pointer address + pub async fn update_group_address( + &self, + authority: &Pubkey, + new_group_address: Option, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + + self.process_ixs( + &[group_pointer::instruction::update( + &self.program_id, + self.get_address(), + authority, + &multisig_signers, + new_group_address, + )?], + signing_keypairs, + ) + .await + } + /// Update confidential transfer mint - pub async fn confidential_transfer_update_mint( + pub async fn confidential_transfer_update_mint( &self, - authority: &S, + authority: &Pubkey, auto_approve_new_account: bool, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[confidential_transfer::instruction::update_mint( &self.program_id, &self.pubkey, - &authority.pubkey(), + authority, + &multisig_signers, auto_approve_new_account, auditor_elgamal_pubkey, )?], - &[authority], + signing_keypairs, ) .await } - /// Configures confidential transfers for a token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_configure_token_account( + /// Configures confidential transfers for a token account. If the maximum pending balance + /// credit counter for the extension is not provided, then it is set to be a default value of + /// `2^16`. + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_configure_token_account( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + context_state_account: Option<&Pubkey>, + maximum_pending_balance_credit_counter: Option, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + signing_keypairs: &S, ) -> TokenResult { - let maximum_pending_balance_credit_counter = - 2 << confidential_transfer::MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH; + const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536; - self.confidential_transfer_configure_token_account_with_pending_counter( - token_account, - authority, - maximum_pending_balance_credit_counter, - ) - .await - } + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_configure_token_account_with_pending_counter( - &self, - token_account: &Pubkey, - authority: &S, - maximum_pending_balance_credit_counter: u64, - ) -> TokenResult { - let elgamal_keypair = - ElGamalKeypair::new(authority, token_account).map_err(TokenError::Key)?; - let decryptable_zero_balance = AeKey::new(authority, token_account) - .map_err(TokenError::Key)? - .encrypt(0); + let maximum_pending_balance_credit_counter = maximum_pending_balance_credit_counter + .unwrap_or(DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER); - self.confidential_transfer_configure_token_account_with_pending_counter_and_keypair( - token_account, - authority, - maximum_pending_balance_credit_counter, - &elgamal_keypair, - decryptable_zero_balance, - ) - .await - } + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + confidential_transfer::instruction::PubkeyValidityData::new(elgamal_keypair) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_configure_token_account_with_pending_counter_and_keypair< - S: Signer, - >( - &self, - token_account: &Pubkey, - authority: &S, - maximum_pending_balance_credit_counter: u64, - elgamal_keypair: &ElGamalKeypair, - decryptable_zero_balance: AeCiphertext, - ) -> TokenResult { - let proof_data = - confidential_transfer::instruction::PubkeyValidityData::new(elgamal_keypair) - .map_err(TokenError::Proof)?; + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; + + let decryptable_balance = aes_key.encrypt(0); self.process_ixs( &confidential_transfer::instruction::configure_account( &self.program_id, - token_account, + account, &self.pubkey, - decryptable_zero_balance, + decryptable_balance, maximum_pending_balance_credit_counter, - &authority.pubkey(), - &[], - &proof_data, + authority, + &multisig_signers, + proof_location, )?, - &[authority], + signing_keypairs, ) .await } /// Approves a token account for confidential transfers - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_approve_account( + pub async fn confidential_transfer_approve_account( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[confidential_transfer::instruction::approve_account( &self.program_id, - token_account, + account, &self.pubkey, - &authority.pubkey(), + authority, + &multisig_signers, )?], - &[authority], + signing_keypairs, ) .await } /// Prepare a token account with the confidential transfer extension for closing - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_empty_account( - &self, - token_account: &Pubkey, - authority: &S, - ) -> TokenResult { - let elgamal_keypair = - ElGamalKeypair::new(authority, token_account).map_err(TokenError::Key)?; - self.confidential_transfer_empty_account_with_keypair( - token_account, - authority, - &elgamal_keypair, - ) - .await - } - - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_empty_account_with_keypair( + pub async fn confidential_transfer_empty_account( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + context_state_account: Option<&Pubkey>, + account_info: Option, elgamal_keypair: &ElGamalKeypair, + signing_keypairs: &S, ) -> TokenResult { - let state = self.get_account_info(token_account).await.unwrap(); - let extension = - state.get_extension::()?; + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); - let proof_data = confidential_transfer::instruction::CloseAccountData::new( - elgamal_keypair, - &extension.available_balance.try_into().unwrap(), - ) - .map_err(TokenError::Proof)?; + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(account).await?; + let confidential_transfer_account = + account.get_extension::()?; + EmptyAccountAccountInfo::new(confidential_transfer_account) + }; + + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_proof_data(elgamal_keypair) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; self.process_ixs( &confidential_transfer::instruction::empty_account( &self.program_id, - token_account, - &authority.pubkey(), - &[], - &proof_data, + account, + authority, + &multisig_signers, + proof_location, )?, - &[authority], - ) - .await - } - - /// Fetch and decrypt the available balance of a confidential token account using the uniquely - /// derived decryption key from a signer - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_available_balance( - &self, - token_account: &Pubkey, - authority: &S, - ) -> TokenResult { - let authenticated_encryption_key = - AeKey::new(authority, token_account).map_err(TokenError::Key)?; - - self.confidential_transfer_get_available_balance_with_key( - token_account, - &authenticated_encryption_key, - ) - .await - } - - /// Fetch and decrypt the available balance of a confidential token account using a custom - /// decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_available_balance_with_key( - &self, - token_account: &Pubkey, - authenticated_encryption_key: &AeKey, - ) -> TokenResult { - let state = self.get_account_info(token_account).await.unwrap(); - let extension = - state.get_extension::()?; - - let decryptable_balance_ciphertext: AeCiphertext = extension - .decryptable_available_balance - .try_into() - .map_err(TokenError::Proof)?; - let decryptable_balance = decryptable_balance_ciphertext - .decrypt(authenticated_encryption_key) - .ok_or(TokenError::AccountDecryption)?; - - Ok(decryptable_balance) - } - - /// Fetch and decrypt the pending balance of a confidential token account using the uniquely - /// derived decryption key from a signer - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_pending_balance( - &self, - token_account: &Pubkey, - authority: &S, - ) -> TokenResult { - let elgamal_keypair = - ElGamalKeypair::new(authority, token_account).map_err(TokenError::Key)?; - - self.confidential_transfer_get_pending_balance_with_key(token_account, &elgamal_keypair) - .await - } - - /// Fetch and decrypt the pending balance of a confidential token account using a custom - /// decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_pending_balance_with_key( - &self, - token_account: &Pubkey, - elgamal_keypair: &ElGamalKeypair, - ) -> TokenResult { - let state = self.get_account_info(token_account).await.unwrap(); - let extension = - state.get_extension::()?; - - // decrypt pending balance - let pending_balance_lo = extension - .pending_balance_lo - .decrypt(&elgamal_keypair.secret) - .ok_or(TokenError::AccountDecryption)?; - let pending_balance_hi = extension - .pending_balance_hi - .decrypt(&elgamal_keypair.secret) - .ok_or(TokenError::AccountDecryption)?; - - let pending_balance = pending_balance_lo - .checked_add(pending_balance_hi << confidential_transfer::PENDING_BALANCE_HI_BIT_LENGTH) - .ok_or(TokenError::AccountDecryption)?; - - Ok(pending_balance) - } - - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_withheld_amount( - &self, - withdraw_withheld_authority: &S, - sources: &[&Pubkey], - ) -> TokenResult { - let withdraw_withheld_authority_elgamal_keypair = - ElGamalKeypair::new(withdraw_withheld_authority, &self.pubkey) - .map_err(TokenError::Key)?; - - self.confidential_transfer_get_withheld_amount_with_key( - &withdraw_withheld_authority_elgamal_keypair, - sources, + signing_keypairs, ) .await } - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_withheld_amount_with_key( - &self, - withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, - sources: &[&Pubkey], - ) -> TokenResult { - let mut aggregate_withheld_amount_ciphertext = ElGamalCiphertext::default(); - for &source in sources { - let state = self.get_account_info(source).await.unwrap(); - let extension = - state.get_extension::()?; - - let withheld_amount_ciphertext: ElGamalCiphertext = - extension.withheld_amount.try_into().unwrap(); - - aggregate_withheld_amount_ciphertext = - aggregate_withheld_amount_ciphertext + withheld_amount_ciphertext; - } - - let aggregate_withheld_amount = aggregate_withheld_amount_ciphertext - .decrypt_u32(&withdraw_withheld_authority_elgamal_keypair.secret) - .ok_or(TokenError::AccountDecryption)?; - - Ok(aggregate_withheld_amount) - } - - /// Fetch the ElGamal public key associated with a confidential token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_elgamal_pubkey( - &self, - token_account: &Pubkey, - ) -> TokenResult { - let state = self.get_account_info(token_account).await.unwrap(); - let extension = - state.get_extension::()?; - let elgamal_pubkey = extension - .elgamal_pubkey - .try_into() - .map_err(TokenError::Proof)?; - - Ok(elgamal_pubkey) - } - - /// Fetch the ElGamal pubkey key of the auditor associated with a confidential token mint - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_auditor_elgamal_pubkey( - &self, - ) -> TokenResult> { - let mint_state = self.get_mint_info().await.unwrap(); - let ct_mint = - mint_state.get_extension::()?; - let auditor_elgamal_pubkey: Option = ct_mint.auditor_elgamal_pubkey.into(); - - if let Some(elgamal_pubkey) = auditor_elgamal_pubkey { - let elgamal_pubkey: ElGamalPubkey = - elgamal_pubkey.try_into().map_err(TokenError::Proof)?; - Ok(Some(elgamal_pubkey)) - } else { - Ok(None) - } - } - - /// Fetch the ElGamal pubkey key of the withdraw withheld authority associated with a - /// confidential token mint - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_get_withdraw_withheld_authority_elgamal_pubkey( - &self, - ) -> TokenResult> { - let mint_state = self.get_mint_info().await.unwrap(); - let ct_mint = - mint_state.get_extension::()?; - let withdraw_withheld_authority_elgamal_pubkey: Option = - ct_mint.withdraw_withheld_authority_elgamal_pubkey.into(); - - if let Some(elgamal_pubkey) = withdraw_withheld_authority_elgamal_pubkey { - let elgamal_pubkey: ElGamalPubkey = - elgamal_pubkey.try_into().map_err(TokenError::Proof)?; - Ok(Some(elgamal_pubkey)) - } else { - Ok(None) - } - } - /// Deposit SPL Tokens into the pending balance of a confidential token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_deposit( + pub async fn confidential_transfer_deposit( &self, - token_account: &Pubkey, - token_authority: &S, + account: &Pubkey, + authority: &Pubkey, amount: u64, decimals: u8, + signing_keypairs: &S, ) -> TokenResult { - if amount >> confidential_transfer::MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH != 0 { - return Err(TokenError::MaximumDepositTransferAmountExceeded); - } + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); self.process_ixs( &[confidential_transfer::instruction::deposit( &self.program_id, - token_account, + account, &self.pubkey, amount, decimals, - &token_authority.pubkey(), - &[], + authority, + &multisig_signers, )?], - &[token_authority], + signing_keypairs, ) .await } - /// Withdraw SPL Tokens from the available balance of a confidential token account using the - /// uniquely derived decryption key from a signer + /// Withdraw SPL Tokens from the available balance of a confidential token account #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw( + pub async fn confidential_transfer_withdraw( &self, - token_account: &Pubkey, - token_authority: &S, - amount: u64, - available_balance: u64, - available_balance_ciphertext: &ElGamalCiphertext, + account: &Pubkey, + authority: &Pubkey, + context_state_account: Option<&Pubkey>, + withdraw_amount: u64, decimals: u8, + account_info: Option, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + signing_keypairs: &S, ) -> TokenResult { - let elgamal_keypair = - ElGamalKeypair::new(token_authority, token_account).map_err(TokenError::Key)?; - let authenticated_encryption_key = - AeKey::new(token_authority, token_account).map_err(TokenError::Key)?; + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); - self.confidential_transfer_withdraw_with_key( - token_account, - token_authority, - amount, - decimals, - available_balance, - available_balance_ciphertext, - &elgamal_keypair, - &authenticated_encryption_key, - ) - .await - } + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(account).await?; + let confidential_transfer_account = + account.get_extension::()?; + WithdrawAccountInfo::new(confidential_transfer_account) + }; - /// Withdraw SPL Tokens from the available balance of a confidential token account using custom - /// keys - #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw_with_key( - &self, - token_account: &Pubkey, - token_authority: &S, - amount: u64, - decimals: u8, - available_balance: u64, - available_balance_ciphertext: &ElGamalCiphertext, - elgamal_keypair: &ElGamalKeypair, - authenticated_encryption_key: &AeKey, - ) -> TokenResult { - let proof_data = confidential_transfer::instruction::WithdrawData::new( - amount, - elgamal_keypair, - available_balance, - available_balance_ciphertext, - ) - .map_err(TokenError::Proof)?; + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_proof_data(withdraw_amount, elgamal_keypair, aes_key) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; - let remaining_balance = available_balance - .checked_sub(amount) - .ok_or(TokenError::NotEnoughFunds)?; - let new_decryptable_available_balance = - authenticated_encryption_key.encrypt(remaining_balance); + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(withdraw_amount, aes_key) + .map_err(|_| TokenError::AccountDecryption)?; self.process_ixs( &confidential_transfer::instruction::withdraw( &self.program_id, - token_account, + account, &self.pubkey, - amount, + withdraw_amount, decimals, new_decryptable_available_balance, - &token_authority.pubkey(), - &[], - &proof_data, + authority, + &multisig_signers, + proof_location, )?, - &[token_authority], + signing_keypairs, ) .await } - /// Transfer tokens confidentially using the uniquely derived decryption keys from a signer - #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_transfer( + /// Create withdraw proof context state account for a confidential transfer withdraw + /// instruction. + pub async fn create_withdraw_proof_context_state( &self, - source_token_account: &Pubkey, - destination_token_account: &Pubkey, - source_token_authority: &S, - amount: u64, - source_available_balance: u64, - source_available_balance_ciphertext: &ElGamalCiphertext, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option, - ) -> TokenResult { - let source_elgamal_keypair = - ElGamalKeypair::new(source_token_authority, source_token_account) - .map_err(TokenError::Key)?; - let source_authenticated_encryption_key = - AeKey::new(source_token_authority, source_token_account).map_err(TokenError::Key)?; - - self.confidential_transfer_transfer_with_key( - source_token_account, - destination_token_account, - source_token_authority, - amount, - source_available_balance, - source_available_balance_ciphertext, - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - &source_elgamal_keypair, - &source_authenticated_encryption_key, - ) + context_state_account: &Pubkey, + context_state_authority: &Pubkey, + withdraw_proof_data: &WithdrawData, + withdraw_proof_signer: &S, + ) -> TokenResult { + // create withdraw proof context state + let instruction_type = ProofInstruction::VerifyWithdraw; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + + let withdraw_proof_context_state_info = ContextStateInfo { + context_state_account, + context_state_authority, + }; + + self.process_ixs( + &[system_instruction::create_account( + &self.payer.pubkey(), + context_state_account, + rent, + space as u64, + &zk_token_proof_program::id(), + )], + &[withdraw_proof_signer], + ) + .await?; + + self.process_ixs( + &[instruction_type + .encode_verify_proof(Some(withdraw_proof_context_state_info), withdraw_proof_data)], + &[] as &[&dyn Signer; 0], + ) .await } - /// Transfer tokens confidentially using custom decryption keys + /// Transfer tokens confidentially #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_transfer_with_key( - &self, - source_token_account: &Pubkey, - destination_token_account: &Pubkey, - source_token_authority: &S, - amount: u64, - source_available_balance: u64, - source_available_balance_ciphertext: &ElGamalCiphertext, + pub async fn confidential_transfer_transfer( + &self, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_account: Option<&Pubkey>, + transfer_amount: u64, + account_info: Option, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys); + + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_transfer_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; + + self.process_ixs( + &confidential_transfer::instruction::transfer( + &self.program_id, + source_account, + &self.pubkey, + destination_account, + new_decryptable_available_balance, + source_authority, + &multisig_signers, + proof_location, + )?, + signing_keypairs, + ) + .await + } + + /// Transfer tokens confidentially using split proofs. + /// + /// This function assumes that proof context states have already been created. + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_transfer_with_split_proofs( + &self, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + transfer_amount: u64, + account_info: Option, + source_aes_key: &AeKey, + source_decrypt_handles: &SourceDecryptHandles, + signing_keypairs: &S, + ) -> TokenResult { + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; + + self.process_ixs( + &[ + confidential_transfer::instruction::transfer_with_split_proofs( + &self.program_id, + source_account, + &self.pubkey, + destination_account, + new_decryptable_available_balance.into(), + source_authority, + context_state_accounts, + source_decrypt_handles, + )?, + ], + signing_keypairs, + ) + .await + } + + /// Transfer tokens confidentially using split proofs in parallel + /// + /// This function internally generates the ZK Token proof instructions to create the necessary + /// proof context states. + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_transfer_with_split_proofs_in_parallel( + &self, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + transfer_amount: u64, + account_info: Option, source_elgamal_keypair: &ElGamalKeypair, - source_authenticated_encryption_key: &AeKey, + source_aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + equality_and_ciphertext_validity_proof_signers: &S, + range_proof_signers: &S, + ) -> TokenResult<(T::Output, T::Output)> { + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let ( + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + ) = account_info + .generate_split_transfer_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; + + let transfer_instruction = confidential_transfer::instruction::transfer_with_split_proofs( + &self.program_id, + source_account, + &self.pubkey, + destination_account, + new_decryptable_available_balance.into(), + source_authority, + context_state_accounts, + &source_decrypt_handles, + )?; + + let transfer_with_equality_and_ciphertext_validity = self + .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_parallel( + context_state_accounts, + &equality_proof_data, + &ciphertext_validity_proof_data, + &transfer_instruction, + equality_and_ciphertext_validity_proof_signers, + ); + + let transfer_with_range_proof = self + .create_range_proof_context_state_for_transfer_parallel( + context_state_accounts, + &range_proof_data, + &transfer_instruction, + range_proof_signers, + ); + + try_join!( + transfer_with_equality_and_ciphertext_validity, + transfer_with_range_proof + ) + } + + /// Create equality proof context state account for a confidential transfer. + #[allow(clippy::too_many_arguments)] + pub async fn create_equality_proof_context_state_for_transfer( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + equality_proof_signer: &S, + ) -> TokenResult { + // create equality proof context state + let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + + let equality_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.equality_proof, + context_state_authority: context_state_accounts.authority, + }; + + self.process_ixs( + &[ + system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.equality_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof( + Some(equality_proof_context_state_info), + equality_proof_data, + ), + ], + &[equality_proof_signer], + ) + .await + } + + /// Create ciphertext validity proof context state account for a confidential transfer. + pub async fn create_ciphertext_validity_proof_context_state_for_transfer( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + ciphertext_validity_proof_signer: &S, + ) -> TokenResult { + // create ciphertext validity proof context state + let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; + let space = + size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + + let ciphertext_validity_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.ciphertext_validity_proof, + context_state_authority: context_state_accounts.authority, + }; + + self.process_ixs( + &[ + system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.ciphertext_validity_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof( + Some(ciphertext_validity_proof_context_state_info), + ciphertext_validity_proof_data, + ), + ], + &[ciphertext_validity_proof_signer], + ) + .await + } + + /// Create equality and ciphertext validity proof context state accounts for a confidential transfer. + #[allow(clippy::too_many_arguments)] + pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer< + S: Signers, + >( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + signing_keypairs: &S, + ) -> TokenResult { + self.create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer( + context_state_accounts, + equality_proof_data, + ciphertext_validity_proof_data, + None, + signing_keypairs, + ) + .await + } + + /// Create equality and ciphertext validity proof context state accounts with a confidential + /// transfer instruction. + #[allow(clippy::too_many_arguments)] + pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_parallel< + S: Signers, + >( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: &Instruction, + signing_keypairs: &S, ) -> TokenResult { - if amount >> confidential_transfer::MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH != 0 { - return Err(TokenError::MaximumDepositTransferAmountExceeded); + self.create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer( + context_state_accounts, + equality_proof_data, + ciphertext_validity_proof_data, + Some(transfer_instruction), + signing_keypairs, + ) + .await + } + + /// Create equality and ciphertext validity proof context states for a confidential transfer. + /// + /// If an optional transfer instruction is provided, then the transfer instruction is attached + /// to the same transaction. + #[allow(clippy::too_many_arguments)] + async fn create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer< + S: Signers, + >( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: Option<&Instruction>, + signing_keypairs: &S, + ) -> TokenResult { + let mut instructions = vec![]; + + // create equality proof context state + let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.equality_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let equality_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.equality_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push( + instruction_type + .encode_verify_proof(Some(equality_proof_context_state_info), equality_proof_data), + ); + + // create ciphertext validity proof context state + let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; + let space = + size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.ciphertext_validity_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let ciphertext_validity_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.ciphertext_validity_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push(instruction_type.encode_verify_proof( + Some(ciphertext_validity_proof_context_state_info), + ciphertext_validity_proof_data, + )); + + // add transfer instruction + if let Some(transfer_instruction) = transfer_instruction { + instructions.push(transfer_instruction.clone()); } - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or_default(); + self.process_ixs(&instructions, signing_keypairs).await + } - let proof_data = confidential_transfer::instruction::TransferData::new( - amount, - ( - source_available_balance, - source_available_balance_ciphertext, + /// Create a range proof context state account for a confidential transfer. + pub async fn create_range_proof_context_state_for_transfer( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU128Data, + range_proof_keypair: &S, + ) -> TokenResult { + let instruction_type = ProofInstruction::VerifyBatchedRangeProofU128; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + let range_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.range_proof, + context_state_authority: context_state_accounts.authority, + }; + self.process_ixs( + &[system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.range_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )], + &[range_proof_keypair], + ) + .await?; + + // This instruction is right at the transaction size limit, but in the + // future it might be able to support the transfer too + self.process_ixs( + &[instruction_type + .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data)], + &[] as &[&dyn Signer; 0], + ) + .await + } + + /// Create a range proof context state account with a confidential transfer instruction. + pub async fn create_range_proof_context_state_for_transfer_parallel( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU128Data, + transfer_instruction: &Instruction, + signing_keypairs: &S, + ) -> TokenResult { + self.create_range_proof_context_state_with_optional_transfer( + context_state_accounts, + range_proof_data, + Some(transfer_instruction), + signing_keypairs, + ) + .await + } + + /// Create a range proof context state account and an optional confidential transfer instruction. + async fn create_range_proof_context_state_with_optional_transfer( + &self, + context_state_accounts: TransferSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU128Data, + transfer_instruction: Option<&Instruction>, + signing_keypairs: &S, + ) -> TokenResult { + let instruction_type = ProofInstruction::VerifyBatchedRangeProofU128; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + let range_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.range_proof, + context_state_authority: context_state_accounts.authority, + }; + + let mut instructions = vec![ + system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.range_proof, + rent, + space as u64, + &zk_token_proof_program::id(), ), - source_elgamal_keypair, - (destination_elgamal_pubkey, &auditor_elgamal_pubkey), + instruction_type + .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data), + ]; + + if let Some(transfer_instruction) = transfer_instruction { + instructions.push(transfer_instruction.clone()); + } + + self.process_ixs(&instructions, signing_keypairs).await + } + + /// Close a ZK Token proof program context state + pub async fn confidential_transfer_close_context_state( + &self, + context_state_account: &Pubkey, + lamport_destination_account: &Pubkey, + context_state_authority: &Pubkey, + signing_keypairs: &S, + ) -> TokenResult { + let context_state_info = ContextStateInfo { + context_state_account, + context_state_authority, + }; + + self.process_ixs( + &[zk_token_proof_instruction::close_context_state( + context_state_info, + lamport_destination_account, + )], + signing_keypairs, ) - .map_err(TokenError::Proof)?; + .await + } - let source_remaining_balance = source_available_balance - .checked_sub(amount) - .ok_or(TokenError::NotEnoughFunds)?; - let new_source_available_balance = - source_authenticated_encryption_key.encrypt(source_remaining_balance); + /// Transfer tokens confidentially with fee + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_transfer_with_fee( + &self, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_account: Option<&Pubkey>, + transfer_amount: u64, + account_info: Option, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + fee_rate_basis_points: u16, + maximum_fee: u64, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys); + + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_transfer_with_fee_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + ) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; self.process_ixs( - &confidential_transfer::instruction::transfer( + &confidential_transfer::instruction::transfer_with_fee( &self.program_id, - source_token_account, - destination_token_account, + source_account, + destination_account, &self.pubkey, - new_source_available_balance, - &source_token_authority.pubkey(), - &[], - &proof_data, + new_decryptable_available_balance, + source_authority, + &multisig_signers, + proof_location, )?, - &[source_token_authority], + signing_keypairs, ) .await } - /// Transfer tokens confidentially with fee using the uniquely derived decryption keys from a - /// signer + /// Transfer tokens confidentially with fee using split proofs. + /// + /// This function assumes that proof context states have already been created. #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_transfer_with_fee( + pub async fn confidential_transfer_transfer_with_fee_and_split_proofs( + &self, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + transfer_amount: u64, + account_info: Option, + source_aes_key: &AeKey, + source_decrypt_handles: &SourceDecryptHandles, + signing_keypairs: &S, + ) -> TokenResult { + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; + + self.process_ixs( + &[ + confidential_transfer::instruction::transfer_with_fee_and_split_proofs( + &self.program_id, + source_account, + &self.pubkey, + destination_account, + new_decryptable_available_balance.into(), + source_authority, + context_state_accounts, + source_decrypt_handles, + )?, + ], + signing_keypairs, + ) + .await + } + + /// Transfer tokens confidentially using split proofs in parallel + /// + /// This function internally generates the ZK Token proof instructions to create the necessary + /// proof context states. + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_transfer_with_fee_and_split_proofs_in_parallel< + S: Signers, + >( &self, - source_token_account: &Pubkey, - destination_token_account: &Pubkey, - source_token_authority: &S, - amount: u64, - source_available_balance: u64, - source_available_balance_ciphertext: &ElGamalCiphertext, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + transfer_amount: u64, + account_info: Option, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, - epoch_info: &EpochInfo, - ) -> TokenResult { - let source_elgamal_keypair = - ElGamalKeypair::new(source_token_authority, source_token_account) - .map_err(TokenError::Key)?; - let source_authenticated_encryption_key = - AeKey::new(source_token_authority, source_token_account).map_err(TokenError::Key)?; - - self.confidential_transfer_transfer_with_fee_with_key( - source_token_account, - destination_token_account, - source_token_authority, - amount, - source_available_balance, - source_available_balance_ciphertext, + fee_rate_basis_points: u16, + maximum_fee: u64, + equality_and_ciphertext_validity_proof_signers: &S, + fee_sigma_proof_signers: &S, + range_proof_signers: &S, + ) -> TokenResult<(T::Output, T::Output, T::Output)> { + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(source_account).await?; + let confidential_transfer_account = + account.get_extension::()?; + TransferAccountInfo::new(confidential_transfer_account) + }; + + let current_source_available_balance = account_info + .available_balance + .try_into() + .map_err(|_| TokenError::AccountDecryption)?; + let current_decryptable_available_balance = account_info + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::AccountDecryption)?; + + let fee_parameters = FeeParameters { + fee_rate_basis_points, + maximum_fee, + }; + + let ( + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + fee_sigma_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + ) = transfer_with_fee_split_proof_data( + ¤t_source_available_balance, + ¤t_decryptable_available_balance, + transfer_amount, + source_elgamal_keypair, + source_aes_key, destination_elgamal_pubkey, auditor_elgamal_pubkey, withdraw_withheld_authority_elgamal_pubkey, - &source_elgamal_keypair, - &source_authenticated_encryption_key, - epoch_info, + &fee_parameters, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(transfer_amount, source_aes_key) + .map_err(|_| TokenError::AccountDecryption)?; + + let transfer_instruction = + confidential_transfer::instruction::transfer_with_fee_and_split_proofs( + &self.program_id, + source_account, + &self.pubkey, + destination_account, + new_decryptable_available_balance.into(), + source_authority, + context_state_accounts, + &source_decrypt_handles, + )?; + + let transfer_with_equality_and_ciphertext_valdity = self + .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel( + context_state_accounts, + &equality_proof_data, + &transfer_amount_ciphertext_validity_proof_data, + &transfer_instruction, + equality_and_ciphertext_validity_proof_signers + ); + + let transfer_with_fee_sigma_and_ciphertext_validity = self + .create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel( + context_state_accounts, + &fee_sigma_proof_data, + &fee_ciphertext_validity_proof_data, + &transfer_instruction, + fee_sigma_proof_signers, + ); + + let transfer_with_range_proof = self + .create_range_proof_context_state_for_transfer_with_fee_parallel( + context_state_accounts, + &range_proof_data, + &transfer_instruction, + range_proof_signers, + ); + + try_join!( + transfer_with_equality_and_ciphertext_valdity, + transfer_with_fee_sigma_and_ciphertext_validity, + transfer_with_range_proof, + ) + } + + /// Create equality and transfer amount ciphertext validity proof context state accounts for a + /// confidential transfer with fee. + #[allow(clippy::too_many_arguments)] + pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee< + S: Signers, + >( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + signing_keypairs: &S, + ) -> TokenResult { + self.create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( + context_state_accounts, + equality_proof_data, + ciphertext_validity_proof_data, + None, + signing_keypairs, ) .await } - /// Transfer tokens confidential with fee using custom decryption keys + /// Create equality and transfer amount ciphertext validity proof context state accounts with a confidential + /// transfer instruction. #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_transfer_with_fee_with_key( + pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel< + S: Signers, + >( &self, - source_token_account: &Pubkey, - destination_token_account: &Pubkey, - source_token_authority: &S, - amount: u64, - source_available_balance: u64, - source_available_balance_ciphertext: &ElGamalCiphertext, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option, - withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, - source_elgamal_keypair: &ElGamalKeypair, - source_authenticated_encryption_key: &AeKey, - epoch_info: &EpochInfo, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: &Instruction, + signing_keypairs: &S, + ) -> TokenResult { + self.create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( + context_state_accounts, + equality_proof_data, + ciphertext_validity_proof_data, + Some(transfer_instruction), + signing_keypairs, + ) + .await + } + + /// Create equality and ciphertext validity proof context states for a confidential transfer + /// with fee. + /// + /// If an optional transfer instruction is provided, then the transfer instruction is attached + /// to the same transaction. + #[allow(clippy::too_many_arguments)] + async fn create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee< + S: Signers, + >( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + equality_proof_data: &CiphertextCommitmentEqualityProofData, + transfer_amount_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: Option<&Instruction>, + signing_keypairs: &S, ) -> TokenResult { - if amount >> confidential_transfer::MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH != 0 { - return Err(TokenError::MaximumDepositTransferAmountExceeded); + let mut instructions = vec![]; + + // create equality proof context state + let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.equality_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let equality_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.equality_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push( + instruction_type + .encode_verify_proof(Some(equality_proof_context_state_info), equality_proof_data), + ); + + // create transfer amount ciphertext validity proof context state + let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; + let space = + size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.transfer_amount_ciphertext_validity_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let transfer_amount_ciphertext_validity_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.transfer_amount_ciphertext_validity_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push(instruction_type.encode_verify_proof( + Some(transfer_amount_ciphertext_validity_proof_context_state_info), + transfer_amount_ciphertext_validity_proof_data, + )); + + // add transfer instruction + if let Some(transfer_instruction) = transfer_instruction { + instructions.push(transfer_instruction.clone()); } - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or_default(); + self.process_ixs(&instructions, signing_keypairs).await + } - let mint_state = self.get_mint_info().await.unwrap(); - let transfer_fee_config = mint_state - .get_extension::() - .unwrap(); - let fee_parameters = transfer_fee_config.get_epoch_fee(epoch_info.epoch); + /// Create fee sigma and fee ciphertext validity proof context state accounts for a + /// confidential transfer with fee. + #[allow(clippy::too_many_arguments)] + pub async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee< + S: Signers, + >( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + fee_sigma_proof_data: &FeeSigmaProofData, + fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + signing_keypairs: &S, + ) -> TokenResult { + self.create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( + context_state_accounts, + fee_sigma_proof_data, + fee_ciphertext_validity_proof_data, + None, + signing_keypairs, + ) + .await + } - let proof_data = confidential_transfer::instruction::TransferWithFeeData::new( - amount, - ( - source_available_balance, - source_available_balance_ciphertext, - ), - source_elgamal_keypair, - (destination_elgamal_pubkey, &auditor_elgamal_pubkey), - FeeParameters { - fee_rate_basis_points: u16::from(fee_parameters.transfer_fee_basis_points), - maximum_fee: u64::from(fee_parameters.maximum_fee), - }, - withdraw_withheld_authority_elgamal_pubkey, + /// Create fee sigma and fee ciphertext validity proof context state accounts with a confidential + /// transfer with fee. + #[allow(clippy::too_many_arguments)] + pub async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel< + S: Signers, + >( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + fee_sigma_proof_data: &FeeSigmaProofData, + fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: &Instruction, + signing_keypairs: &S, + ) -> TokenResult { + self.create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( + context_state_accounts, + fee_sigma_proof_data, + fee_ciphertext_validity_proof_data, + Some(transfer_instruction), + signing_keypairs, ) - .map_err(TokenError::Proof)?; + .await + } - let source_remaining_balance = source_available_balance - .checked_sub(amount) - .ok_or(TokenError::NotEnoughFunds)?; - let new_source_decryptable_balance = - source_authenticated_encryption_key.encrypt(source_remaining_balance); + /// Create fee sigma and fee ciphertext validity proof context states for a confidential + /// transfer with fee. + /// + /// If an optional transfer instruction is provided, then the transfer instruction is attached + /// to the same transaction. + #[allow(clippy::too_many_arguments)] + async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee< + S: Signers, + >( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + fee_sigma_proof_data: &FeeSigmaProofData, + fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, + transfer_instruction: Option<&Instruction>, + signing_keypairs: &S, + ) -> TokenResult { + let mut instructions = vec![]; - self.process_ixs( - &confidential_transfer::instruction::transfer_with_fee( - &self.program_id, - source_token_account, - destination_token_account, - &self.pubkey, - new_source_decryptable_balance, - &source_token_authority.pubkey(), - &[], - &proof_data, - )?, - &[source_token_authority], + // create fee sigma proof context state + let instruction_type = ProofInstruction::VerifyFeeSigma; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.fee_sigma_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let fee_sigma_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.fee_sigma_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push(instruction_type.encode_verify_proof( + Some(fee_sigma_proof_context_state_info), + fee_sigma_proof_data, + )); + + // create fee ciphertext validity proof context state + let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; + let space = + size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + instructions.push(system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.fee_ciphertext_validity_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + )); + + let fee_ciphertext_validity_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.fee_ciphertext_validity_proof, + context_state_authority: context_state_accounts.authority, + }; + instructions.push(instruction_type.encode_verify_proof( + Some(fee_ciphertext_validity_proof_context_state_info), + fee_ciphertext_validity_proof_data, + )); + + // add transfer instruction + if let Some(transfer_instruction) = transfer_instruction { + instructions.push(transfer_instruction.clone()); + } + + self.process_ixs(&instructions, signing_keypairs).await + } + + /// Create range proof context state account for a confidential transfer with fee. + #[allow(clippy::too_many_arguments)] + pub async fn create_range_proof_context_state_for_transfer_with_fee( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU256Data, + signing_keypairs: &S, + ) -> TokenResult { + self.create_range_proof_context_state_with_optional_transfer_with_fee( + context_state_accounts, + range_proof_data, + None, + signing_keypairs, + ) + .await + } + + /// Create range proof context state account for a confidential transfer with fee. + #[allow(clippy::too_many_arguments)] + pub async fn create_range_proof_context_state_for_transfer_with_fee_parallel( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU256Data, + transfer_instruction: &Instruction, + signing_keypairs: &S, + ) -> TokenResult { + self.create_range_proof_context_state_with_optional_transfer_with_fee( + context_state_accounts, + range_proof_data, + Some(transfer_instruction), + signing_keypairs, ) .await } - /// Applies the confidential transfer pending balance to the available balance using the - /// uniquely derived decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_apply_pending_balance( - &self, - token_account: &Pubkey, - authority: &S, - available_balance: u64, - pending_balance: u64, - expected_pending_balance_credit_counter: u64, - ) -> TokenResult { - let authenticated_encryption_key = - AeKey::new(authority, token_account).map_err(TokenError::Key)?; + /// Create a range proof context state account and an optional confidential transfer instruction. + async fn create_range_proof_context_state_with_optional_transfer_with_fee( + &self, + context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, + range_proof_data: &BatchedRangeProofU256Data, + transfer_instruction: Option<&Instruction>, + signing_keypairs: &S, + ) -> TokenResult { + let instruction_type = ProofInstruction::VerifyBatchedRangeProofU256; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + let range_proof_context_state_info = ContextStateInfo { + context_state_account: context_state_accounts.range_proof, + context_state_authority: context_state_accounts.authority, + }; + + let mut instructions = vec![ + system_instruction::create_account( + &self.payer.pubkey(), + context_state_accounts.range_proof, + rent, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type + .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data), + ]; + + if let Some(transfer_instruction) = transfer_instruction { + instructions.push(transfer_instruction.clone()); + } - self.confidential_transfer_apply_pending_balance_with_key( - token_account, - authority, - available_balance, - pending_balance, - expected_pending_balance_credit_counter, - &authenticated_encryption_key, - ) - .await + self.process_ixs(&instructions, signing_keypairs).await } - /// Applies the confidential transfer pending balance to the available balance using a custom - /// decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_apply_pending_balance_with_key( + /// Applies the confidential transfer pending balance to the available balance + pub async fn confidential_transfer_apply_pending_balance( &self, - token_account: &Pubkey, - authority: &S, - available_balance: u64, - pending_balance: u64, - expected_pending_balance_credit_counter: u64, - authenticated_encryption_key: &AeKey, + account: &Pubkey, + authority: &Pubkey, + account_info: Option, + elgamal_secret_key: &ElGamalSecretKey, + aes_key: &AeKey, + signing_keypairs: &S, ) -> TokenResult { - let new_decryptable_balance = available_balance.checked_add(pending_balance).unwrap(); - let new_decryptable_balance_ciphertext = - authenticated_encryption_key.encrypt(new_decryptable_balance); + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + + let account_info = if let Some(account_info) = account_info { + account_info + } else { + let account = self.get_account_info(account).await?; + let confidential_transfer_account = + account.get_extension::()?; + ApplyPendingBalanceAccountInfo::new(confidential_transfer_account) + }; + + let expected_pending_balance_credit_counter = account_info.pending_balance_credit_counter(); + let new_decryptable_available_balance = account_info + .new_decryptable_available_balance(elgamal_secret_key, aes_key) + .map_err(|_| TokenError::AccountDecryption)?; self.process_ixs( &[confidential_transfer::instruction::apply_pending_balance( &self.program_id, - token_account, + account, expected_pending_balance_credit_counter, - new_decryptable_balance_ciphertext, - &authority.pubkey(), - &[], + new_decryptable_available_balance, + authority, + &multisig_signers, )?], - &[authority], + signing_keypairs, ) .await } /// Enable confidential transfer `Deposit` and `Transfer` instructions for a token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_enable_confidential_credits( + pub async fn confidential_transfer_enable_confidential_credits( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[ confidential_transfer::instruction::enable_confidential_credits( &self.program_id, - token_account, - &authority.pubkey(), - &[], + account, + authority, + &multisig_signers, )?, ], - &[authority], + signing_keypairs, ) .await } /// Disable confidential transfer `Deposit` and `Transfer` instructions for a token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_disable_confidential_credits( + pub async fn confidential_transfer_disable_confidential_credits( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[ confidential_transfer::instruction::disable_confidential_credits( &self.program_id, - token_account, - &authority.pubkey(), - &[], + account, + authority, + &multisig_signers, )?, ], - &[authority], + signing_keypairs, ) .await } /// Enable a confidential extension token account to receive non-confidential payments - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_enable_non_confidential_credits( + pub async fn confidential_transfer_enable_non_confidential_credits( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[ confidential_transfer::instruction::enable_non_confidential_credits( &self.program_id, - token_account, - &authority.pubkey(), - &[], + account, + authority, + &multisig_signers, )?, ], - &[authority], + signing_keypairs, ) .await } /// Disable non-confidential payments for a confidential extension token account - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_disable_non_confidential_credits( + pub async fn confidential_transfer_disable_non_confidential_credits( &self, - token_account: &Pubkey, - authority: &S, + account: &Pubkey, + authority: &Pubkey, + signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + self.process_ixs( &[ confidential_transfer::instruction::disable_non_confidential_credits( &self.program_id, - token_account, - &authority.pubkey(), - &[], + account, + authority, + &multisig_signers, )?, ], - &[authority], + signing_keypairs, ) .await } - /// Withdraw withheld confidential tokens from mint using the uniquely derived decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw_withheld_tokens_from_mint( + /// Withdraw withheld confidential tokens from mint + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_withdraw_withheld_tokens_from_mint( &self, - withdraw_withheld_authority: &S, - destination_token_account: &Pubkey, + destination_account: &Pubkey, + withdraw_withheld_authority: &Pubkey, + context_state_account: Option<&Pubkey>, + withheld_tokens_info: Option, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, destination_elgamal_pubkey: &ElGamalPubkey, - withheld_amount: u64, - withheld_amount_ciphertext: &ElGamalCiphertext, + new_decryptable_available_balance: &DecryptableBalance, + signing_keypairs: &S, ) -> TokenResult { - // derive withheld authority elgamal key - let withdraw_withheld_authority_elgamal_keypair = - ElGamalKeypair::new(withdraw_withheld_authority, &self.pubkey) - .map_err(TokenError::Key)?; + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = + self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys); - self.confidential_transfer_withdraw_withheld_tokens_from_mint_with_key( - withdraw_withheld_authority, - destination_token_account, - destination_elgamal_pubkey, - withheld_amount, - withheld_amount_ciphertext, - &withdraw_withheld_authority_elgamal_keypair, - ) - .await - } + let account_info = if let Some(account_info) = withheld_tokens_info { + account_info + } else { + let mint_info = self.get_mint_info().await?; + let confidential_transfer_fee_config = + mint_info.get_extension::()?; + WithheldTokensInfo::new(&confidential_transfer_fee_config.withheld_amount) + }; - /// Withdraw withheld confidential tokens from mint using a custom decryption key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw_withheld_tokens_from_mint_with_key( - &self, - withdraw_withheld_authority: &S, - destination_token_account: &Pubkey, - destination_elgamal_pubkey: &ElGamalPubkey, - withheld_amount: u64, - withheld_amount_ciphertext: &ElGamalCiphertext, - withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, - ) -> TokenResult { - let proof_data = confidential_transfer::instruction::WithdrawWithheldTokensData::new( - withdraw_withheld_authority_elgamal_keypair, - destination_elgamal_pubkey, - withheld_amount_ciphertext, - withheld_amount, - ) - .map_err(TokenError::Proof)?; + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; self.process_ixs( - &confidential_transfer::instruction::withdraw_withheld_tokens_from_mint( + &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_mint( &self.program_id, &self.pubkey, - destination_token_account, - &withdraw_withheld_authority.pubkey(), - &[], - &proof_data, + destination_account, + new_decryptable_available_balance, + withdraw_withheld_authority, + &multisig_signers, + proof_location, )?, - &[withdraw_withheld_authority], + signing_keypairs, ) .await } - /// Withdraw withheld confidential tokens from accounts using the uniquely derived decryption - /// key - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw_withheld_tokens_from_accounts( + /// Withdraw withheld confidential tokens from accounts + #[allow(clippy::too_many_arguments)] + pub async fn confidential_transfer_withdraw_withheld_tokens_from_accounts( &self, - withdraw_withheld_authority: &S, - destination_token_account: &Pubkey, + destination_account: &Pubkey, + withdraw_withheld_authority: &Pubkey, + context_state_account: Option<&Pubkey>, + withheld_tokens_info: Option, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, destination_elgamal_pubkey: &ElGamalPubkey, - aggregate_withheld_amount: u64, - aggregate_withheld_amount_ciphertext: &ElGamalCiphertext, + new_decryptable_available_balance: &DecryptableBalance, sources: &[&Pubkey], + signing_keypairs: &S, ) -> TokenResult { - let withdraw_withheld_authority_elgamal_keypair = - ElGamalKeypair::new(withdraw_withheld_authority, &self.pubkey) - .map_err(TokenError::Key)?; + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = + self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys); - self.confidential_transfer_withdraw_withheld_tokens_from_accounts_with_key( - withdraw_withheld_authority, - destination_token_account, - destination_elgamal_pubkey, - aggregate_withheld_amount, - aggregate_withheld_amount_ciphertext, - &withdraw_withheld_authority_elgamal_keypair, - sources, - ) - .await - } + let account_info = if let Some(account_info) = withheld_tokens_info { + account_info + } else { + let futures = sources.iter().map(|source| self.get_account_info(source)); + let sources_extensions = join_all(futures).await; + + let mut aggregate_withheld_amount = ElGamalCiphertext::default(); + for source_extension in sources_extensions { + let withheld_amount: ElGamalCiphertext = source_extension? + .get_extension::()? + .withheld_amount + .try_into() + .map_err(|_| TokenError::AccountDecryption)?; + aggregate_withheld_amount = aggregate_withheld_amount + withheld_amount; + } - /// Withdraw withheld confidential tokens from accounts using a custom decryption key - #[allow(clippy::too_many_arguments)] - #[cfg(feature = "proof-program")] - pub async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_key< - S: Signer, - >( - &self, - withdraw_withheld_authority: &S, - destination_token_account: &Pubkey, - destination_elgamal_pubkey: &ElGamalPubkey, - aggregate_withheld_amount: u64, - aggregate_withheld_amount_ciphertext: &ElGamalCiphertext, - withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, - sources: &[&Pubkey], - ) -> TokenResult { - let proof_data = confidential_transfer::instruction::WithdrawWithheldTokensData::new( - withdraw_withheld_authority_elgamal_keypair, - destination_elgamal_pubkey, - aggregate_withheld_amount_ciphertext, - aggregate_withheld_amount, - ) - .map_err(TokenError::Proof)?; + WithheldTokensInfo::new(&aggregate_withheld_amount.into()) + }; + + let proof_data = if context_state_account.is_some() { + None + } else { + Some( + account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration)?, + ) + }; + + let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { + ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) + } else { + let context_state_account = context_state_account.unwrap(); + ProofLocation::ContextStateAccount(context_state_account) + }; self.process_ixs( - &confidential_transfer::instruction::withdraw_withheld_tokens_from_accounts( + &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_accounts( &self.program_id, &self.pubkey, - destination_token_account, - &withdraw_withheld_authority.pubkey(), - &[], + destination_account, + new_decryptable_available_balance, + withdraw_withheld_authority, + &multisig_signers, sources, - &proof_data, + proof_location, )?, - &[withdraw_withheld_authority], + signing_keypairs, ) .await } /// Harvest withheld confidential tokens to mint - #[cfg(feature = "proof-program")] pub async fn confidential_transfer_harvest_withheld_tokens_to_mint( &self, sources: &[&Pubkey], ) -> TokenResult { self.process_ixs::<[&dyn Signer; 0]>( &[ - confidential_transfer::instruction::harvest_withheld_tokens_to_mint( + confidential_transfer_fee::instruction::harvest_withheld_tokens_to_mint( &self.program_id, &self.pubkey, sources, @@ -2524,6 +3354,54 @@ where .await } + /// Enable harvest of confidential fees to mint + pub async fn confidential_transfer_enable_harvest_to_mint( + &self, + withdraw_withheld_authority: &Pubkey, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = + self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys); + + self.process_ixs( + &[ + confidential_transfer_fee::instruction::enable_harvest_to_mint( + &self.program_id, + &self.pubkey, + withdraw_withheld_authority, + &multisig_signers, + )?, + ], + signing_keypairs, + ) + .await + } + + /// Disable harvest of confidential fees to mint + pub async fn confidential_transfer_disable_harvest_to_mint( + &self, + withdraw_withheld_authority: &Pubkey, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = + self.get_multisig_signers(withdraw_withheld_authority, &signing_pubkeys); + + self.process_ixs( + &[ + confidential_transfer_fee::instruction::disable_harvest_to_mint( + &self.program_id, + &self.pubkey, + withdraw_withheld_authority, + &multisig_signers, + )?, + ], + signing_keypairs, + ) + .await + } + pub async fn withdraw_excess_lamports( &self, source: &Pubkey, @@ -2546,4 +3424,204 @@ where ) .await } + + /// Initialize token-metadata on a mint + pub async fn token_metadata_initialize( + &self, + update_authority: &Pubkey, + mint_authority: &Pubkey, + name: String, + symbol: String, + uri: String, + signing_keypairs: &S, + ) -> TokenResult { + self.process_ixs( + &[spl_token_metadata_interface::instruction::initialize( + &self.program_id, + &self.pubkey, + update_authority, + &self.pubkey, + mint_authority, + name, + symbol, + uri, + )], + signing_keypairs, + ) + .await + } + + async fn get_additional_rent_for_new_metadata( + &self, + token_metadata: &TokenMetadata, + ) -> TokenResult { + let account = self.get_account(self.pubkey).await?; + let account_lamports = account.lamports; + let mint_state = self.unpack_mint_info(account)?; + let new_account_len = mint_state.try_get_new_account_len(token_metadata)?; + let new_rent_exempt_minimum = self + .client + .get_minimum_balance_for_rent_exemption(new_account_len) + .await + .map_err(TokenError::Client)?; + Ok(new_rent_exempt_minimum.saturating_sub(account_lamports)) + } + + /// Initialize token-metadata on a mint + #[allow(clippy::too_many_arguments)] + pub async fn token_metadata_initialize_with_rent_transfer( + &self, + payer: &Pubkey, + update_authority: &Pubkey, + mint_authority: &Pubkey, + name: String, + symbol: String, + uri: String, + signing_keypairs: &S, + ) -> TokenResult { + let token_metadata = TokenMetadata { + name, + symbol, + uri, + ..Default::default() + }; + let additional_lamports = self + .get_additional_rent_for_new_metadata(&token_metadata) + .await?; + let mut instructions = vec![]; + if additional_lamports > 0 { + instructions.push(system_instruction::transfer( + payer, + &self.pubkey, + additional_lamports, + )); + } + instructions.push(spl_token_metadata_interface::instruction::initialize( + &self.program_id, + &self.pubkey, + update_authority, + &self.pubkey, + mint_authority, + token_metadata.name, + token_metadata.symbol, + token_metadata.uri, + )); + self.process_ixs(&instructions, signing_keypairs).await + } + + /// Update a token-metadata field on a mint + pub async fn token_metadata_update_field( + &self, + update_authority: &Pubkey, + field: Field, + value: String, + signing_keypairs: &S, + ) -> TokenResult { + self.process_ixs( + &[spl_token_metadata_interface::instruction::update_field( + &self.program_id, + &self.pubkey, + update_authority, + field, + value, + )], + signing_keypairs, + ) + .await + } + + async fn get_additional_rent_for_updated_metadata( + &self, + field: Field, + value: String, + ) -> TokenResult { + let account = self.get_account(self.pubkey).await?; + let account_lamports = account.lamports; + let mint_state = self.unpack_mint_info(account)?; + let mut token_metadata = mint_state.get_variable_len_extension::()?; + token_metadata.update(field, value); + let new_account_len = mint_state.try_get_new_account_len(&token_metadata)?; + let new_rent_exempt_minimum = self + .client + .get_minimum_balance_for_rent_exemption(new_account_len) + .await + .map_err(TokenError::Client)?; + Ok(new_rent_exempt_minimum.saturating_sub(account_lamports)) + } + + /// Update a token-metadata field on a mint. Includes a transfer for any + /// additional rent-exempt SOL required. + #[allow(clippy::too_many_arguments)] + pub async fn token_metadata_update_field_with_rent_transfer( + &self, + payer: &Pubkey, + update_authority: &Pubkey, + field: Field, + value: String, + transfer_lamports: Option, + signing_keypairs: &S, + ) -> TokenResult { + let additional_lamports = if let Some(transfer_lamports) = transfer_lamports { + transfer_lamports + } else { + self.get_additional_rent_for_updated_metadata(field.clone(), value.clone()) + .await? + }; + let mut instructions = vec![]; + if additional_lamports > 0 { + instructions.push(system_instruction::transfer( + payer, + &self.pubkey, + additional_lamports, + )); + } + instructions.push(spl_token_metadata_interface::instruction::update_field( + &self.program_id, + &self.pubkey, + update_authority, + field, + value, + )); + self.process_ixs(&instructions, signing_keypairs).await + } + + /// Update the token-metadata authority in a mint + pub async fn token_metadata_update_authority( + &self, + current_authority: &Pubkey, + new_authority: Option, + signing_keypairs: &S, + ) -> TokenResult { + self.process_ixs( + &[spl_token_metadata_interface::instruction::update_authority( + &self.program_id, + &self.pubkey, + current_authority, + new_authority.try_into()?, + )], + signing_keypairs, + ) + .await + } + + /// Remove a token-metadata field on a mint + pub async fn token_metadata_remove_key( + &self, + update_authority: &Pubkey, + key: String, + idempotent: bool, + signing_keypairs: &S, + ) -> TokenResult { + self.process_ixs( + &[spl_token_metadata_interface::instruction::remove_key( + &self.program_id, + &self.pubkey, + update_authority, + key, + idempotent, + )], + signing_keypairs, + ) + .await + } } diff --git a/token/js/examples/transferHook.ts b/token/js/examples/transferHook.ts new file mode 100644 index 00000000000..fb7e5944d74 --- /dev/null +++ b/token/js/examples/transferHook.ts @@ -0,0 +1,98 @@ +import { + clusterApiUrl, + sendAndConfirmTransaction, + Connection, + Keypair, + PublicKey, + SystemProgram, + Transaction, + LAMPORTS_PER_SOL, +} from '@solana/web3.js'; + +import { + ExtensionType, + createInitializeMintInstruction, + createInitializeTransferHookInstruction, + getMintLen, + TOKEN_2022_PROGRAM_ID, + updateTransferHook, + transferCheckedWithHook, + getAssociatedTokenAddressSync, + ASSOCIATED_TOKEN_PROGRAM_ID, +} from '../src'; + +(async () => { + const payer = Keypair.generate(); + + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const mint = mintKeypair.publicKey; + + const sender = Keypair.generate(); + const recipient = Keypair.generate(); + + const extensions = [ExtensionType.TransferHook]; + const mintLen = getMintLen(extensions); + const decimals = 9; + const transferHookPogramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj'); + const newTransferHookProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj'); + + const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); + + const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL); + await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) }); + + const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + space: mintLen, + lamports: mintLamports, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookPogramId, TOKEN_2022_PROGRAM_ID), + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + ); + await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); + + await updateTransferHook( + connection, + payer, + mint, + newTransferHookProgramId, + payer.publicKey, + [], + undefined, + TOKEN_2022_PROGRAM_ID + ); + + const senderAta = getAssociatedTokenAddressSync( + mint, + sender.publicKey, + false, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const recipientAta = getAssociatedTokenAddressSync( + mint, + recipient.publicKey, + false, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + await transferCheckedWithHook( + connection, + payer, + senderAta, + mint, + recipientAta, + sender, + BigInt(1000000000), + 9, + [], + undefined, + TOKEN_2022_PROGRAM_ID + ); +})(); diff --git a/token/js/package-lock.json b/token/js/package-lock.json index 84109295b82..48073cd8562 100644 --- a/token/js/package-lock.json +++ b/token/js/package-lock.json @@ -21,24 +21,24 @@ "@types/mocha": "^10.0.0", "@types/node": "^20.1.1", "@types/node-fetch": "^2.6.2", - "@types/prettier": "^2.7.0", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", "eslint": "^8.20.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-require-extensions": "^0.1.1", - "gh-pages": "^5.0.0", + "gh-pages": "^6.0.0", "mocha": "^10.1.0", - "prettier": "^2.7.1", + "prettier": "^3.0.0", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.0", "ts-node": "^10.9.1", "tslib": "^2.3.1", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" }, "engines": { @@ -58,11 +58,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -104,18 +104,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -136,9 +136,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -158,12 +158,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -185,9 +185,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@jridgewell/resolve-uri": { @@ -204,29 +204,26 @@ "license": "MIT" }, "node_modules/@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dependencies": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -260,6 +257,26 @@ "node": ">= 8" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@sideway/address": { "version": "4.1.4", "dev": true, @@ -319,23 +336,23 @@ } }, "node_modules/@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "dependencies": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -361,15 +378,16 @@ "license": "MIT" }, "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "node_modules/@types/chai-as-promised": { - "version": "7.1.5", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.7.tgz", + "integrity": "sha512-APucaP5rlmTRYKtRA6FE5QPP87x76ejw5t5guRJ4y5OgMnwtsvigw7HHhKZlx2MGXLeZd6R/GNZR/IqDHcbtQw==", "dev": true, - "license": "MIT", "dependencies": { "@types/chai": "*" } @@ -383,42 +401,49 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "node_modules/@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "version": "20.8.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==", "dev": true, "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" } }, "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "deprecated": "This is a stub types definition. prettier provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "prettier": "*" + } }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/ws": { @@ -430,32 +455,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -464,25 +490,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -491,16 +518,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -508,25 +535,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -535,12 +562,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -548,21 +575,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -575,48 +602,53 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -778,20 +810,6 @@ "form-data": "^4.0.0" } }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, @@ -822,6 +840,15 @@ ], "license": "MIT" }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/bigint-buffer": { "version": "1.1.5", "hasInstallScript": true, @@ -873,6 +900,18 @@ "text-encoding-utf-8": "^1.0.2" } }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "dev": true, @@ -939,6 +978,21 @@ "node": ">=6.14.2" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -960,18 +1014,18 @@ } }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" }, "engines": { "node": ">=4" @@ -1004,9 +1058,13 @@ } }, "node_modules/check-error": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, - "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, "engines": { "node": "*" } @@ -1169,6 +1227,52 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -1275,27 +1379,28 @@ } }, "node_modules/eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1305,7 +1410,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -1317,7 +1421,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -1331,9 +1434,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -1343,20 +1446,29 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, - "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } @@ -1375,23 +1487,14 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1399,15 +1502,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1415,19 +1514,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -1453,19 +1543,11 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -1473,18 +1555,10 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -1517,6 +1591,77 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -1537,9 +1682,10 @@ "license": "Apache-2.0" }, "node_modules/fast-glob": { - "version": "3.2.11", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -1713,9 +1859,10 @@ } }, "node_modules/form-data": { - "version": "3.0.1", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -1731,16 +1878,17 @@ "license": "MIT" }, "node_modules/fs-extra": { - "version": "8.1.0", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -1774,25 +1922,38 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", "dev": true, "dependencies": { "async": "^3.2.4", - "commander": "^2.18.0", + "commander": "^11.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", + "fs-extra": "^11.1.1", "globby": "^6.1.0" }, "bin": { @@ -1814,6 +1975,15 @@ "node": ">=0.10.0" } }, + "node_modules/gh-pages/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/gh-pages/node_modules/globby": { "version": "6.1.0", "dev": true, @@ -1868,9 +2038,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1902,14 +2072,10 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "dev": true, - "license": "ISC" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "dev": true, - "license": "MIT" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -1979,9 +2145,10 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.2.0", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -2054,6 +2221,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "dev": true, @@ -2073,6 +2255,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -2098,6 +2298,18 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "dev": true, @@ -2109,6 +2321,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "dev": true, @@ -2199,9 +2438,13 @@ "dev": true }, "node_modules/jsonfile": { - "version": "4.0.0", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -2290,9 +2533,10 @@ } }, "node_modules/loupe": { - "version": "2.3.4", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "dev": true, - "license": "MIT", "dependencies": { "get-func-name": "^2.0.0" } @@ -2529,16 +2773,10 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2572,6 +2810,33 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "dev": true, @@ -2602,6 +2867,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -2723,6 +3006,12 @@ "through": "~2.3" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "dev": true, @@ -2822,15 +3111,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -2927,9 +3216,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/require-directory": { "version": "2.1.1", @@ -3026,6 +3315,68 @@ } } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "dev": true, @@ -3076,9 +3427,10 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.3.7", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -3184,9 +3536,9 @@ } }, "node_modules/start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "dependencies": { "arg": "^5.0.2", @@ -3204,7 +3556,7 @@ "start-test": "src/bin/start.js" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/start-server-and-test/node_modules/arg": { @@ -3235,17 +3587,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/start-server-and-test/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/start-server-and-test/node_modules/is-stream": { "version": "2.0.1", "dev": true, @@ -3372,6 +3713,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/text-encoding-utf-8": { "version": "1.0.2" }, @@ -3384,6 +3741,18 @@ "version": "2.3.8", "license": "MIT" }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "dev": true, @@ -3419,6 +3788,18 @@ "node": ">=0.8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.1", "dev": true, @@ -3469,30 +3850,11 @@ "node": ">=0.3.1" } }, - "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "node_modules/type-check": { "version": "0.4.0", @@ -3528,24 +3890,24 @@ } }, "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.3.tgz", + "integrity": "sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -3558,9 +3920,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3573,9 +3935,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3585,12 +3947,27 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/universalify": { - "version": "0.1.2", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/uri-js": { @@ -3812,11 +4189,11 @@ "dev": true }, "@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@cspotcode/source-map-support": { @@ -3846,15 +4223,15 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -3869,9 +4246,9 @@ } }, "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true }, "@hapi/hoek": { @@ -3886,12 +4263,12 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" } @@ -3903,9 +4280,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@jridgewell/resolve-uri": { @@ -3917,17 +4294,17 @@ "dev": true }, "@noble/curves": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", - "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "requires": { - "@noble/hashes": "1.3.0" + "@noble/hashes": "1.3.2" } }, "@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -3949,6 +4326,20 @@ "fastq": "^1.6.0" } }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, "@sideway/address": { "version": "4.1.4", "dev": true, @@ -3993,23 +4384,23 @@ } }, "@solana/web3.js": { - "version": "1.77.3", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz", - "integrity": "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==", + "version": "1.87.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz", + "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==", "requires": { - "@babel/runtime": "^7.12.5", - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.3.0", + "@babel/runtime": "^7.23.2", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.1", "@solana/buffer-layout": "^4.0.0", - "agentkeepalive": "^4.2.1", + "agentkeepalive": "^4.3.0", "bigint-buffer": "^1.1.5", - "bn.js": "^5.0.0", + "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.12", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } @@ -4031,13 +4422,15 @@ "dev": true }, "@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "@types/chai-as-promised": { - "version": "7.1.5", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.7.tgz", + "integrity": "sha512-APucaP5rlmTRYKtRA6FE5QPP87x76ejw5t5guRJ4y5OgMnwtsvigw7HHhKZlx2MGXLeZd6R/GNZR/IqDHcbtQw==", "dev": true, "requires": { "@types/chai": "*" @@ -4052,42 +4445,48 @@ } }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "version": "20.8.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", + "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "requires": { + "undici-types": "~5.26.4" + } }, "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==", "dev": true, "requires": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.0" } }, "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA==", + "dev": true, + "requires": { + "prettier": "*" + } }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "@types/ws": { @@ -4099,104 +4498,111 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" } }, "@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.9.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -4303,19 +4709,6 @@ "requires": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } } }, "balanced-match": { @@ -4331,6 +4724,12 @@ "base64-js": { "version": "1.5.1" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, "bigint-buffer": { "version": "1.1.5", "requires": { @@ -4365,6 +4764,15 @@ "text-encoding-utf-8": "^1.0.2" } }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "dev": true, @@ -4404,6 +4812,15 @@ "node-gyp-build": "^4.3.0" } }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4415,18 +4832,18 @@ "dev": true }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" } }, "chai-as-promised": { @@ -4445,8 +4862,13 @@ } }, "check-error": { - "version": "1.0.2", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } }, "check-more-types": { "version": "2.24.0", @@ -4551,6 +4973,34 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -4619,27 +5069,28 @@ "dev": true }, "eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4649,7 +5100,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -4661,40 +5111,24 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "requires": {} }, "eslint-plugin-prettier": { - "version": "4.2.1", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" } }, "eslint-plugin-require-extensions": { @@ -4705,25 +5139,25 @@ "requires": {} }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { "acorn": "^8.9.0", @@ -4738,33 +5172,21 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -4789,6 +5211,52 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, "eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -4805,7 +5273,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -4916,7 +5386,9 @@ "dev": true }, "form-data": { - "version": "3.0.1", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -4929,12 +5401,14 @@ "dev": true }, "fs-extra": { - "version": "8.1.0", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dev": true, "requires": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, "fs.realpath": { @@ -4955,21 +5429,29 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "gh-pages": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", - "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.0.0.tgz", + "integrity": "sha512-FXZWJRsvP/fK2HJGY+Di6FRNHvqFF6gOIELaopDjXXgjeOYSNURcuYwEO/6bwuq6koP5Lnkvnr5GViXzuOB89g==", "dev": true, "requires": { "async": "^3.2.4", - "commander": "^2.18.0", + "commander": "^11.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", + "fs-extra": "^11.1.1", "globby": "^6.1.0" }, "dependencies": { @@ -4980,6 +5462,12 @@ "array-uniq": "^1.0.1" } }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, "globby": { "version": "6.1.0", "dev": true, @@ -5017,9 +5505,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -5038,11 +5526,9 @@ } }, "graceful-fs": { - "version": "4.2.10", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "graphemer": { @@ -5082,7 +5568,9 @@ "version": "1.2.1" }, "ignore": { - "version": "5.2.0", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -5129,6 +5617,12 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "dev": true @@ -5140,6 +5634,15 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, "is-number": { "version": "7.0.0", "dev": true @@ -5154,10 +5657,33 @@ "version": "2.1.0", "dev": true }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, "is-unicode-supported": { "version": "0.1.0", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, "isexe": { "version": "2.0.0", "dev": true @@ -5236,10 +5762,13 @@ "dev": true }, "jsonfile": { - "version": "4.0.0", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" } }, "jsonparse": { @@ -5294,7 +5823,9 @@ } }, "loupe": { - "version": "2.3.4", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "dev": true, "requires": { "get-func-name": "^2.0.0" @@ -5455,16 +5986,10 @@ "version": "1.4.0", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "requires": { "whatwg-url": "^5.0.0" } @@ -5477,6 +6002,23 @@ "version": "3.0.0", "dev": true }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "object-assign": { "version": "4.1.1", "dev": true @@ -5495,6 +6037,18 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5567,6 +6121,12 @@ "through": "~2.3" } }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.1", "dev": true @@ -5627,9 +6187,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "prettier-linter-helpers": { @@ -5682,9 +6242,9 @@ } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "require-directory": { "version": "2.1.1", @@ -5737,6 +6297,49 @@ } } }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + } + } + }, "run-parallel": { "version": "1.2.0", "dev": true, @@ -5757,7 +6360,9 @@ "version": "5.2.1" }, "semver": { - "version": "7.3.7", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5826,9 +6431,9 @@ } }, "start-server-and-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", - "integrity": "sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", + "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", "dev": true, "requires": { "arg": "^5.0.2", @@ -5862,10 +6467,6 @@ "strip-final-newline": "^2.0.0" } }, - "get-stream": { - "version": "6.0.1", - "dev": true - }, "is-stream": { "version": "2.0.1", "dev": true @@ -5943,6 +6544,16 @@ "version": "1.0.0", "dev": true }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "text-encoding-utf-8": { "version": "1.0.2" }, @@ -5953,6 +6564,12 @@ "through": { "version": "2.3.8" }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "dev": true, @@ -5978,6 +6595,13 @@ } } }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, "ts-node": { "version": "10.9.1", "dev": true, @@ -6004,24 +6628,11 @@ } }, "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "dev": true - } - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6044,14 +6655,14 @@ "dev": true }, "typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.3.tgz", + "integrity": "sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw==", "dev": true, "requires": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "dependencies": { @@ -6065,9 +6676,9 @@ } }, "minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -6076,13 +6687,26 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "universalify": { - "version": "0.1.2", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, "uri-js": { diff --git a/token/js/package.json b/token/js/package.json index e250bcdc520..181b6304ec8 100644 --- a/token/js/package.json +++ b/token/js/package.json @@ -41,9 +41,9 @@ "example": "node --experimental-specifier-resolution=node --loader ts-node/esm examples/createMintAndTransferTokens.ts", "test": "npm run test:unit && npm run test:e2e-built && npm run test:e2e-native && npm run test:e2e-2022", "test:unit": "mocha test/unit", - "test:e2e-built": "start-server-and-test 'solana-test-validator --bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA ../../target/deploy/spl_token.so --reset --quiet' http://localhost:8899/health 'mocha test/e2e'", - "test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --reset --quiet' http://localhost:8899/health 'mocha test/e2e*'", - "test:e2e-native": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health 'mocha test/e2e'", + "test:e2e-built": "start-server-and-test 'solana-test-validator --bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA ../../target/deploy/spl_token.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'", + "test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --bpf-program TokenHookExampLe8smaVNrxTBezWTRbEwxwb1Zykrb ../../target/deploy/spl_transfer_hook_example.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e*'", + "test:e2e-native": "start-server-and-test 'solana-test-validator --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'", "test:build-programs": "cargo build-sbf --manifest-path ../program/Cargo.toml && cargo build-sbf --manifest-path ../program-2022/Cargo.toml && cargo build-sbf --manifest-path ../../associated-token-account/program/Cargo.toml", "deploy": "npm run deploy:docs", "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", @@ -65,24 +65,24 @@ "@types/mocha": "^10.0.0", "@types/node": "^20.1.1", "@types/node-fetch": "^2.6.2", - "@types/prettier": "^2.7.0", - "@typescript-eslint/eslint-plugin": "^5.34.0", - "@typescript-eslint/parser": "^5.34.0", + "@types/prettier": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", "eslint": "^8.20.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-require-extensions": "^0.1.1", - "gh-pages": "^5.0.0", + "gh-pages": "^6.0.0", "mocha": "^10.1.0", - "prettier": "^2.7.1", + "prettier": "^3.0.0", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.0", "tslib": "^2.3.1", "ts-node": "^10.9.1", - "typedoc": "^0.24.7", + "typedoc": "^0.25.0", "typescript": "^5.0.4" } } diff --git a/token/js/src/errors.ts b/token/js/src/errors.ts index 5e633d35202..7760b436fde 100644 --- a/token/js/src/errors.ts +++ b/token/js/src/errors.ts @@ -15,6 +15,11 @@ export class TokenInvalidAccountError extends TokenError { name = 'TokenInvalidAccountError'; } +/** Thrown if a program state account does not contain valid data */ +export class TokenInvalidAccountDataError extends TokenError { + name = 'TokenInvalidAccountDataError'; +} + /** Thrown if a program state account is not owned by the expected token program */ export class TokenInvalidAccountOwnerError extends TokenError { name = 'TokenInvalidAccountOwnerError'; @@ -64,3 +69,13 @@ export class TokenInvalidInstructionTypeError extends TokenError { export class TokenUnsupportedInstructionError extends TokenError { name = 'TokenUnsupportedInstructionError'; } + +/** Thrown if the transfer hook extra accounts contains an invalid account index */ +export class TokenTransferHookAccountNotFound extends TokenError { + name = 'TokenTransferHookAccountNotFound'; +} + +/** Thrown if the transfer hook extra accounts contains an invalid seed */ +export class TokenTransferHookInvalidSeed extends TokenError { + name = 'TokenTransferHookInvalidSeed'; +} diff --git a/token/js/src/extensions/extensionType.ts b/token/js/src/extensions/extensionType.ts index fabfb8f7499..75172abec48 100644 --- a/token/js/src/extensions/extensionType.ts +++ b/token/js/src/extensions/extensionType.ts @@ -12,6 +12,7 @@ import { MINT_CLOSE_AUTHORITY_SIZE } from './mintCloseAuthority.js'; import { NON_TRANSFERABLE_SIZE, NON_TRANSFERABLE_ACCOUNT_SIZE } from './nonTransferable.js'; import { PERMANENT_DELEGATE_SIZE } from './permanentDelegate.js'; import { TRANSFER_FEE_AMOUNT_SIZE, TRANSFER_FEE_CONFIG_SIZE } from './transferFee/index.js'; +import { TRANSFER_HOOK_ACCOUNT_SIZE, TRANSFER_HOOK_SIZE } from './transferHook/index.js'; export enum ExtensionType { Uninitialized, @@ -28,6 +29,8 @@ export enum ExtensionType { CpiGuard, PermanentDelegate, NonTransferableAccount, + TransferHook, + TransferHookAccount, } export const TYPE_SIZE = 2; @@ -65,6 +68,10 @@ export function getTypeLen(e: ExtensionType): number { return PERMANENT_DELEGATE_SIZE; case ExtensionType.NonTransferableAccount: return NON_TRANSFERABLE_ACCOUNT_SIZE; + case ExtensionType.TransferHook: + return TRANSFER_HOOK_SIZE; + case ExtensionType.TransferHookAccount: + return TRANSFER_HOOK_ACCOUNT_SIZE; default: throw Error(`Unknown extension type: ${e}`); } @@ -79,6 +86,7 @@ export function isMintExtension(e: ExtensionType): boolean { case ExtensionType.NonTransferable: case ExtensionType.InterestBearingConfig: case ExtensionType.PermanentDelegate: + case ExtensionType.TransferHook: return true; case ExtensionType.Uninitialized: case ExtensionType.TransferFeeAmount: @@ -87,6 +95,7 @@ export function isMintExtension(e: ExtensionType): boolean { case ExtensionType.MemoTransfer: case ExtensionType.CpiGuard: case ExtensionType.NonTransferableAccount: + case ExtensionType.TransferHookAccount: return false; default: throw Error(`Unknown extension type: ${e}`); @@ -101,6 +110,7 @@ export function isAccountExtension(e: ExtensionType): boolean { case ExtensionType.MemoTransfer: case ExtensionType.CpiGuard: case ExtensionType.NonTransferableAccount: + case ExtensionType.TransferHookAccount: return true; case ExtensionType.Uninitialized: case ExtensionType.TransferFeeConfig: @@ -110,6 +120,7 @@ export function isAccountExtension(e: ExtensionType): boolean { case ExtensionType.NonTransferable: case ExtensionType.InterestBearingConfig: case ExtensionType.PermanentDelegate: + case ExtensionType.TransferHook: return false; default: throw Error(`Unknown extension type: ${e}`); @@ -124,6 +135,8 @@ export function getAccountTypeOfMintType(e: ExtensionType): ExtensionType { return ExtensionType.ConfidentialTransferAccount; case ExtensionType.NonTransferable: return ExtensionType.NonTransferableAccount; + case ExtensionType.TransferHook: + return ExtensionType.TransferHookAccount; case ExtensionType.TransferFeeAmount: case ExtensionType.ConfidentialTransferAccount: case ExtensionType.CpiGuard: @@ -135,6 +148,7 @@ export function getAccountTypeOfMintType(e: ExtensionType): ExtensionType { case ExtensionType.InterestBearingConfig: case ExtensionType.PermanentDelegate: case ExtensionType.NonTransferableAccount: + case ExtensionType.TransferHookAccount: return ExtensionType.Uninitialized; } } diff --git a/token/js/src/extensions/index.ts b/token/js/src/extensions/index.ts index 05b799502f1..08aa516c5cc 100644 --- a/token/js/src/extensions/index.ts +++ b/token/js/src/extensions/index.ts @@ -9,3 +9,4 @@ export * from './mintCloseAuthority.js'; export * from './nonTransferable.js'; export * from './transferFee/index.js'; export * from './permanentDelegate.js'; +export * from './transferHook/index.js'; diff --git a/token/js/src/extensions/transferHook/actions.ts b/token/js/src/extensions/transferHook/actions.ts new file mode 100644 index 00000000000..c14b01631d7 --- /dev/null +++ b/token/js/src/extensions/transferHook/actions.ts @@ -0,0 +1,176 @@ +import type { ConfirmOptions, Connection, Signer, TransactionSignature } from '@solana/web3.js'; +import type { PublicKey } from '@solana/web3.js'; +import { sendAndConfirmTransaction, Transaction } from '@solana/web3.js'; +import { getSigners } from '../../actions/internal.js'; +import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../../constants.js'; +import { + createInitializeTransferHookInstruction, + createTransferCheckedWithFeeAndTransferHookInstruction, + createTransferCheckedWithTransferHookInstruction, + createUpdateTransferHookInstruction, +} from './instructions.js'; + +/** + * Initialize a transfer hook on a mint + * + * @param connection Connection to use + * @param payer Payer of the transaction fees + * @param mint Mint to initialize with extension + * @param authority Transfer hook authority account + * @param transferHookProgramId The transfer hook program account + * @param confirmOptions Options for confirming the transaction + * @param programId SPL Token program account + * + * @return Signature of the confirmed transaction + */ +export async function initializeTransferHook( + connection: Connection, + payer: Signer, + mint: PublicKey, + authority: PublicKey, + transferHookProgramId: PublicKey, + confirmOptions?: ConfirmOptions, + programId = TOKEN_2022_PROGRAM_ID +): Promise { + const transaction = new Transaction().add( + createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, programId) + ); + + return await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); +} + +/** + * Update the transfer hook program on a mint + * + * @param connection Connection to use + * @param payer Payer of the transaction fees + * @param mint Mint to modify + * @param transferHookProgramId New transfer hook program account + * @param authority Transfer hook update authority + * @param multiSigners Signing accounts if `freezeAuthority` is a multisig + * @param confirmOptions Options for confirming the transaction + * @param programId SPL Token program account + * + * @return Signature of the confirmed transaction + */ +export async function updateTransferHook( + connection: Connection, + payer: Signer, + mint: PublicKey, + transferHookProgramId: PublicKey, + authority: Signer | PublicKey, + multiSigners: Signer[] = [], + confirmOptions?: ConfirmOptions, + programId = TOKEN_2022_PROGRAM_ID +): Promise { + const [authorityPublicKey, signers] = getSigners(authority, multiSigners); + + const transaction = new Transaction().add( + createUpdateTransferHookInstruction(mint, authorityPublicKey, transferHookProgramId, signers, programId) + ); + + return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); +} + +/** + * Transfer tokens from one account to another, asserting the token mint, and decimals + * + * @param connection Connection to use + * @param payer Payer of the transaction fees + * @param source Source account + * @param mint Mint for the account + * @param destination Destination account + * @param authority Authority of the source account + * @param amount Number of tokens to transfer + * @param decimals Number of decimals in transfer amount + * @param multiSigners Signing accounts if `owner` is a multisig + * @param confirmOptions Options for confirming the transaction + * @param programId SPL Token program account + * + * @return Signature of the confirmed transaction + */ +export async function transferCheckedWithTransferHook( + connection: Connection, + payer: Signer, + source: PublicKey, + mint: PublicKey, + destination: PublicKey, + authority: Signer | PublicKey, + amount: bigint, + decimals: number, + multiSigners: Signer[] = [], + confirmOptions?: ConfirmOptions, + programId = TOKEN_PROGRAM_ID +): Promise { + const [authorityPublicKey, signers] = getSigners(authority, multiSigners); + + const transaction = new Transaction().add( + await createTransferCheckedWithTransferHookInstruction( + connection, + source, + mint, + destination, + authorityPublicKey, + amount, + decimals, + signers, + confirmOptions?.commitment, + programId + ) + ); + + return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); +} + +/** + * Transfer tokens from one account to another, asserting the transfer fee, token mint, and decimals + * + * @param connection Connection to use + * @param payer Payer of the transaction fees + * @param source Source account + * @param mint Mint for the account + * @param destination Destination account + * @param authority Authority of the source account + * @param amount Number of tokens to transfer + * @param decimals Number of decimals in transfer amount + * @param fee The calculated fee for the transfer fee extension + * @param multiSigners Signing accounts if `owner` is a multisig + * @param confirmOptions Options for confirming the transaction + * @param programId SPL Token program account + * + * @return Signature of the confirmed transaction + */ +export async function transferCheckedWithFeeAndTransferHook( + connection: Connection, + payer: Signer, + source: PublicKey, + mint: PublicKey, + destination: PublicKey, + authority: Signer | PublicKey, + amount: bigint, + decimals: number, + fee: bigint, + multiSigners: Signer[] = [], + confirmOptions?: ConfirmOptions, + programId = TOKEN_PROGRAM_ID +): Promise { + const [authorityPublicKey, signers] = getSigners(authority, multiSigners); + + const transaction = new Transaction().add( + await createTransferCheckedWithFeeAndTransferHookInstruction( + connection, + source, + mint, + destination, + authorityPublicKey, + amount, + decimals, + fee, + signers, + confirmOptions?.commitment, + programId + ) + ); + + return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); +} diff --git a/token/js/src/extensions/transferHook/index.ts b/token/js/src/extensions/transferHook/index.ts new file mode 100644 index 00000000000..b788b0de4ad --- /dev/null +++ b/token/js/src/extensions/transferHook/index.ts @@ -0,0 +1,4 @@ +export * from './actions.js'; +export * from './instructions.js'; +export * from './seeds.js'; +export * from './state.js'; diff --git a/token/js/src/extensions/transferHook/instructions.ts b/token/js/src/extensions/transferHook/instructions.ts new file mode 100644 index 00000000000..75194be4762 --- /dev/null +++ b/token/js/src/extensions/transferHook/instructions.ts @@ -0,0 +1,273 @@ +import { struct, u8 } from '@solana/buffer-layout'; +import type { Commitment, Connection, PublicKey, Signer } from '@solana/web3.js'; +import { TransactionInstruction } from '@solana/web3.js'; +import { programSupportsExtensions, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../../constants.js'; +import { TokenUnsupportedInstructionError } from '../../errors.js'; +import { addSigners } from '../../instructions/internal.js'; +import { TokenInstruction } from '../../instructions/types.js'; +import { publicKey } from '@solana/buffer-layout-utils'; +import { createTransferCheckedInstruction } from '../../instructions/transferChecked.js'; +import { createTransferCheckedWithFeeInstruction } from '../transferFee/instructions.js'; +import { getMint } from '../../state/mint.js'; +import { getExtraAccountMetaAddress, getExtraAccountMetas, getTransferHook, resolveExtraAccountMeta } from './state.js'; + +export enum TransferHookInstruction { + Initialize = 0, + Update = 1, +} + +/** Deserialized instruction for the initiation of an transfer hook */ +export interface InitializeTransferHookInstructionData { + instruction: TokenInstruction.TransferHookExtension; + transferHookInstruction: TransferHookInstruction.Initialize; + authority: PublicKey; + transferHookProgramId: PublicKey; +} + +/** The struct that represents the instruction data as it is read by the program */ +export const initializeTransferHookInstructionData = struct([ + u8('instruction'), + u8('transferHookInstruction'), + publicKey('authority'), + publicKey('transferHookProgramId'), +]); + +/** + * Construct an InitializeTransferHook instruction + * + * @param mint Token mint account + * @param authority Transfer hook authority account + * @param transferHookProgramId Transfer hook program account + * @param programId SPL Token program account + * + * @return Instruction to add to a transaction + */ +export function createInitializeTransferHookInstruction( + mint: PublicKey, + authority: PublicKey, + transferHookProgramId: PublicKey, + programId: PublicKey +): TransactionInstruction { + if (!programSupportsExtensions(programId)) { + throw new TokenUnsupportedInstructionError(); + } + const keys = [{ pubkey: mint, isSigner: false, isWritable: true }]; + + const data = Buffer.alloc(initializeTransferHookInstructionData.span); + initializeTransferHookInstructionData.encode( + { + instruction: TokenInstruction.TransferHookExtension, + transferHookInstruction: TransferHookInstruction.Initialize, + authority, + transferHookProgramId, + }, + data + ); + + return new TransactionInstruction({ keys, programId, data }); +} + +/** Deserialized instruction for the initiation of an transfer hook */ +export interface UpdateTransferHookInstructionData { + instruction: TokenInstruction.TransferHookExtension; + transferHookInstruction: TransferHookInstruction.Update; + transferHookProgramId: PublicKey; +} + +/** The struct that represents the instruction data as it is read by the program */ +export const updateTransferHookInstructionData = struct([ + u8('instruction'), + u8('transferHookInstruction'), + publicKey('transferHookProgramId'), +]); + +/** + * Construct an UpdateTransferHook instruction + * + * @param mint Mint to update + * @param authority The mint's transfer hook authority + * @param transferHookProgramId The new transfer hook program account + * @param signers The signer account(s) for a multisig + * @param tokenProgramId SPL Token program account + * + * @return Instruction to add to a transaction + */ +export function createUpdateTransferHookInstruction( + mint: PublicKey, + authority: PublicKey, + transferHookProgramId: PublicKey, + multiSigners: (Signer | PublicKey)[] = [], + programId = TOKEN_2022_PROGRAM_ID +): TransactionInstruction { + if (!programSupportsExtensions(programId)) { + throw new TokenUnsupportedInstructionError(); + } + + const keys = addSigners([{ pubkey: mint, isSigner: false, isWritable: true }], authority, multiSigners); + const data = Buffer.alloc(updateTransferHookInstructionData.span); + updateTransferHookInstructionData.encode( + { + instruction: TokenInstruction.TransferHookExtension, + transferHookInstruction: TransferHookInstruction.Update, + transferHookProgramId, + }, + data + ); + + return new TransactionInstruction({ keys, programId, data }); +} + +/** + * Add extra accounts needed for transfer hook to an instruction + * + * @param connection Connection to use + * @param instruction The transferChecked instruction to add accounts to + * @param commitment Commitment to use + * @param programId SPL Token program account + * + * @return Instruction to add to a transaction + */ +export async function addExtraAccountsToInstruction( + connection: Connection, + instruction: TransactionInstruction, + mint: PublicKey, + commitment?: Commitment, + programId = TOKEN_PROGRAM_ID +): Promise { + if (!programSupportsExtensions(programId)) { + throw new TokenUnsupportedInstructionError(); + } + + const mintInfo = await getMint(connection, mint, commitment, programId); + const transferHook = getTransferHook(mintInfo); + if (transferHook == null) { + return instruction; + } + + const extraAccountsAccount = getExtraAccountMetaAddress(mint, transferHook.programId); + const extraAccountsInfo = await connection.getAccountInfo(extraAccountsAccount, commitment); + if (extraAccountsInfo == null) { + return instruction; + } + + const extraAccountMetas = getExtraAccountMetas(extraAccountsInfo); + + const accountMetas = instruction.keys; + accountMetas.push({ pubkey: extraAccountsAccount, isSigner: false, isWritable: false }); + + for (const extraAccountMeta of extraAccountMetas) { + const accountMeta = resolveExtraAccountMeta( + extraAccountMeta, + accountMetas, + instruction.data, + transferHook.programId + ); + accountMetas.push(accountMeta); + } + accountMetas.push({ pubkey: transferHook.programId, isSigner: false, isWritable: false }); + + return new TransactionInstruction({ keys: accountMetas, programId, data: instruction.data }); +} + +/** + * Construct an transferChecked instruction with extra accounts for transfer hook + * + * @param connection Connection to use + * @param source Source account + * @param mint Mint to update + * @param destination Destination account + * @param authority The mint's transfer hook authority + * @param amount The amount of tokens to transfer + * @param decimals Number of decimals in transfer amount + * @param multiSigners The signer account(s) for a multisig + * @param commitment Commitment to use + * @param programId SPL Token program account + * + * @return Instruction to add to a transaction + */ +export async function createTransferCheckedWithTransferHookInstruction( + connection: Connection, + source: PublicKey, + mint: PublicKey, + destination: PublicKey, + authority: PublicKey, + amount: bigint, + decimals: number, + multiSigners: (Signer | PublicKey)[] = [], + commitment?: Commitment, + programId = TOKEN_PROGRAM_ID +) { + const rawInstruction = createTransferCheckedInstruction( + source, + mint, + destination, + authority, + amount, + decimals, + multiSigners, + programId + ); + + const hydratedInstruction = await addExtraAccountsToInstruction( + connection, + rawInstruction, + mint, + commitment, + programId + ); + + return hydratedInstruction; +} + +/** + * Construct an transferChecked instruction with extra accounts for transfer hook + * + * @param connection Connection to use + * @param source Source account + * @param mint Mint to update + * @param destination Destination account + * @param authority The mint's transfer hook authority + * @param amount The amount of tokens to transfer + * @param decimals Number of decimals in transfer amount + * @param fee The calculated fee for the transfer fee extension + * @param multiSigners The signer account(s) for a multisig + * @param commitment Commitment to use + * @param programId SPL Token program account + * + * @return Instruction to add to a transaction + */ +export async function createTransferCheckedWithFeeAndTransferHookInstruction( + connection: Connection, + source: PublicKey, + mint: PublicKey, + destination: PublicKey, + authority: PublicKey, + amount: bigint, + decimals: number, + fee: bigint, + multiSigners: (Signer | PublicKey)[] = [], + commitment?: Commitment, + programId = TOKEN_PROGRAM_ID +) { + const rawInstruction = createTransferCheckedWithFeeInstruction( + source, + mint, + destination, + authority, + amount, + decimals, + fee, + multiSigners, + programId + ); + + const hydratedInstruction = await addExtraAccountsToInstruction( + connection, + rawInstruction, + mint, + commitment, + programId + ); + + return hydratedInstruction; +} diff --git a/token/js/src/extensions/transferHook/seeds.ts b/token/js/src/extensions/transferHook/seeds.ts new file mode 100644 index 00000000000..759f5afd062 --- /dev/null +++ b/token/js/src/extensions/transferHook/seeds.ts @@ -0,0 +1,86 @@ +import type { AccountMeta } from '@solana/web3.js'; +import { TokenTransferHookInvalidSeed } from '../../errors.js'; + +interface Seed { + data: Buffer; + packedLength: number; +} + +const DISCRIMINATOR_SPAN = 1; +const LITERAL_LENGTH_SPAN = 1; +const INSTRUCTION_ARG_OFFSET_SPAN = 1; +const INSTRUCTION_ARG_LENGTH_SPAN = 1; +const ACCOUNT_KEY_INDEX_SPAN = 1; + +function unpackSeedLiteral(seeds: Uint8Array): Seed { + if (seeds.length < 1) { + throw new TokenTransferHookInvalidSeed(); + } + const [length, ...rest] = seeds; + if (rest.length < length) { + throw new TokenTransferHookInvalidSeed(); + } + return { + data: Buffer.from(rest.slice(0, length)), + packedLength: DISCRIMINATOR_SPAN + LITERAL_LENGTH_SPAN + length, + }; +} + +function unpackSeedInstructionArg(seeds: Uint8Array, instructionData: Buffer): Seed { + if (seeds.length < 2) { + throw new TokenTransferHookInvalidSeed(); + } + const [index, length] = seeds; + if (instructionData.length < length + index) { + throw new TokenTransferHookInvalidSeed(); + } + return { + data: instructionData.subarray(index, index + length), + packedLength: DISCRIMINATOR_SPAN + INSTRUCTION_ARG_OFFSET_SPAN + INSTRUCTION_ARG_LENGTH_SPAN, + }; +} + +function unpackSeedAccountKey(seeds: Uint8Array, previousMetas: AccountMeta[]): Seed { + if (seeds.length < 1) { + throw new TokenTransferHookInvalidSeed(); + } + const [index] = seeds; + if (previousMetas.length <= index) { + throw new TokenTransferHookInvalidSeed(); + } + return { + data: previousMetas[index].pubkey.toBuffer(), + packedLength: DISCRIMINATOR_SPAN + ACCOUNT_KEY_INDEX_SPAN, + }; +} + +function unpackFirstSeed(seeds: Uint8Array, previousMetas: AccountMeta[], instructionData: Buffer): Seed | null { + const [discriminator, ...rest] = seeds; + const remaining = new Uint8Array(rest); + switch (discriminator) { + case 0: + return null; + case 1: + return unpackSeedLiteral(remaining); + case 2: + return unpackSeedInstructionArg(remaining, instructionData); + case 3: + return unpackSeedAccountKey(remaining, previousMetas); + default: + throw new TokenTransferHookInvalidSeed(); + } +} + +export function unpackSeeds(seeds: Uint8Array, previousMetas: AccountMeta[], instructionData: Buffer): Buffer[] { + const unpackedSeeds: Buffer[] = []; + let i = 0; + while (i < 32) { + const seed = unpackFirstSeed(seeds.slice(i), previousMetas, instructionData); + if (seed == null) { + break; + } + unpackedSeeds.push(seed.data); + i += seed.packedLength; + } + return unpackedSeeds; +} diff --git a/token/js/src/extensions/transferHook/state.ts b/token/js/src/extensions/transferHook/state.ts new file mode 100644 index 00000000000..67f5866995b --- /dev/null +++ b/token/js/src/extensions/transferHook/state.ts @@ -0,0 +1,139 @@ +import { blob, greedy, seq, struct, u32, u8 } from '@solana/buffer-layout'; +import type { Mint } from '../../state/mint.js'; +import { ExtensionType, getExtensionData } from '../extensionType.js'; +import type { AccountInfo, AccountMeta } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; +import { bool, publicKey, u64 } from '@solana/buffer-layout-utils'; +import type { Account } from '../../state/account.js'; +import { TokenTransferHookAccountNotFound } from '../../errors.js'; +import { unpackSeeds } from './seeds.js'; + +/** TransferHook as stored by the program */ +export interface TransferHook { + /** The transfer hook update authrority */ + authority: PublicKey; + /** The transfer hook program account */ + programId: PublicKey; +} + +/** Buffer layout for de/serializing a transfer hook extension */ +export const TransferHookLayout = struct([publicKey('authority'), publicKey('programId')]); + +export const TRANSFER_HOOK_SIZE = TransferHookLayout.span; + +export function getTransferHook(mint: Mint): TransferHook | null { + const extensionData = getExtensionData(ExtensionType.TransferHook, mint.tlvData); + if (extensionData !== null) { + return TransferHookLayout.decode(extensionData); + } else { + return null; + } +} + +/** TransferHookAccount as stored by the program */ +export interface TransferHookAccount { + /** + * Whether or not this account is currently tranferring tokens + * True during the transfer hook cpi, otherwise false + */ + transferring: boolean; +} + +/** Buffer layout for de/serializing a transfer hook account extension */ +export const TransferHookAccountLayout = struct([bool('transferring')]); + +export const TRANSFER_HOOK_ACCOUNT_SIZE = TransferHookAccountLayout.span; + +export function getTransferHookAccount(account: Account): TransferHookAccount | null { + const extensionData = getExtensionData(ExtensionType.TransferHookAccount, account.tlvData); + if (extensionData !== null) { + return TransferHookAccountLayout.decode(extensionData); + } else { + return null; + } +} + +export function getExtraAccountMetaAddress(mint: PublicKey, programId: PublicKey): PublicKey { + const seeds = [Buffer.from('extra-account-metas'), mint.toBuffer()]; + return PublicKey.findProgramAddressSync(seeds, programId)[0]; +} + +/** ExtraAccountMeta as stored by the transfer hook program */ +export interface ExtraAccountMeta { + discriminator: number; + addressConfig: Uint8Array; + isSigner: boolean; + isWritable: boolean; +} + +/** Buffer layout for de/serializing an ExtraAccountMeta */ +export const ExtraAccountMetaLayout = struct([ + u8('discriminator'), + blob(32, 'addressConfig'), + bool('isSigner'), + bool('isWritable'), +]); + +export interface ExtraAccountMetaList { + count: number; + extraAccounts: ExtraAccountMeta[]; +} + +/** Buffer layout for de/serializing a list of ExtraAccountMeta prefixed by a u32 length */ +export const ExtraAccountMetaListLayout = struct([ + u32('count'), + seq(ExtraAccountMetaLayout, greedy(ExtraAccountMetaLayout.span), 'extraAccounts'), +]); + +/** Buffer layout for de/serializing a list of ExtraAccountMetaAccountData prefixed by a u32 length */ +export interface ExtraAccountMetaAccountData { + instructionDiscriminator: bigint; + length: number; + extraAccountsList: ExtraAccountMetaList; +} + +/** Buffer layout for de/serializing an ExtraAccountMetaAccountData */ +export const ExtraAccountMetaAccountDataLayout = struct([ + u64('instructionDiscriminator'), + u32('length'), + ExtraAccountMetaListLayout.replicate('extraAccountsList'), +]); + +/** Unpack an extra account metas account and parse the data into a list of ExtraAccountMetas */ +export function getExtraAccountMetas(account: AccountInfo): ExtraAccountMeta[] { + const extraAccountsList = ExtraAccountMetaAccountDataLayout.decode(account.data).extraAccountsList; + return extraAccountsList.extraAccounts.slice(0, extraAccountsList.count); +} + +/** Take an ExtraAccountMeta and construct that into an acutal AccountMeta */ +export function resolveExtraAccountMeta( + extraMeta: ExtraAccountMeta, + previousMetas: AccountMeta[], + instructionData: Buffer, + transferHookProgramId: PublicKey +): AccountMeta { + if (extraMeta.discriminator === 0) { + return { + pubkey: new PublicKey(extraMeta.addressConfig), + isSigner: extraMeta.isSigner, + isWritable: extraMeta.isWritable, + }; + } + + let programId = PublicKey.default; + + if (extraMeta.discriminator === 1) { + programId = transferHookProgramId; + } else { + const accountIndex = extraMeta.discriminator - (1 << 7); + if (previousMetas.length <= accountIndex) { + throw new TokenTransferHookAccountNotFound(); + } + programId = previousMetas[accountIndex].pubkey; + } + + const seeds = unpackSeeds(extraMeta.addressConfig, previousMetas, instructionData); + const pubkey = PublicKey.findProgramAddressSync(seeds, programId)[0]; + + return { pubkey, isSigner: extraMeta.isSigner, isWritable: extraMeta.isWritable }; +} diff --git a/token/js/src/instructions/setAuthority.ts b/token/js/src/instructions/setAuthority.ts index 71722d451fb..4301f57c4ca 100644 --- a/token/js/src/instructions/setAuthority.ts +++ b/token/js/src/instructions/setAuthority.ts @@ -18,6 +18,15 @@ export enum AuthorityType { FreezeAccount = 1, AccountOwner = 2, CloseAccount = 3, + TransferFeeConfig = 4, + WithheldWithdraw = 5, + CloseMint = 6, + InterestRate = 7, + PermanentDelegate = 8, + ConfidentialTransferMint = 9, + TransferHookProgramId = 10, + ConfidentialTransferFeeConfig = 11, + MetadataPointer = 12, } /** TODO: docs */ diff --git a/token/js/src/instructions/types.ts b/token/js/src/instructions/types.ts index eb62fe8fe69..4448cbf3469 100644 --- a/token/js/src/instructions/types.ts +++ b/token/js/src/instructions/types.ts @@ -36,4 +36,5 @@ export enum TokenInstruction { InterestBearingMintExtension = 33, CpiGuardExtension = 34, InitializePermanentDelegate = 35, + TransferHookExtension = 36, } diff --git a/token/js/test/common.ts b/token/js/test/common.ts index 670cd55d0c9..6f1e20a6fe1 100644 --- a/token/js/test/common.ts +++ b/token/js/test/common.ts @@ -10,7 +10,7 @@ export async function newAccountWithLamports(connection: Connection, lamports = } export async function getConnection(): Promise { - const url = 'http://localhost:8899'; + const url = 'http://127.0.0.1:8899'; const connection = new Connection(url, 'confirmed'); await connection.getVersion(); return connection; @@ -19,3 +19,5 @@ export async function getConnection(): Promise { export const TEST_PROGRAM_ID = process.env.TEST_PROGRAM_ID ? new PublicKey(process.env.TEST_PROGRAM_ID) : TOKEN_PROGRAM_ID; + +export const TRANSFER_HOOK_TEST_PROGRAM_ID = new PublicKey('TokenHookExampLe8smaVNrxTBezWTRbEwxwb1Zykrb'); diff --git a/token/js/test/e2e-2022/closeMint.test.ts b/token/js/test/e2e-2022/closeMint.test.ts index f5351f65c80..f6610a9c251 100644 --- a/token/js/test/e2e-2022/closeMint.test.ts +++ b/token/js/test/e2e-2022/closeMint.test.ts @@ -2,7 +2,8 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; chai.use(chaiAsPromised); -import type { Connection, PublicKey, Signer } from '@solana/web3.js'; +import type { Connection, Signer } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { sendAndConfirmTransaction, Keypair, SystemProgram, Transaction } from '@solana/web3.js'; import { createAccount, @@ -12,6 +13,10 @@ import { mintTo, getMintLen, ExtensionType, + AuthorityType, + getMint, + setAuthority, + getMintCloseAuthority, } from '../../src'; import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../common'; @@ -80,4 +85,23 @@ describe('closeMint', () => { expect(destinationInfo.lamports).to.eql(rentExemptAmount); } }); + it('authority', async () => { + await setAuthority( + connection, + payer, + mint, + closeAuthority, + AuthorityType.CloseMint, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const mintCloseAuthority = getMintCloseAuthority(mintInfo); + expect(mintCloseAuthority).to.not.be.null; + if (mintCloseAuthority !== null) { + expect(mintCloseAuthority.closeAuthority).to.eql(PublicKey.default); + } + }); }); diff --git a/token/js/test/e2e-2022/interestBearingMint.test.ts b/token/js/test/e2e-2022/interestBearingMint.test.ts index 2edd20ec92f..6da2514771e 100644 --- a/token/js/test/e2e-2022/interestBearingMint.test.ts +++ b/token/js/test/e2e-2022/interestBearingMint.test.ts @@ -2,12 +2,15 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; chai.use(chaiAsPromised); -import type { Connection, PublicKey, Signer } from '@solana/web3.js'; +import type { Connection, Signer } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { Keypair } from '@solana/web3.js'; import { + AuthorityType, createInterestBearingMint, getInterestBearingMintConfigState, getMint, + setAuthority, updateRateInterestBearingMint, } from '../../src'; import { getConnection, newAccountWithLamports, TEST_PROGRAM_ID } from '../common'; @@ -81,4 +84,23 @@ describe('interestBearingMint', () => { expect(updatedRateConfigState.initializationTimestamp).to.be.greaterThan(0); } }); + it('authority', async () => { + await setAuthority( + connection, + payer, + mint, + rateAuthority, + AuthorityType.InterestRate, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const rateConfigState = getInterestBearingMintConfigState(mintInfo); + expect(rateConfigState).to.not.be.null; + if (rateConfigState !== null) { + expect(rateConfigState.rateAuthority).to.eql(PublicKey.default); + } + }); }); diff --git a/token/js/test/e2e-2022/permanentDelegate.test.ts b/token/js/test/e2e-2022/permanentDelegate.test.ts index 5cbbc8ac5b9..802518a51fe 100644 --- a/token/js/test/e2e-2022/permanentDelegate.test.ts +++ b/token/js/test/e2e-2022/permanentDelegate.test.ts @@ -2,7 +2,8 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; chai.use(chaiAsPromised); -import type { Connection, PublicKey, Signer } from '@solana/web3.js'; +import type { Connection, Signer } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { sendAndConfirmTransaction, Keypair, SystemProgram, Transaction } from '@solana/web3.js'; import { createAccount, @@ -13,6 +14,10 @@ import { createInitializePermanentDelegateInstruction, burn, transferChecked, + AuthorityType, + getMint, + setAuthority, + getPermanentDelegate, } from '../../src'; import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../common'; @@ -100,4 +105,23 @@ describe('permanentDelegate', () => { expect(destination_info.value.uiAmount).to.eql(2); } }); + it('authority', async () => { + await setAuthority( + connection, + payer, + mint, + permanentDelegate, + AuthorityType.PermanentDelegate, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const permanentDelegateConfig = getPermanentDelegate(mintInfo); + expect(permanentDelegateConfig).to.not.be.null; + if (permanentDelegateConfig !== null) { + expect(permanentDelegateConfig.delegate).to.eql(PublicKey.default); + } + }); }); diff --git a/token/js/test/e2e-2022/transferFee.test.ts b/token/js/test/e2e-2022/transferFee.test.ts index ce0bae69410..8e5cad6266d 100644 --- a/token/js/test/e2e-2022/transferFee.test.ts +++ b/token/js/test/e2e-2022/transferFee.test.ts @@ -2,7 +2,8 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; chai.use(chaiAsPromised); -import type { Connection, PublicKey, Signer } from '@solana/web3.js'; +import type { Connection, Signer } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { Keypair, SystemProgram, Transaction, sendAndConfirmTransaction } from '@solana/web3.js'; import { @@ -16,6 +17,8 @@ import { getAccount, getMint, getMintLen, + setAuthority, + AuthorityType, } from '../../src'; import { @@ -226,4 +229,42 @@ describe('transferFee', () => { expect(transferFeeConfig.withheldAmount).to.eql(BigInt(0)); } }); + it('transferFeeConfigAuthority', async () => { + await setAuthority( + connection, + payer, + mint, + transferFeeConfigAuthority, + AuthorityType.TransferFeeConfig, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const transferFeeConfig = getTransferFeeConfig(mintInfo); + expect(transferFeeConfig).to.not.be.null; + if (transferFeeConfig !== null) { + expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(PublicKey.default); + } + }); + it('withdrawWithheldAuthority', async () => { + await setAuthority( + connection, + payer, + mint, + withdrawWithheldAuthority, + AuthorityType.WithheldWithdraw, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const transferFeeConfig = getTransferFeeConfig(mintInfo); + expect(transferFeeConfig).to.not.be.null; + if (transferFeeConfig !== null) { + expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(PublicKey.default); + } + }); }); diff --git a/token/js/test/e2e-2022/transferHook.test.ts b/token/js/test/e2e-2022/transferHook.test.ts new file mode 100644 index 00000000000..58d16af2486 --- /dev/null +++ b/token/js/test/e2e-2022/transferHook.test.ts @@ -0,0 +1,224 @@ +import chai, { expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +chai.use(chaiAsPromised); + +import type { AccountMeta, Connection, Signer } from '@solana/web3.js'; +import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { sendAndConfirmTransaction, Keypair, SystemProgram, Transaction } from '@solana/web3.js'; +import { + createInitializeMintInstruction, + getMint, + getMintLen, + ExtensionType, + createInitializeTransferHookInstruction, + getTransferHook, + updateTransferHook, + AuthorityType, + setAuthority, + createAssociatedTokenAccountInstruction, + getAssociatedTokenAddressSync, + ASSOCIATED_TOKEN_PROGRAM_ID, + createMintToCheckedInstruction, + getExtraAccountMetaAddress, + ExtraAccountMetaListLayout, + ExtraAccountMetaLayout, + transferCheckedWithTransferHook, + createAssociatedTokenAccountIdempotent, +} from '../../src'; +import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection, TRANSFER_HOOK_TEST_PROGRAM_ID } from '../common'; +import { createHash } from 'crypto'; + +const TEST_TOKEN_DECIMALS = 2; +const EXTENSIONS = [ExtensionType.TransferHook]; +describe('transferHook', () => { + let connection: Connection; + let payer: Signer; + let payerAta: PublicKey; + let destinationAuthority: PublicKey; + let destinationAta: PublicKey; + let transferHookAuthority: Keypair; + let pdaExtraAccountMeta: PublicKey; + let mint: PublicKey; + before(async () => { + connection = await getConnection(); + payer = await newAccountWithLamports(connection, 1000000000); + destinationAuthority = Keypair.generate().publicKey; + transferHookAuthority = Keypair.generate(); + }); + beforeEach(async () => { + const mintKeypair = Keypair.generate(); + mint = mintKeypair.publicKey; + pdaExtraAccountMeta = getExtraAccountMetaAddress(mint, TRANSFER_HOOK_TEST_PROGRAM_ID); + payerAta = getAssociatedTokenAddressSync( + mint, + payer.publicKey, + false, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + destinationAta = getAssociatedTokenAddressSync( + mint, + destinationAuthority, + false, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const mintLen = getMintLen(EXTENSIONS); + const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); + const transaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + space: mintLen, + lamports, + programId: TEST_PROGRAM_ID, + }), + createInitializeTransferHookInstruction( + mint, + transferHookAuthority.publicKey, + TRANSFER_HOOK_TEST_PROGRAM_ID, + TEST_PROGRAM_ID + ), + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, payer.publicKey, null, TEST_PROGRAM_ID) + ); + + await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); + }); + it('is initialized', async () => { + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const transferHook = getTransferHook(mintInfo); + expect(transferHook).to.not.be.null; + if (transferHook !== null) { + expect(transferHook.authority).to.eql(transferHookAuthority.publicKey); + expect(transferHook.programId).to.eql(TRANSFER_HOOK_TEST_PROGRAM_ID); + } + }); + it('can be updated', async () => { + const newTransferHookProgramId = Keypair.generate().publicKey; + await updateTransferHook( + connection, + payer, + mint, + newTransferHookProgramId, + transferHookAuthority, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const transferHook = getTransferHook(mintInfo); + expect(transferHook).to.not.be.null; + if (transferHook !== null) { + expect(transferHook.authority).to.eql(transferHookAuthority.publicKey); + expect(transferHook.programId).to.eql(newTransferHookProgramId); + } + }); + it('authority', async () => { + await setAuthority( + connection, + payer, + mint, + transferHookAuthority, + AuthorityType.TransferHookProgramId, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const transferHook = getTransferHook(mintInfo); + expect(transferHook).to.not.be.null; + if (transferHook !== null) { + expect(transferHook.authority).to.eql(PublicKey.default); + } + }); + it('transferChecked', async () => { + const extraAccount = Keypair.generate().publicKey; + const keys: AccountMeta[] = [ + { pubkey: pdaExtraAccountMeta, isSigner: false, isWritable: true }, + { pubkey: mint, isSigner: false, isWritable: false }, + { pubkey: payer.publicKey, isSigner: true, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ]; + + const data = Buffer.alloc(8 + 4 + ExtraAccountMetaLayout.span); + const discriminator = createHash('sha256') + .update('spl-transfer-hook-interface:initialize-extra-account-metas') + .digest() + .subarray(0, 8); + discriminator.copy(data); + ExtraAccountMetaListLayout.encode( + { + count: 1, + extraAccounts: [ + { + discriminator: 0, + addressConfig: extraAccount.toBuffer(), + isSigner: false, + isWritable: false, + }, + ], + }, + data, + 8 + ); + + const initExtraAccountMetaInstruction = new TransactionInstruction({ + keys, + data, + programId: TRANSFER_HOOK_TEST_PROGRAM_ID, + }); + + const setupTransaction = new Transaction().add( + initExtraAccountMetaInstruction, + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: pdaExtraAccountMeta, + lamports: 10000000, + }), + createAssociatedTokenAccountInstruction( + payer.publicKey, + payerAta, + payer.publicKey, + mint, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ), + createMintToCheckedInstruction( + mint, + payerAta, + payer.publicKey, + 5 * 10 ** TEST_TOKEN_DECIMALS, + TEST_TOKEN_DECIMALS, + [], + TEST_PROGRAM_ID + ) + ); + + await sendAndConfirmTransaction(connection, setupTransaction, [payer]); + + await createAssociatedTokenAccountIdempotent( + connection, + payer, + mint, + destinationAuthority, + undefined, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + await transferCheckedWithTransferHook( + connection, + payer, + payerAta, + mint, + destinationAta, + payer, + BigInt(10 ** TEST_TOKEN_DECIMALS), + TEST_TOKEN_DECIMALS, + [], + undefined, + TEST_PROGRAM_ID + ); + }); +}); diff --git a/token/js/test/e2e/create.test.ts b/token/js/test/e2e/create.test.ts index 9a9cdda5238..b6e17731478 100644 --- a/token/js/test/e2e/create.test.ts +++ b/token/js/test/e2e/create.test.ts @@ -13,6 +13,7 @@ import { createAssociatedTokenAccountIdempotent, getAccount, getAssociatedTokenAddress, + getOrCreateAssociatedTokenAccount, } from '../../src'; import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../common'; @@ -100,6 +101,57 @@ describe('createAccount', () => { ); expect(account2).to.not.eql(account); }), + it('creates associated token account if it does not exist', async () => { + const owner = Keypair.generate(); + const associatedAddress = await getAssociatedTokenAddress( + mint, + owner.publicKey, + false, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + // associated account shouldn't exist + const info = await connection.getAccountInfo(associatedAddress); + expect(info).to.be.null; + + const createdAccountInfo = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + owner.publicKey, + false, + undefined, + undefined, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + expect(createdAccountInfo.mint).to.eql(mint); + expect(createdAccountInfo.owner).to.eql(owner.publicKey); + expect(createdAccountInfo.amount).to.eql(BigInt(0)); + expect(createdAccountInfo.delegate).to.be.null; + expect(createdAccountInfo.delegatedAmount).to.eql(BigInt(0)); + expect(createdAccountInfo.isInitialized).to.be.true; + expect(createdAccountInfo.isFrozen).to.be.false; + expect(createdAccountInfo.isNative).to.be.false; + expect(createdAccountInfo.rentExemptReserve).to.be.null; + expect(createdAccountInfo.closeAuthority).to.be.null; + + // do it again, just gives the account info + const accountInfo = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + owner.publicKey, + false, + undefined, + undefined, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + expect(createdAccountInfo).to.eql(accountInfo); + }), it('associated token account', async () => { const owner = Keypair.generate(); const associatedAddress = await getAssociatedTokenAddress( diff --git a/token/js/test/e2e/setAuthority.test.ts b/token/js/test/e2e/setAuthority.test.ts index 1700d82b2f9..f5c931907df 100644 --- a/token/js/test/e2e/setAuthority.test.ts +++ b/token/js/test/e2e/setAuthority.test.ts @@ -15,18 +15,20 @@ describe('setAuthority', () => { let payer: Signer; let mint: PublicKey; let mintAuthority: Keypair; + let freezeAuthority: Keypair; let owner: Keypair; let account: PublicKey; before(async () => { connection = await getConnection(); payer = await newAccountWithLamports(connection, 1000000000); mintAuthority = Keypair.generate(); + freezeAuthority = Keypair.generate(); const mintKeypair = Keypair.generate(); mint = await createMint( connection, payer, mintAuthority.publicKey, - mintAuthority.publicKey, + freezeAuthority.publicKey, TEST_TOKEN_DECIMALS, mintKeypair, undefined, @@ -116,4 +118,19 @@ describe('setAuthority', () => { const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); expect(accountInfo.closeAuthority).to.eql(closeAuthority.publicKey); }); + it('FreezeAuthority', async () => { + await setAuthority( + connection, + payer, + mint, + freezeAuthority, + AuthorityType.FreezeAccount, + null, + [], + undefined, + TEST_PROGRAM_ID + ); + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + expect(mintInfo.freezeAuthority).to.be.null; + }); }); diff --git a/token/js/test/unit/programId.test.ts b/token/js/test/unit/programId.test.ts index b192792a2ce..e9a435b85a9 100644 --- a/token/js/test/unit/programId.test.ts +++ b/token/js/test/unit/programId.test.ts @@ -16,6 +16,7 @@ import { TokenUnsupportedInstructionError, createInitializePermanentDelegateInstruction, createEnableCpiGuardInstruction, + createInitializeTransferHookInstruction, } from '../../src'; chai.use(chaiAsPromised); @@ -24,6 +25,7 @@ describe('unsupported extensions in spl-token', () => { const account = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'); const authority = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'); const payer = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'); + const transferHookProgramId = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'); it('initializeMintCloseAuthority', () => { expect(function () { createInitializeMintCloseAuthorityInstruction(mint, null, TOKEN_PROGRAM_ID); @@ -64,6 +66,14 @@ describe('unsupported extensions in spl-token', () => { createCreateNativeMintInstruction(payer, NATIVE_MINT_2022, TOKEN_2022_PROGRAM_ID); }).to.not.throw(TokenUnsupportedInstructionError); }); + it('transferHook', () => { + expect(function () { + createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, TOKEN_PROGRAM_ID); + }).to.throw(TokenUnsupportedInstructionError); + expect(function () { + createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, TOKEN_2022_PROGRAM_ID); + }).to.not.throw(TokenUnsupportedInstructionError); + }); it('nonTransferableMint', () => { expect(function () { createInitializeNonTransferableMintInstruction(mint, TOKEN_PROGRAM_ID); diff --git a/token/js/test/unit/transferHook.test.ts b/token/js/test/unit/transferHook.test.ts new file mode 100644 index 00000000000..7d4909b8125 --- /dev/null +++ b/token/js/test/unit/transferHook.test.ts @@ -0,0 +1,142 @@ +import { getExtraAccountMetas, resolveExtraAccountMeta } from '../../src'; +import { expect } from 'chai'; +import { PublicKey } from '@solana/web3.js'; + +describe('transferHookExtraAccounts', () => { + const testProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj'); + const instructionData = Buffer.from(Array.from(Array(32).keys())); + const plainAccount = new PublicKey('6c5q79ccBTWvZTEx3JkdHThtMa2eALba5bfvHGf8kA2c'); + const seeds = [Buffer.from('seed'), Buffer.from([4, 5, 6, 7]), plainAccount.toBuffer()]; + const pdaPublicKey = PublicKey.findProgramAddressSync(seeds, testProgramId)[0]; + const pdaPublicKeyWithProgramId = PublicKey.findProgramAddressSync(seeds, plainAccount)[0]; + + const plainSeed = Buffer.concat([ + Buffer.from([1]), // u8 discriminator + Buffer.from([4]), // u8 length + Buffer.from('seed'), // 4 bytes seed + ]); + + const instructionDataSeed = Buffer.concat([ + Buffer.from([2]), // u8 discriminator + Buffer.from([4]), // u8 offset + Buffer.from([4]), // u8 length + ]); + + const accountKeySeed = Buffer.concat([ + Buffer.from([3]), // u8 discriminator + Buffer.from([0]), // u8 index + ]); + + const addressConfig = Buffer.concat([plainSeed, instructionDataSeed, accountKeySeed], 32); + + const plainExtraAccountMeta = { + discriminator: 0, + addressConfig: plainAccount.toBuffer(), + isSigner: false, + isWritable: false, + }; + const plainExtraAccount = Buffer.concat([ + Buffer.from([0]), // u8 discriminator + plainAccount.toBuffer(), // 32 bytes address + Buffer.from([0]), // bool isSigner + Buffer.from([0]), // bool isWritable + ]); + + const pdaExtraAccountMeta = { + discriminator: 1, + addressConfig, + isSigner: true, + isWritable: false, + }; + const pdaExtraAccount = Buffer.concat([ + Buffer.from([1]), // u8 discriminator + addressConfig, // 32 bytes address config + Buffer.from([1]), // bool isSigner + Buffer.from([0]), // bool isWritable + ]); + + const pdaExtraAccountMetaWithProgramId = { + discriminator: 128, + addressConfig, + isSigner: false, + isWritable: true, + }; + const pdaExtraAccountWithProgramId = Buffer.concat([ + Buffer.from([128]), // u8 discriminator + addressConfig, // 32 bytes address config + Buffer.from([0]), // bool isSigner + Buffer.from([1]), // bool isWritable + ]); + + const extraAccountList = Buffer.concat([ + Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), // u64 accountDiscriminator + Buffer.from([0, 0, 0, 0]), // u32 length + Buffer.from([3, 0, 0, 0]), // u32 count + plainExtraAccount, + pdaExtraAccount, + pdaExtraAccountWithProgramId, + ]); + + it('getExtraAccountMetas', () => { + const accountInfo = { + data: extraAccountList, + owner: PublicKey.default, + executable: false, + lamports: 0, + }; + const parsedExtraAccounts = getExtraAccountMetas(accountInfo); + expect(parsedExtraAccounts).to.not.be.null; + if (parsedExtraAccounts == null) { + return; + } + + expect(parsedExtraAccounts).to.have.length(3); + if (parsedExtraAccounts.length !== 3) { + return; + } + + expect(parsedExtraAccounts[0].discriminator).to.eql(0); + expect(parsedExtraAccounts[0].addressConfig).to.eql(plainAccount.toBuffer()); + expect(parsedExtraAccounts[0].isSigner).to.be.false; + expect(parsedExtraAccounts[0].isWritable).to.be.false; + + expect(parsedExtraAccounts[1].discriminator).to.eql(1); + expect(parsedExtraAccounts[1].addressConfig).to.eql(addressConfig); + expect(parsedExtraAccounts[1].isSigner).to.be.true; + expect(parsedExtraAccounts[1].isWritable).to.be.false; + + expect(parsedExtraAccounts[2].discriminator).to.eql(128); + expect(parsedExtraAccounts[2].addressConfig).to.eql(addressConfig); + expect(parsedExtraAccounts[2].isSigner).to.be.false; + expect(parsedExtraAccounts[2].isWritable).to.be.true; + }); + it('resolveExtraAccountMeta', () => { + const resolvedPlainAccount = resolveExtraAccountMeta(plainExtraAccountMeta, [], instructionData, testProgramId); + + expect(resolvedPlainAccount.pubkey).to.eql(plainAccount); + expect(resolvedPlainAccount.isSigner).to.be.false; + expect(resolvedPlainAccount.isWritable).to.be.false; + + const resolvedPdaAccount = resolveExtraAccountMeta( + pdaExtraAccountMeta, + [resolvedPlainAccount], + instructionData, + testProgramId + ); + + expect(resolvedPdaAccount.pubkey).to.eql(pdaPublicKey); + expect(resolvedPdaAccount.isSigner).to.be.true; + expect(resolvedPdaAccount.isWritable).to.be.false; + + const resolvedPdaAccountWithProgramId = resolveExtraAccountMeta( + pdaExtraAccountMetaWithProgramId, + [resolvedPlainAccount], + instructionData, + testProgramId + ); + + expect(resolvedPdaAccountWithProgramId.pubkey).to.eql(pdaPublicKeyWithProgramId); + expect(resolvedPdaAccountWithProgramId.isSigner).to.be.false; + expect(resolvedPdaAccountWithProgramId.isWritable).to.be.true; + }); +}); diff --git a/token/program-2022-test/Cargo.toml b/token/program-2022-test/Cargo.toml index 555ab1ebc8b..ec702843feb 100644 --- a/token/program-2022-test/Cargo.toml +++ b/token/program-2022-test/Cargo.toml @@ -18,15 +18,18 @@ walkdir = "2" [dev-dependencies] async-trait = "0.1" +borsh = "0.10" futures-util = "0.3" -solana-program = "=1.16.1" -solana-program-test = "=1.16.1" -solana-sdk = "=1.16.1" +solana-program = "=1.17.2" +solana-program-test = "=1.17.2" +solana-sdk = "=1.17.2" spl-associated-token-account = { version = "2.0", path = "../../associated-token-account/program" } spl-memo = { version = "4.0.0", path = "../../memo/program", features = ["no-entrypoint"] } -spl-token-2022 = { version = "0.7", path="../program-2022", features = ["no-entrypoint"] } +spl-pod = { version = "0.1.0", path = "../../libraries/pod" } +spl-token-2022 = { version = "0.9", path="../program-2022", features = ["no-entrypoint"] } spl-instruction-padding = { version = "0.1.0", path="../../instruction-padding/program", features = ["no-entrypoint"] } -spl-token-client = { version = "0.5", path = "../client" } -spl-transfer-hook-example = { version = "0.1", path="../transfer-hook-example", features = ["no-entrypoint"] } -spl-transfer-hook-interface = { version = "0.1", path="../transfer-hook-interface" } -test-case = "3.1" +spl-token-client = { version = "0.8", path = "../client" } +spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" } +spl-transfer-hook-example = { version = "0.3", path="../transfer-hook/example", features = ["no-entrypoint"] } +spl-transfer-hook-interface = { version = "0.3", path="../transfer-hook/interface" } +test-case = "3.2" diff --git a/token/program-2022-test/tests/confidential_transfer.rs b/token/program-2022-test/tests/confidential_transfer.rs index b7e4160c888..f7e16a58a7f 100644 --- a/token/program-2022-test/tests/confidential_transfer.rs +++ b/token/program-2022-test/tests/confidential_transfer.rs @@ -1,157 +1,143 @@ -#![cfg(all(feature = "test-sbf"))] -#![cfg(twoxtx)] +#![cfg(feature = "test-sbf")] mod program_test; use { program_test::{TestContext, TokenContext}, solana_program_test::tokio, solana_sdk::{ - instruction::InstructionError, signature::Signer, signer::keypair::Keypair, - transaction::TransactionError, transport::TransportError, + instruction::InstructionError, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + transaction::{Transaction, TransactionError}, + transport::TransportError, }, spl_token_2022::{ error::TokenError, - extension::{confidential_transfer::ConfidentialTransferMint, BaseStateWithExtensions}, - instruction, - solana_zk_token_sdk::encryption::elgamal::*, - }, - spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, - std::convert::TryInto, -}; - -#[cfg(feature = "proof-program")] -use { - solana_sdk::{pubkey::Pubkey, signature::Signer}, - spl_token_2022::{ extension::{ - confidential_transfer::{ConfidentialTransferAccount, EncryptedWithheldAmount}, - ExtensionType, + confidential_transfer::{ + self, + account_info::TransferAccountInfo, + instruction::{ + TransferSplitContextStateAccounts, TransferWithFeeSplitContextStateAccounts, + }, + ConfidentialTransferAccount, MAXIMUM_DEPOSIT_TRANSFER_AMOUNT, + }, + BaseStateWithExtensions, ExtensionType, }, - solana_zk_token_sdk::{encryption::auth_encryption::*, zk_token_elgamal::pod::Zeroable}, + solana_zk_token_sdk::{ + encryption::{auth_encryption::*, elgamal::*}, + zk_token_elgamal::pod::{self, Zeroable}, + zk_token_proof_instruction::*, + zk_token_proof_program, + zk_token_proof_state::ProofContextState, + }, + }, + spl_token_client::{ + client::{SendTransaction, SimulateTransaction}, + proof_generation::transfer_with_fee_split_proof_data, + token::{ExtensionInitializationParams, Token, TokenError as TokenClientError}, }, - spl_token_client::{client::SendTransaction, token::Token}, + std::{convert::TryInto, mem::size_of}, }; -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] -use {solana_sdk::epoch_info::EpochInfo, spl_token_2022::solana_zk_token_sdk::zk_token_elgamal}; - -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] const TEST_MAXIMUM_FEE: u64 = 100; -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] const TEST_FEE_BASIS_POINTS: u16 = 250; -#[cfg(feature = "proof-program")] -const TEST_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 2; - -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] -fn test_epoch_info() -> EpochInfo { - EpochInfo { - epoch: 0, - slot_index: 0, - slots_in_epoch: 0, - absolute_slot: 0, - block_height: 0, - transaction_count: None, - } -} -#[cfg(feature = "proof-program")] struct ConfidentialTokenAccountMeta { token_account: Pubkey, elgamal_keypair: ElGamalKeypair, - ae_key: AeKey, + aes_key: AeKey, } -#[cfg(feature = "proof-program")] impl ConfidentialTokenAccountMeta { - async fn new(token: &Token, owner: &Keypair) -> Self + async fn new( + token: &Token, + owner: &Keypair, + maximum_pending_balance_credit_counter: Option, + require_memo: bool, + require_fee: bool, + ) -> Self where - T: SendTransaction, + T: SendTransaction + SimulateTransaction, { let token_account_keypair = Keypair::new(); - token - .create_auxiliary_token_account_with_extension_space( - &token_account_keypair, - &owner.pubkey(), - vec![ExtensionType::ConfidentialTransferAccount], - ) - .await - .unwrap(); - let token_account = token_account_keypair.pubkey(); - - let elgamal_keypair = ElGamalKeypair::new(owner, &token_account).unwrap(); - let ae_key = AeKey::new(owner, &token_account).unwrap(); - - token - .confidential_transfer_configure_token_account_with_pending_counter( - &token_account, - owner, - TEST_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER, - ) - .await - .unwrap(); - Self { - token_account, - elgamal_keypair, - ae_key, + let mut extensions = vec![ExtensionType::ConfidentialTransferAccount]; + if require_memo { + extensions.push(ExtensionType::MemoTransfer); + } + if require_fee { + extensions.push(ExtensionType::ConfidentialTransferFeeAmount); } - } - #[cfg(feature = "zk-ops")] - async fn new_with_required_memo_transfers(token: &Token, owner: &Keypair) -> Self - where - T: SendTransaction, - { - let token_account_keypair = Keypair::new(); token .create_auxiliary_token_account_with_extension_space( &token_account_keypair, &owner.pubkey(), - vec![ - ExtensionType::ConfidentialTransferAccount, - ExtensionType::MemoTransfer, - ], + extensions, ) .await .unwrap(); let token_account = token_account_keypair.pubkey(); - let elgamal_keypair = ElGamalKeypair::new(owner, &token_account).unwrap(); - let ae_key = AeKey::new(owner, &token_account).unwrap(); + let elgamal_keypair = + ElGamalKeypair::new_from_signer(owner, &token_account.to_bytes()).unwrap(); + let aes_key = AeKey::new_from_signer(owner, &token_account.to_bytes()).unwrap(); token - .confidential_transfer_configure_token_account_with_pending_counter( + .confidential_transfer_configure_token_account( &token_account, - owner, - TEST_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER, + &owner.pubkey(), + None, + maximum_pending_balance_credit_counter, + &elgamal_keypair, + &aes_key, + &[owner], ) .await .unwrap(); - token - .enable_required_transfer_memos(&token_account, &owner.pubkey(), &[owner]) - .await - .unwrap(); + if require_memo { + token + .enable_required_transfer_memos(&token_account, &owner.pubkey(), &[owner]) + .await + .unwrap(); + } Self { token_account, elgamal_keypair, - ae_key, + aes_key, } } - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] - async fn with_tokens( + #[allow(clippy::too_many_arguments)] + #[cfg(feature = "zk-ops")] + async fn new_with_tokens( token: &Token, owner: &Keypair, + maximum_pending_balance_credit_counter: Option, + require_memo: bool, + require_fee: bool, mint_authority: &Keypair, amount: u64, decimals: u8, ) -> Self where - T: SendTransaction, + T: SendTransaction + SimulateTransaction, { - let meta = Self::new(token, owner).await; + let meta = Self::new( + token, + owner, + maximum_pending_balance_credit_counter, + require_memo, + require_fee, + ) + .await; token .mint_to( @@ -164,21 +150,34 @@ impl ConfidentialTokenAccountMeta { .unwrap(); token - .confidential_transfer_deposit(&meta.token_account, owner, amount, decimals) + .confidential_transfer_deposit( + &meta.token_account, + &owner.pubkey(), + amount, + decimals, + &[owner], + ) .await .unwrap(); token - .confidential_transfer_apply_pending_balance(&meta.token_account, owner, 0, amount, 1) + .confidential_transfer_apply_pending_balance( + &meta.token_account, + &owner.pubkey(), + None, + meta.elgamal_keypair.secret(), + &meta.aes_key, + &[owner], + ) .await .unwrap(); meta } - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] + #[cfg(feature = "zk-ops")] async fn check_balances(&self, token: &Token, expected: ConfidentialTokenAccountBalances) where - T: SendTransaction, + T: SendTransaction + SimulateTransaction, { let state = token.get_account_info(&self.token_account).await.unwrap(); let extension = state @@ -188,26 +187,26 @@ impl ConfidentialTokenAccountMeta { assert_eq!( extension .pending_balance_lo - .decrypt(&self.elgamal_keypair.secret) + .decrypt(self.elgamal_keypair.secret()) .unwrap(), expected.pending_balance_lo, ); assert_eq!( extension .pending_balance_hi - .decrypt(&self.elgamal_keypair.secret) + .decrypt(self.elgamal_keypair.secret()) .unwrap(), expected.pending_balance_hi, ); assert_eq!( extension .available_balance - .decrypt(&self.elgamal_keypair.secret) + .decrypt(self.elgamal_keypair.secret()) .unwrap(), expected.available_balance, ); assert_eq!( - self.ae_key + self.aes_key .decrypt(&extension.decryptable_available_balance.try_into().unwrap()) .unwrap(), expected.decryptable_available_balance, @@ -215,7 +214,7 @@ impl ConfidentialTokenAccountMeta { } } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] struct ConfidentialTokenAccountBalances { pending_balance_lo: u64, pending_balance_hi: u64, @@ -223,27 +222,10 @@ struct ConfidentialTokenAccountBalances { decryptable_available_balance: u64, } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] -async fn check_withheld_amount_in_mint( - token: &Token, - withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, - expected: u64, -) where - T: SendTransaction, -{ - let state = token.get_mint_info().await.unwrap(); - let extension = state.get_extension::().unwrap(); - let decrypted_amount = extension - .withheld_amount - .decrypt(&withdraw_withheld_authority_elgamal_keypair.secret) - .unwrap(); - assert_eq!(decrypted_amount, expected); -} - #[tokio::test] -async fn confidential_transfer_initialize_and_update_mint() { +async fn confidential_transfer_configure_token_account() { let authority = Keypair::new(); - let auto_approve_new_accounts = true; + let auto_approve_new_accounts = false; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); @@ -259,150 +241,9 @@ async fn confidential_transfer_initialize_and_update_mint() { .await .unwrap(); - let TokenContext { token, .. } = context.token_context.unwrap(); - - let state = token.get_mint_info().await.unwrap(); - let extension = state.get_extension::().unwrap(); - - assert_eq!( - extension.authority, - Some(authority.pubkey()).try_into().unwrap() - ); - assert_eq!( - extension.auto_approve_new_accounts, - auto_approve_new_accounts.into() - ); - assert_eq!( - extension.auditor_elgamal_pubkey, - Some(auditor_elgamal_pubkey).try_into().unwrap() - ); - - // Change the authority - let new_authority = Keypair::new(); - let wrong_keypair = Keypair::new(); - - let err = token - .set_authority( - token.get_address(), - &wrong_keypair.pubkey(), - Some(&new_authority.pubkey()), - instruction::AuthorityType::ConfidentialTransferMint, - &[&wrong_keypair], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::OwnerMismatch as u32) - ) - ))) - ); - - token - .set_authority( - token.get_address(), - &authority.pubkey(), - Some(&new_authority.pubkey()), - instruction::AuthorityType::ConfidentialTransferMint, - &[&authority], - ) - .await - .unwrap(); - - // New authority can change mint parameters while the old cannot - let new_auto_approve_new_accounts = false; - let new_auditor_elgamal_pubkey = None; - - let err = token - .confidential_transfer_update_mint( - &authority, - new_auto_approve_new_accounts, - new_auditor_elgamal_pubkey, - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::OwnerMismatch as u32) - ) - ))) - ); - - token - .confidential_transfer_update_mint( - &new_authority, - new_auto_approve_new_accounts, - new_auditor_elgamal_pubkey, - ) - .await - .unwrap(); - - let state = token.get_mint_info().await.unwrap(); - let extension = state.get_extension::().unwrap(); - assert_eq!( - extension.authority, - Some(new_authority.pubkey()).try_into().unwrap() - ); - assert_eq!( - extension.auto_approve_new_accounts, - new_auto_approve_new_accounts.try_into().unwrap(), - ); - assert_eq!( - extension.auditor_elgamal_pubkey, - new_auditor_elgamal_pubkey.try_into().unwrap(), - ); - - // Set new authority to None - token - .set_authority( - token.get_address(), - &new_authority.pubkey(), - None, - instruction::AuthorityType::ConfidentialTransferMint, - &[&new_authority], - ) - .await - .unwrap(); - - let state = token.get_mint_info().await.unwrap(); - let extension = state.get_extension::().unwrap(); - assert_eq!(extension.authority, None.try_into().unwrap()); -} - -#[cfg(feature = "proof-program")] -#[tokio::test] -async fn ct_configure_token_account() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_authority, - .. - } = ConfidentialTransferMintWithKeypairs::without_auto_approve(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), - }, - ]) - .await - .unwrap(); - let TokenContext { token, alice, .. } = context.token_context.unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice).await; + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; + let alice_elgamal_pubkey = (*alice_meta.elgamal_keypair.pubkey()).into(); let state = token .get_account_info(&alice_meta.token_account) @@ -413,20 +254,21 @@ async fn ct_configure_token_account() { .unwrap(); assert!(!bool::from(&extension.approved)); assert!(bool::from(&extension.allow_confidential_credits)); - assert_eq!( - extension.elgamal_pubkey, - alice_meta.elgamal_keypair.public.into() - ); + assert_eq!(extension.elgamal_pubkey, alice_elgamal_pubkey); assert_eq!( alice_meta - .ae_key + .aes_key .decrypt(&(extension.decryptable_available_balance.try_into().unwrap())) .unwrap(), 0 ); token - .confidential_transfer_approve_account(&alice_meta.token_account, &ct_mint_authority) + .confidential_transfer_approve_account( + &alice_meta.token_account, + &authority.pubkey(), + &[&authority], + ) .await .unwrap(); @@ -441,10 +283,14 @@ async fn ct_configure_token_account() { // Configuring an already initialized account should produce an error let err = token - .confidential_transfer_configure_token_account_with_pending_counter( + .confidential_transfer_configure_token_account( &alice_meta.token_account, - &alice, - TEST_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER, + &alice.pubkey(), + None, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], ) .await .unwrap_err(); @@ -460,31 +306,40 @@ async fn ct_configure_token_account() { ); } -#[cfg(feature = "proof-program")] #[tokio::test] -async fn ct_enable_disable_confidential_credits() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_enable_disable_confidential_credits() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await .unwrap(); - let TokenContext { token, alice, .. } = context.token_context.unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice).await; + let TokenContext { + token, + alice, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; token - .confidential_transfer_disable_confidential_credits(&alice_meta.token_account, &alice) + .confidential_transfer_disable_confidential_credits( + &alice_meta.token_account, + &alice.pubkey(), + &[&alice], + ) .await .unwrap(); let state = token @@ -497,7 +352,44 @@ async fn ct_enable_disable_confidential_credits() { assert!(!bool::from(&extension.allow_confidential_credits)); token - .confidential_transfer_enable_confidential_credits(&alice_meta.token_account, &alice) + .mint_to( + &alice_meta.token_account, + &mint_authority.pubkey(), + 10, + &[&mint_authority], + ) + .await + .unwrap(); + + let err = token + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 10, + decimals, + &[&alice], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom( + TokenError::ConfidentialTransferDepositsAndTransfersDisabled as u32 + ) + ) + ))) + ); + + token + .confidential_transfer_enable_confidential_credits( + &alice_meta.token_account, + &alice.pubkey(), + &[&alice], + ) .await .unwrap(); let state = token @@ -508,23 +400,35 @@ async fn ct_enable_disable_confidential_credits() { .get_extension::() .unwrap(); assert!(bool::from(&extension.allow_confidential_credits)); + + // Refresh the blockhash since we're doing the same thing twice in a row + token.get_new_latest_blockhash().await.unwrap(); + token + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 10, + decimals, + &[&alice], + ) + .await + .unwrap(); } -#[cfg(feature = "proof-program")] #[tokio::test] -async fn ct_enable_disable_non_confidential_credits() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_enable_disable_non_confidential_credits() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await @@ -537,8 +441,8 @@ async fn ct_enable_disable_non_confidential_credits() { mint_authority, .. } = context.token_context.unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice).await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await; + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, false).await; token .mint_to( @@ -551,7 +455,11 @@ async fn ct_enable_disable_non_confidential_credits() { .unwrap(); token - .confidential_transfer_disable_non_confidential_credits(&bob_meta.token_account, &bob) + .confidential_transfer_disable_non_confidential_credits( + &bob_meta.token_account, + &bob.pubkey(), + &[&bob], + ) .await .unwrap(); let state = token @@ -585,7 +493,11 @@ async fn ct_enable_disable_non_confidential_credits() { ); token - .confidential_transfer_enable_non_confidential_credits(&bob_meta.token_account, &bob) + .confidential_transfer_enable_non_confidential_credits( + &bob_meta.token_account, + &bob.pubkey(), + &[&bob], + ) .await .unwrap(); let state = token @@ -609,50 +521,59 @@ async fn ct_enable_disable_non_confidential_credits() { .unwrap(); } -#[cfg(feature = "proof-program")] #[tokio::test] -async fn ct_new_account_is_empty() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_empty_account() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; + + // newly created confidential transfer account should hold no balance and therefore, + // immediately closable context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await .unwrap(); let TokenContext { token, alice, .. } = context.token_context.unwrap(); + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice).await; token - .confidential_transfer_empty_account(&alice_meta.token_account, &alice) + .confidential_transfer_empty_account( + &alice_meta.token_account, + &alice.pubkey(), + None, + None, + &alice_meta.elgamal_keypair, + &[&alice], + ) .await .unwrap(); } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_deposit() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_deposit() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await @@ -665,7 +586,7 @@ async fn ct_deposit() { decimals, .. } = context.token_context.unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice).await; + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, Some(2), false, false).await; token .mint_to( @@ -690,19 +611,25 @@ async fn ct_deposit() { assert_eq!(extension.actual_pending_balance_credit_counter, 0.into()); assert_eq!( extension.pending_balance_lo, - zk_token_elgamal::pod::ElGamalCiphertext::zeroed() + pod::ElGamalCiphertext::zeroed() ); assert_eq!( extension.pending_balance_hi, - zk_token_elgamal::pod::ElGamalCiphertext::zeroed() + pod::ElGamalCiphertext::zeroed() ); assert_eq!( extension.available_balance, - zk_token_elgamal::pod::ElGamalCiphertext::zeroed() + pod::ElGamalCiphertext::zeroed() ); token - .confidential_transfer_deposit(&alice_meta.token_account, &alice, 65537, decimals) + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 65537, + decimals, + &[&alice], + ) .await .unwrap(); @@ -730,13 +657,51 @@ async fn ct_deposit() { ) .await; + // deposit zero amount token - .confidential_transfer_deposit(&alice_meta.token_account, &alice, 0, decimals) + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 0, + decimals, + &[&alice], + ) + .await + .unwrap(); + + token + .confidential_transfer_apply_pending_balance( + &alice_meta.token_account, + &alice.pubkey(), + None, + alice_meta.elgamal_keypair.secret(), + &alice_meta.aes_key, + &[&alice], + ) + .await + .unwrap(); + + // try to deposit over maximum allowed value + let illegal_amount = MAXIMUM_DEPOSIT_TRANSFER_AMOUNT.checked_add(1).unwrap(); + + token + .mint_to( + &alice_meta.token_account, + &mint_authority.pubkey(), + illegal_amount, + &[&mint_authority], + ) .await .unwrap(); let err = token - .confidential_transfer_deposit(&alice_meta.token_account, &alice, 0, decimals) + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + illegal_amount, + decimals, + &[&alice], + ) .await .unwrap_err(); @@ -745,45 +710,86 @@ async fn ct_deposit() { TokenClientError::Client(Box::new(TransportError::TransactionError( TransactionError::InstructionError( 0, - InstructionError::Custom( - TokenError::MaximumPendingBalanceCreditCounterExceeded as u32 - ), + InstructionError::Custom(TokenError::MaximumDepositAmountExceeded as u32), ) ))) ); + // deposit maximum allowed value + token + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + MAXIMUM_DEPOSIT_TRANSFER_AMOUNT, + decimals, + &[&alice], + ) + .await + .unwrap(); + + // maximum pending balance credits exceeded token - .confidential_transfer_apply_pending_balance(&alice_meta.token_account, &alice, 0, 65537, 2) + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 0, + decimals, + &[&alice], + ) .await .unwrap(); + let err = token + .confidential_transfer_deposit( + &alice_meta.token_account, + &alice.pubkey(), + 1, + decimals, + &[&alice], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom( + TokenError::MaximumPendingBalanceCreditCounterExceeded as u32 + ), + ) + ))) + ); + let state = token .get_account_info(&alice_meta.token_account) .await .unwrap(); + assert_eq!(state.base.amount, 1); let extension = state .get_extension::() .unwrap(); - assert_eq!(extension.pending_balance_credit_counter, 0.into()); + assert_eq!(extension.pending_balance_credit_counter, 2.into()); assert_eq!(extension.expected_pending_balance_credit_counter, 2.into()); assert_eq!(extension.actual_pending_balance_credit_counter, 2.into()); } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_withdraw() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_withdraw() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await @@ -796,61 +802,75 @@ async fn ct_withdraw() { decimals, .. } = context.token_context.unwrap(); - - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 42, decimals) - .await; + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; let state = token .get_account_info(&alice_meta.token_account) .await .unwrap(); - let extension = state - .get_extension::() - .unwrap(); assert_eq!(state.base.amount, 0); + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 42, + decryptable_available_balance: 42, + }, + ) + .await; + // withdraw zero amount token .confidential_transfer_withdraw( &alice_meta.token_account, - &alice, - 21, - 42, - &extension.available_balance.try_into().unwrap(), + &alice.pubkey(), + None, + 0, decimals, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], ) .await .unwrap(); - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - assert_eq!(state.base.amount, 21); - alice_meta .check_balances( &token, ConfidentialTokenAccountBalances { pending_balance_lo: 0, pending_balance_hi: 0, - available_balance: 21, - decryptable_available_balance: 21, + available_balance: 42, + decryptable_available_balance: 42, }, ) .await; + // withdraw entire balance token .confidential_transfer_withdraw( &alice_meta.token_account, - &alice, - 21, - 21, - &extension.available_balance.try_into().unwrap(), + &alice.pubkey(), + None, + 42, decimals, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], ) .await .unwrap(); @@ -860,7 +880,6 @@ async fn ct_withdraw() { .await .unwrap(); assert_eq!(state.base.amount, 42); - alice_meta .check_balances( &token, @@ -873,30 +892,52 @@ async fn ct_withdraw() { ) .await; + // attempt to withdraw without enough funds + let err = token + .confidential_transfer_withdraw( + &alice_meta.token_account, + &alice.pubkey(), + None, + 1, + decimals, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + ) + .await + .unwrap_err(); + + assert_eq!(err, TokenClientError::ProofGeneration); + token - .confidential_transfer_empty_account(&alice_meta.token_account, &alice) + .confidential_transfer_empty_account( + &alice_meta.token_account, + &alice.pubkey(), + None, + None, + &alice_meta.elgamal_keypair, + &[&alice], + ) .await .unwrap(); } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_transfer() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_transfer() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await @@ -910,30 +951,35 @@ async fn ct_transfer() { decimals, .. } = context.token_context.unwrap(); - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 42, decimals) - .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; + + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, Some(2), false, false).await; // Self-transfer of 0 tokens token .confidential_transfer_transfer( &alice_meta.token_account, &alice_meta.token_account, - &alice, - 0, // amount - 42, - &extension.available_balance.try_into().unwrap(), - &alice_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + &alice.pubkey(), + None, + 0, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], ) .await .unwrap(); @@ -950,25 +996,20 @@ async fn ct_transfer() { ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - // Self-transfer of N tokens token .confidential_transfer_transfer( &alice_meta.token_account, &alice_meta.token_account, - &alice, - 42, // amount + &alice.pubkey(), + None, 42, - &extension.available_balance.try_into().unwrap(), - &alice_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], ) .await .unwrap(); @@ -985,43 +1026,16 @@ async fn ct_transfer() { ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - - let err = token - .confidential_transfer_transfer( - &alice_meta.token_account, + token + .confidential_transfer_apply_pending_balance( &alice_meta.token_account, - &alice, - 0, // amount - 0, - &extension.available_balance.try_into().unwrap(), - &alice_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + &alice.pubkey(), + None, + alice_meta.elgamal_keypair.secret(), + &alice_meta.aes_key, + &[&alice], ) .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom( - TokenError::MaximumPendingBalanceCreditCounterExceeded as u32 - ), - ) - ))) - ); - - token - .confidential_transfer_apply_pending_balance(&alice_meta.token_account, &alice, 0, 42, 2) - .await .unwrap(); alice_meta @@ -1036,24 +1050,19 @@ async fn ct_transfer() { ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - token .confidential_transfer_transfer( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 42, // amount + &alice.pubkey(), + None, 42, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], ) .await .unwrap(); @@ -1070,13 +1079,49 @@ async fn ct_transfer() { ) .await; + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 42, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + token - .confidential_transfer_empty_account(&alice_meta.token_account, &alice) + .confidential_transfer_transfer( + &bob_meta.token_account, + &bob_meta.token_account, + &bob.pubkey(), + None, + 0, + None, + &bob_meta.elgamal_keypair, + &bob_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&bob], + ) .await .unwrap(); let err = token - .confidential_transfer_empty_account(&bob_meta.token_account, &bob) + .confidential_transfer_transfer( + &bob_meta.token_account, + &bob_meta.token_account, + &bob.pubkey(), + None, + 0, + None, + &bob_meta.elgamal_keypair, + &bob_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&bob], + ) .await .unwrap_err(); @@ -1085,33 +1130,30 @@ async fn ct_transfer() { TokenClientError::Client(Box::new(TransportError::TransactionError( TransactionError::InstructionError( 0, - InstructionError::Custom(TokenError::ConfidentialTransferAccountHasBalance as u32) + InstructionError::Custom( + TokenError::MaximumPendingBalanceCreditCounterExceeded as u32 + ), ) ))) ); + token + .confidential_transfer_apply_pending_balance( + &bob_meta.token_account, + &bob.pubkey(), + None, + bob_meta.elgamal_keypair.secret(), + &bob_meta.aes_key, + &[&bob], + ) + .await + .unwrap(); + bob_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 42, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - token - .confidential_transfer_apply_pending_balance(&bob_meta.token_account, &bob, 0, 42, 1) - .await - .unwrap(); - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, + pending_balance_lo: 0, pending_balance_hi: 0, available_balance: 42, decryptable_available_balance: 42, @@ -1120,32 +1162,39 @@ async fn ct_transfer() { .await; } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_transfer_with_fee() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - ct_mint_withdraw_withheld_authority_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_transfer_with_fee() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(Pubkey::new_unique()), - withdraw_withheld_authority: Some(Pubkey::new_unique()), + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, }, ]) .await @@ -1160,32 +1209,37 @@ async fn ct_transfer_with_fee() { .. } = context.token_context.unwrap(); - let epoch_info = test_epoch_info(); - - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 100, decimals) - .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await; + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + true, + &mint_authority, + 100, + decimals, + ) + .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; // Self-transfer of 0 tokens token - .confidential_transfer_transfer( + .confidential_transfer_transfer_with_fee( &alice_meta.token_account, &alice_meta.token_account, - &alice, - 0, // amount - 100, - &extension.available_balance.try_into().unwrap(), - &alice_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + &alice.pubkey(), + None, + 0, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &[&alice], ) .await .unwrap(); @@ -1202,25 +1256,23 @@ async fn ct_transfer_with_fee() { ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - // Self-transfers does not incur a fee token - .confidential_transfer_transfer( + .confidential_transfer_transfer_with_fee( &alice_meta.token_account, &alice_meta.token_account, - &alice, - 100, // amount + &alice.pubkey(), + None, 100, - &extension.available_balance.try_into().unwrap(), - &alice_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &[&alice], ) .await .unwrap(); @@ -1238,7 +1290,14 @@ async fn ct_transfer_with_fee() { .await; token - .confidential_transfer_apply_pending_balance(&alice_meta.token_account, &alice, 0, 100, 2) + .confidential_transfer_apply_pending_balance( + &alice_meta.token_account, + &alice.pubkey(), + None, + alice_meta.elgamal_keypair.secret(), + &alice_meta.aes_key, + &[&alice], + ) .await .unwrap(); @@ -1254,26 +1313,22 @@ async fn ct_transfer_with_fee() { ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - token .confidential_transfer_transfer_with_fee( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 100, + &alice.pubkey(), + None, 100, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), - &ct_mint_withdraw_withheld_authority_elgamal_keypair.public, - &epoch_info, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &[&alice], ) .await .unwrap(); @@ -1290,14 +1345,27 @@ async fn ct_transfer_with_fee() { ) .await; - // Alice account cannot be closed since there are withheld fees from self-transfer token - .confidential_transfer_empty_account(&alice_meta.token_account, &alice) + .confidential_transfer_empty_account( + &alice_meta.token_account, + &alice.pubkey(), + None, + None, + &alice_meta.elgamal_keypair, + &[&alice], + ) .await .unwrap(); let err = token - .confidential_transfer_empty_account(&bob_meta.token_account, &bob) + .confidential_transfer_empty_account( + &bob_meta.token_account, + &bob.pubkey(), + None, + None, + &bob_meta.elgamal_keypair, + &[&bob], + ) .await .unwrap_err(); @@ -1324,7 +1392,14 @@ async fn ct_transfer_with_fee() { .await; token - .confidential_transfer_apply_pending_balance(&bob_meta.token_account, &bob, 0, 97, 1) + .confidential_transfer_apply_pending_balance( + &bob_meta.token_account, + &bob.pubkey(), + None, + bob_meta.elgamal_keypair.secret(), + &bob_meta.aes_key, + &[&bob], + ) .await .unwrap(); @@ -1341,34 +1416,21 @@ async fn ct_transfer_with_fee() { .await; } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_withdraw_withheld_tokens_from_mint() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - ct_mint_withdraw_withheld_authority_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); - - let ct_mint_withdraw_withheld_authority = Keypair::new(); +async fn confidential_transfer_transfer_memo() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(Pubkey::new_unique()), - withdraw_withheld_authority: Some(ct_mint_withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, ]) .await @@ -1383,107 +1445,63 @@ async fn ct_withdraw_withheld_tokens_from_mint() { .. } = context.token_context.unwrap(); - let epoch_info = test_epoch_info(); - - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 100, decimals) - .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await; - - token - .confidential_transfer_withdraw_withheld_tokens_from_mint_with_key( - &ct_mint_withdraw_withheld_authority, - &alice_meta.token_account, - &alice_meta.elgamal_keypair.public, - 0_u64, - &ct_mint.withheld_amount.try_into().unwrap(), - &ct_mint_withdraw_withheld_authority_elgamal_keypair, - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 100, - decryptable_available_balance: 100, - }, - ) - .await; - - check_withheld_amount_in_mint( + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( &token, - &ct_mint_withdraw_withheld_authority_elgamal_keypair, - 0, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, ) .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, false).await; - // Test fee is 2.5% so the withheld fees should be 3 - token - .confidential_transfer_transfer_with_fee( + // transfer without memo + let err = token + .confidential_transfer_transfer( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 100, - 100, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), - &ct_mint_withdraw_withheld_authority_elgamal_keypair.public, - &epoch_info, + &alice.pubkey(), + None, + 42, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], ) .await - .unwrap(); - - let state = token - .get_account_info(&bob_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + .unwrap_err(); assert_eq!( - extension - .withheld_amount - .decrypt(&ct_mint_withdraw_withheld_authority_elgamal_keypair.secret), - Some(3), + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::NoMemo as u32) + ) + ))) ); + // transfer with memo token - .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) - .await - .unwrap(); - - check_withheld_amount_in_mint( - &token, - &ct_mint_withdraw_withheld_authority_elgamal_keypair, - 3, - ) - .await; - - let state = token.get_mint_info().await.unwrap(); - let ct_mint = state.get_extension::().unwrap(); - - token - .confidential_transfer_withdraw_withheld_tokens_from_mint_with_key( - &ct_mint_withdraw_withheld_authority, + .with_memo("🦖", vec![alice.pubkey()]) + .confidential_transfer_transfer( &alice_meta.token_account, - &alice_meta.elgamal_keypair.public, - 3_u64, - &ct_mint.withheld_amount.try_into().unwrap(), - &ct_mint_withdraw_withheld_authority_elgamal_keypair, + &bob_meta.token_account, + &alice.pubkey(), + None, + 42, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], ) .await .unwrap(); @@ -1492,7 +1510,19 @@ async fn ct_withdraw_withheld_tokens_from_mint() { .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 3, + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 42, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, @@ -1501,34 +1531,39 @@ async fn ct_withdraw_withheld_tokens_from_mint() { .await; } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn ct_withdraw_withheld_tokens_from_accounts() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - ct_mint_withdraw_withheld_authority_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_transfer_with_fee_and_memo() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - let ct_mint_withdraw_withheld_authority = Keypair::new(); + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(Pubkey::new_unique()), - withdraw_withheld_authority: Some(ct_mint_withdraw_withheld_authority.pubkey()), + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, }, ]) .await @@ -1543,71 +1578,76 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { .. } = context.token_context.unwrap(); - let epoch_info = test_epoch_info(); - - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 100, decimals) - .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await; + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + true, + &mint_authority, + 100, + decimals, + ) + .await; - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, true).await; - // Test fee is 2.5% so the withheld fees should be 3 - token + let err = token .confidential_transfer_transfer_with_fee( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 100, + &alice.pubkey(), + None, 100, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), - &ct_mint_withdraw_withheld_authority_elgamal_keypair.public, - &epoch_info, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &[&alice], ) .await - .unwrap(); - - let state = token - .get_account_info(&bob_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); + .unwrap_err(); assert_eq!( - extension - .withheld_amount - .decrypt(&ct_mint_withdraw_withheld_authority_elgamal_keypair.secret), - Some(3), + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::NoMemo as u32) + ) + ))) ); token - .confidential_transfer_withdraw_withheld_tokens_from_accounts_with_key( - &ct_mint_withdraw_withheld_authority, + .with_memo("🦖", vec![alice.pubkey()]) + .confidential_transfer_transfer_with_fee( &alice_meta.token_account, - &alice_meta.elgamal_keypair.public, - 3_u64, - &extension.withheld_amount.try_into().unwrap(), - &ct_mint_withdraw_withheld_authority_elgamal_keypair, - &[&bob_meta.token_account], + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &[&alice], ) .await .unwrap(); - bob_meta + alice_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 97, + pending_balance_lo: 0, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, @@ -1615,11 +1655,11 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { ) .await; - alice_meta + bob_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 3, + pending_balance_lo: 97, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, @@ -1628,88 +1668,977 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { .await; } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] #[tokio::test] -async fn ct_transfer_memo() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_configure_token_account_with_proof_context() { + let authority = Keypair::new(); + let auto_approve_new_accounts = false; + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: None, }, ]) .await .unwrap(); let TokenContext { - token, - alice, + token, alice, bob, .. + } = context.token_context.unwrap(); + + let token_account_keypair = Keypair::new(); + token + .create_auxiliary_token_account_with_extension_space( + &token_account_keypair, + &alice.pubkey(), + vec![ExtensionType::ConfidentialTransferAccount], + ) + .await + .unwrap(); + let token_account = token_account_keypair.pubkey(); + + let elgamal_keypair = + ElGamalKeypair::new_from_signer(&alice, &token_account.to_bytes()).unwrap(); + let aes_key = AeKey::new_from_signer(&alice, &token_account.to_bytes()).unwrap(); + + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyPubkeyValidity; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let proof_data = + confidential_transfer::instruction::PubkeyValidityData::new(&elgamal_keypair).unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + token + .confidential_transfer_configure_token_account( + &token_account, + &alice.pubkey(), + Some(&context_state_account.pubkey()), + None, + &elgamal_keypair, + &aes_key, + &[&alice], + ) + .await + .unwrap(); + + let elgamal_pubkey = (*elgamal_keypair.pubkey()).into(); + + let state = token.get_account_info(&token_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + assert!(!bool::from(&extension.approved)); + assert!(bool::from(&extension.allow_confidential_credits)); + assert_eq!(extension.elgamal_pubkey, elgamal_pubkey); + assert_eq!( + aes_key + .decrypt(&(extension.decryptable_available_balance.try_into().unwrap())) + .unwrap(), + 0 + ); + + // attempt to create an account with a wrong proof type context state + let token_account_keypair = Keypair::new(); + token + .create_auxiliary_token_account_with_extension_space( + &token_account_keypair, + &bob.pubkey(), + vec![ExtensionType::ConfidentialTransferAccount], + ) + .await + .unwrap(); + let token_account = token_account_keypair.pubkey(); + + let elgamal_keypair = ElGamalKeypair::new_from_signer(&bob, &token_account.to_bytes()).unwrap(); + let aes_key = AeKey::new_from_signer(&bob, &token_account.to_bytes()).unwrap(); + + let context_state_account = Keypair::new(); + + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyZeroBalance; + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let ciphertext = elgamal_keypair.pubkey().encrypt(0_u64); + let proof_data = confidential_transfer::instruction::ZeroBalanceProofData::new( + &elgamal_keypair, + &ciphertext, + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + let err = token + .confidential_transfer_configure_token_account( + &token_account, + &bob.pubkey(), + Some(&context_state_account.pubkey()), + None, + &elgamal_keypair, + &aes_key, + &[&bob], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::InvalidArgument,) + ))) + ); +} + +#[tokio::test] +async fn confidential_transfer_empty_account_with_proof_context() { + let authority = Keypair::new(); + let auto_approve_new_accounts = false; + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: None, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, alice, bob, .. + } = context.token_context.unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyZeroBalance; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let proof_data = confidential_transfer::instruction::ZeroBalanceProofData::new( + &alice_meta.elgamal_keypair, + &ElGamalCiphertext::default(), + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + token + .confidential_transfer_empty_account( + &alice_meta.token_account, + &alice.pubkey(), + Some(&context_state_account.pubkey()), + None, + &alice_meta.elgamal_keypair, + &[&alice], + ) + .await + .unwrap(); + + // attempt to create an account with a wrong proof type context state + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, false).await; + let context_state_account = Keypair::new(); + + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyPubkeyValidity; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let proof_data = + confidential_transfer::instruction::PubkeyValidityData::new(&bob_meta.elgamal_keypair) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + let err = token + .confidential_transfer_empty_account( + &bob_meta.token_account, + &bob.pubkey(), + Some(&context_state_account.pubkey()), + None, + &bob_meta.elgamal_keypair, + &[&bob], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::InvalidArgument,) + ))) + ); +} + +#[tokio::test] +async fn confidential_transfer_withdraw_with_proof_context() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: None, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; + + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyWithdraw; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let state = token + .get_account_info(&alice_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let current_ciphertext = extension.available_balance.try_into().unwrap(); + + let proof_data = confidential_transfer::instruction::WithdrawData::new( + 0, + &alice_meta.elgamal_keypair, + 42, + ¤t_ciphertext, + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + token + .confidential_transfer_withdraw( + &alice_meta.token_account, + &alice.pubkey(), + Some(&context_state_account.pubkey()), + 0, + decimals, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + ) + .await + .unwrap(); + + // attempt to create an account with a wrong proof type context state + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, false).await; + let context_state_account = Keypair::new(); + + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyPubkeyValidity; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let proof_data = + confidential_transfer::instruction::PubkeyValidityData::new(&bob_meta.elgamal_keypair) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + let err = token + .confidential_transfer_withdraw( + &bob_meta.token_account, + &bob.pubkey(), + Some(&context_state_account.pubkey()), + 0, + decimals, + None, + &bob_meta.elgamal_keypair, + &bob_meta.aes_key, + &[&bob], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::InvalidArgument,) + ))) + ); +} + +#[tokio::test] +async fn confidential_transfer_transfer_with_proof_context() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, bob, mint_authority, decimals, .. } = context.token_context.unwrap(); - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 42, decimals) - .await; - let bob_meta = - ConfidentialTokenAccountMeta::new_with_required_memo_transfers(&token, &bob).await; - let state = token - .get_account_info(&alice_meta.token_account) + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; + + let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &bob, + None, + false, + false, + &mint_authority, + 0, + decimals, + ) + .await; + + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyTransfer; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let state = token + .get_account_info(&alice_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let current_available_balance = extension.available_balance.try_into().unwrap(); + + let proof_data = confidential_transfer::instruction::TransferData::new( + 42, + (42, ¤t_available_balance), + &alice_meta.elgamal_keypair, + ( + bob_meta.elgamal_keypair.pubkey(), + auditor_elgamal_keypair.pubkey(), + ), + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + token + .confidential_transfer_transfer( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + Some(&context_state_account.pubkey()), + 42, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], + ) + .await + .unwrap(); + + // attempt to create an account with a wrong proof type context state + let context_state_account = Keypair::new(); + + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyWithdraw; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let state = token + .get_account_info(&alice_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let current_ciphertext = extension.available_balance.try_into().unwrap(); + + let proof_data = confidential_transfer::instruction::WithdrawData::new( + 0, + &alice_meta.elgamal_keypair, + 0, + ¤t_ciphertext, + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + let err = token + .confidential_transfer_transfer( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + Some(&context_state_account.pubkey()), + 0, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &[&alice], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::InvalidArgument,) + ))) + ) +} + +#[tokio::test] +async fn confidential_transfer_transfer_with_split_proof_context() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; + + let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &bob, + None, + false, + false, + &mint_authority, + 0, + decimals, + ) + .await; + + let state = token + .get_account_info(&alice_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let ( + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + ) = transfer_account_info + .generate_split_transfer_proof_data( + 42, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + ) + .unwrap(); + + let context_state_authority = Keypair::new(); + let equality_proof_context_state_account = Keypair::new(); + let ciphertext_validity_proof_context_state_account = Keypair::new(); + let range_proof_context_state_account = Keypair::new(); + + let transfer_context_state_accounts = TransferSplitContextStateAccounts { + equality_proof: &equality_proof_context_state_account.pubkey(), + ciphertext_validity_proof: &ciphertext_validity_proof_context_state_account.pubkey(), + range_proof: &range_proof_context_state_account.pubkey(), + authority: &context_state_authority.pubkey(), + no_op_on_uninitialized_split_context_state: false, + close_split_context_state_accounts: None, + }; + + // create context state accounts + token + .create_equality_and_ciphertext_validity_proof_context_states_for_transfer( + transfer_context_state_accounts, + &equality_proof_data, + &ciphertext_validity_proof_data, + &[ + &equality_proof_context_state_account, + &ciphertext_validity_proof_context_state_account, + ], + ) .await .unwrap(); - let extension = state - .get_extension::() + + token + .create_range_proof_context_state_for_transfer( + transfer_context_state_accounts, + &range_proof_data, + &range_proof_context_state_account, + ) + .await .unwrap(); - // transfer without memo - let err = token - .confidential_transfer_transfer( + // create token22 confidential transfer instruction + token + .confidential_transfer_transfer_with_split_proofs( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 42, // amount + &alice.pubkey(), + transfer_context_state_accounts, 42, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + None, + &alice_meta.aes_key, + &source_decrypt_handles, + &[&alice], ) .await - .unwrap_err(); + .unwrap(); - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::NoMemo as u32) - ) - ))) - ); + // close context state accounts + token + .confidential_transfer_close_context_state( + &equality_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); - // transfer with memo token - .with_memo("🦖", vec![alice.pubkey()]) - .confidential_transfer_transfer( + .confidential_transfer_close_context_state( + &ciphertext_validity_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state( + &range_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + // check balances + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 42, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; +} + +#[tokio::test] +async fn confidential_transfer_transfer_with_split_proof_contexts_in_parallel() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + false, + &mint_authority, + 42, + decimals, + ) + .await; + + let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &bob, + None, + false, + false, + &mint_authority, + 0, + decimals, + ) + .await; + + let context_state_authority = Keypair::new(); + let equality_proof_context_state_account = Keypair::new(); + let ciphertext_validity_proof_context_state_account = Keypair::new(); + let range_proof_context_state_account = Keypair::new(); + + let transfer_context_state_accounts = TransferSplitContextStateAccounts { + equality_proof: &equality_proof_context_state_account.pubkey(), + ciphertext_validity_proof: &ciphertext_validity_proof_context_state_account.pubkey(), + range_proof: &range_proof_context_state_account.pubkey(), + authority: &context_state_authority.pubkey(), + no_op_on_uninitialized_split_context_state: true, + close_split_context_state_accounts: None, + }; + + let equality_and_ciphertext_proof_signers = vec![ + &alice, + &equality_proof_context_state_account, + &ciphertext_validity_proof_context_state_account, + ]; + let range_proof_signers = vec![&alice, &range_proof_context_state_account]; + token + .confidential_transfer_transfer_with_split_proofs_in_parallel( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 42, // amount + &alice.pubkey(), + transfer_context_state_accounts, 42, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + &equality_and_ciphertext_proof_signers, + &range_proof_signers, ) .await .unwrap(); @@ -1739,32 +2668,38 @@ async fn ct_transfer_memo() { .await; } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] #[tokio::test] -async fn ct_transfer_with_fee_memo() { - let ConfidentialTransferMintWithKeypairs { - ct_mint, - ct_mint_transfer_auditor_elgamal_keypair, - ct_mint_withdraw_withheld_authority_elgamal_keypair, - .. - } = ConfidentialTransferMintWithKeypairs::new(); +async fn confidential_transfer_transfer_with_fee_and_split_proof_context() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(Pubkey::new_unique()), - withdraw_withheld_authority: Some(Pubkey::new_unique()), + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: ct_mint.authority.into(), - auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_elgamal_pubkey: ct_mint.auditor_elgamal_pubkey.into(), - withdraw_withheld_authority_elgamal_pubkey: ct_mint - .withdraw_withheld_authority_elgamal_pubkey - .into(), + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, }, ]) .await @@ -1779,13 +2714,19 @@ async fn ct_transfer_with_fee_memo() { .. } = context.token_context.unwrap(); - let epoch_info = test_epoch_info(); + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + true, + &mint_authority, + 100, + decimals, + ) + .await; - let alice_meta = - ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 100, decimals) - .await; - let bob_meta = - ConfidentialTokenAccountMeta::new_with_required_memo_transfers(&token, &bob).await; + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; let state = token .get_account_info(&alice_meta.token_account) @@ -1794,50 +2735,303 @@ async fn ct_transfer_with_fee_memo() { let extension = state .get_extension::() .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let current_source_available_balance = + transfer_account_info.available_balance.try_into().unwrap(); + let current_decryptable_available_balance = transfer_account_info + .decryptable_available_balance + .try_into() + .unwrap(); + + let fee_parameters = FeeParameters { + fee_rate_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }; + + let ( + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + fee_sigma_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + ) = transfer_with_fee_split_proof_data( + ¤t_source_available_balance, + ¤t_decryptable_available_balance, + 100, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + &fee_parameters, + ) + .unwrap(); + + let context_state_authority = Keypair::new(); + let equality_proof_context_state_account = Keypair::new(); + let transfer_amount_ciphertext_validity_proof_context_state_account = Keypair::new(); + let fee_sigma_proof_context_state_account = Keypair::new(); + let fee_ciphertext_validity_proof_context_state_account = Keypair::new(); + let range_proof_context_state_account = Keypair::new(); + + let transfer_context_state_accounts = TransferWithFeeSplitContextStateAccounts { + equality_proof: &equality_proof_context_state_account.pubkey(), + transfer_amount_ciphertext_validity_proof: + &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), + fee_sigma_proof: &fee_sigma_proof_context_state_account.pubkey(), + fee_ciphertext_validity_proof: &fee_ciphertext_validity_proof_context_state_account + .pubkey(), + range_proof: &range_proof_context_state_account.pubkey(), + authority: &context_state_authority.pubkey(), + no_op_on_uninitialized_split_context_state: false, + close_split_context_state_accounts: None, + }; + + // create context state accounts + token + .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee( + transfer_context_state_accounts, + &equality_proof_data, + &transfer_amount_ciphertext_validity_proof_data, + &[ + &equality_proof_context_state_account, + &transfer_amount_ciphertext_validity_proof_context_state_account, + ], + ) + .await + .unwrap(); - let err = token - .confidential_transfer_transfer_with_fee( + token + .create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee( + transfer_context_state_accounts, + &fee_sigma_proof_data, + &fee_ciphertext_validity_proof_data, + &[ + &fee_sigma_proof_context_state_account, + &fee_ciphertext_validity_proof_context_state_account, + ], + ) + .await + .unwrap(); + + token + .create_range_proof_context_state_for_transfer_with_fee( + transfer_context_state_accounts, + &range_proof_data, + &[&range_proof_context_state_account], + ) + .await + .unwrap(); + + // create token22 confidential transfer instruction + token + .confidential_transfer_transfer_with_fee_and_split_proofs( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 100, + &alice.pubkey(), + transfer_context_state_accounts, 100, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), - &ct_mint_withdraw_withheld_authority_elgamal_keypair.public, - &epoch_info, + None, + &alice_meta.aes_key, + &source_decrypt_handles, + &[&alice], ) .await - .unwrap_err(); + .unwrap(); - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::NoMemo as u32) - ) - ))) - ); + // close context state accounts + token + .confidential_transfer_close_context_state( + &equality_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); token - .with_memo("🦖", vec![alice.pubkey()]) - .confidential_transfer_transfer_with_fee( + .confidential_transfer_close_context_state( + &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state( + &fee_sigma_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state( + &fee_ciphertext_validity_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state( + &range_proof_context_state_account.pubkey(), + &alice_meta.token_account, + &context_state_authority.pubkey(), + &[&context_state_authority], + ) + .await + .unwrap(); + + // check balances + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 97, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; +} + +#[tokio::test] +async fn confidential_transfer_transfer_with_fee_and_split_proof_context_in_parallel() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( + &token, + &alice, + None, + false, + true, + &mint_authority, + 100, + decimals, + ) + .await; + + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; + + let context_state_authority = Keypair::new(); + let equality_proof_context_state_account = Keypair::new(); + let transfer_amount_ciphertext_validity_proof_context_state_account = Keypair::new(); + let fee_sigma_proof_context_state_account = Keypair::new(); + let fee_ciphertext_validity_proof_context_state_account = Keypair::new(); + let range_proof_context_state_account = Keypair::new(); + + let transfer_context_state_accounts = TransferWithFeeSplitContextStateAccounts { + equality_proof: &equality_proof_context_state_account.pubkey(), + transfer_amount_ciphertext_validity_proof: + &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), + fee_sigma_proof: &fee_sigma_proof_context_state_account.pubkey(), + fee_ciphertext_validity_proof: &fee_ciphertext_validity_proof_context_state_account + .pubkey(), + range_proof: &range_proof_context_state_account.pubkey(), + authority: &context_state_authority.pubkey(), + no_op_on_uninitialized_split_context_state: true, + close_split_context_state_accounts: None, + }; + + let equality_and_ciphertext_proof_signers = vec![ + &alice, + &equality_proof_context_state_account, + &transfer_amount_ciphertext_validity_proof_context_state_account, + ]; + let fee_sigma_proof_signers = vec![ + &alice, + &fee_sigma_proof_context_state_account, + &fee_ciphertext_validity_proof_context_state_account, + ]; + let range_proof_signers = vec![&alice, &range_proof_context_state_account]; + token + .confidential_transfer_transfer_with_fee_and_split_proofs_in_parallel( &alice_meta.token_account, &bob_meta.token_account, - &alice, - 100, + &alice.pubkey(), + transfer_context_state_accounts, 100, - &extension.available_balance.try_into().unwrap(), - &bob_meta.elgamal_keypair.public, - Some(ct_mint_transfer_auditor_elgamal_keypair.public), - &ct_mint_withdraw_withheld_authority_elgamal_keypair.public, - &epoch_info, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + &equality_and_ciphertext_proof_signers, + &fee_sigma_proof_signers, + &range_proof_signers, ) .await .unwrap(); + // check balances alice_meta .check_balances( &token, diff --git a/token/program-2022-test/tests/confidential_transfer_fee.rs b/token/program-2022-test/tests/confidential_transfer_fee.rs new file mode 100644 index 00000000000..306d5332303 --- /dev/null +++ b/token/program-2022-test/tests/confidential_transfer_fee.rs @@ -0,0 +1,1233 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + program_test::{TestContext, TokenContext}, + solana_program_test::tokio, + solana_sdk::{ + instruction::InstructionError, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + transaction::{Transaction, TransactionError}, + transport::TransportError, + }, + spl_token_2022::{ + error::TokenError, + extension::{ + confidential_transfer::{ConfidentialTransferAccount, ConfidentialTransferMint}, + confidential_transfer_fee::{ + account_info::WithheldTokensInfo, ConfidentialTransferFeeAmount, + ConfidentialTransferFeeConfig, + }, + transfer_fee::TransferFee, + BaseStateWithExtensions, ExtensionType, + }, + instruction, + solana_zk_token_sdk::{ + encryption::{auth_encryption::*, elgamal::*}, + zk_token_elgamal::pod::{self, Zeroable}, + zk_token_proof_instruction::*, + zk_token_proof_program, + zk_token_proof_state::ProofContextState, + }, + }, + spl_token_client::{ + client::{SendTransaction, SimulateTransaction}, + token::{ExtensionInitializationParams, Token, TokenError as TokenClientError}, + }, + std::{convert::TryInto, mem::size_of}, +}; + +#[cfg(feature = "zk-ops")] +const TEST_MAXIMUM_FEE: u64 = 100; +#[cfg(feature = "zk-ops")] +const TEST_FEE_BASIS_POINTS: u16 = 250; + +struct ConfidentialTokenAccountMeta { + token_account: Pubkey, + elgamal_keypair: ElGamalKeypair, + aes_key: AeKey, +} + +impl ConfidentialTokenAccountMeta { + async fn new( + token: &Token, + owner: &Keypair, + mint_authority: &Keypair, + amount: u64, + decimals: u8, + ) -> Self + where + T: SendTransaction + SimulateTransaction, + { + let token_account_keypair = Keypair::new(); + let extensions = vec![ + ExtensionType::ConfidentialTransferAccount, + ExtensionType::ConfidentialTransferFeeAmount, + ]; + + token + .create_auxiliary_token_account_with_extension_space( + &token_account_keypair, + &owner.pubkey(), + extensions, + ) + .await + .unwrap(); + let token_account = token_account_keypair.pubkey(); + + let elgamal_keypair = + ElGamalKeypair::new_from_signer(owner, &token_account.to_bytes()).unwrap(); + let aes_key = AeKey::new_from_signer(owner, &token_account.to_bytes()).unwrap(); + + token + .confidential_transfer_configure_token_account( + &token_account, + &owner.pubkey(), + None, + None, + &elgamal_keypair, + &aes_key, + &[owner], + ) + .await + .unwrap(); + + token + .mint_to( + &token_account, + &mint_authority.pubkey(), + amount, + &[mint_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_deposit( + &token_account, + &owner.pubkey(), + amount, + decimals, + &[owner], + ) + .await + .unwrap(); + + token + .confidential_transfer_apply_pending_balance( + &token_account, + &owner.pubkey(), + None, + elgamal_keypair.secret(), + &aes_key, + &[owner], + ) + .await + .unwrap(); + + Self { + token_account, + elgamal_keypair, + aes_key, + } + } + + #[cfg(feature = "zk-ops")] + async fn check_balances(&self, token: &Token, expected: ConfidentialTokenAccountBalances) + where + T: SendTransaction + SimulateTransaction, + { + let state = token.get_account_info(&self.token_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + + assert_eq!( + extension + .pending_balance_lo + .decrypt(self.elgamal_keypair.secret()) + .unwrap(), + expected.pending_balance_lo, + ); + assert_eq!( + extension + .pending_balance_hi + .decrypt(self.elgamal_keypair.secret()) + .unwrap(), + expected.pending_balance_hi, + ); + assert_eq!( + extension + .available_balance + .decrypt(self.elgamal_keypair.secret()) + .unwrap(), + expected.available_balance, + ); + assert_eq!( + self.aes_key + .decrypt(&extension.decryptable_available_balance.try_into().unwrap()) + .unwrap(), + expected.decryptable_available_balance, + ); + } +} + +#[cfg(feature = "zk-ops")] +struct ConfidentialTokenAccountBalances { + pending_balance_lo: u64, + pending_balance_hi: u64, + available_balance: u64, + decryptable_available_balance: u64, +} + +#[cfg(feature = "zk-ops")] +async fn check_withheld_amount_in_mint( + token: &Token, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, + expected: u64, +) where + T: SendTransaction + SimulateTransaction, +{ + let state = token.get_mint_info().await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let decrypted_amount = extension + .withheld_amount + .decrypt(withdraw_withheld_authority_elgamal_keypair.secret()) + .unwrap(); + assert_eq!(decrypted_amount, expected); +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_fee_config() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + + // Try invalid combinations of extensions + let err = context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 3, + InstructionError::Custom(TokenError::InvalidExtensionCombination as u32), + ) + ))) + ); + + let err = context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 3, + InstructionError::Custom(TokenError::InvalidExtensionCombination as u32), + ) + ))) + ); + + let err = context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 2, + InstructionError::Custom(TokenError::InvalidExtensionCombination as u32), + ) + ))) + ); + + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); +} + +#[tokio::test] +async fn confidential_transfer_initialize_and_update_mint() { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap(); + + let TokenContext { token, .. } = context.token_context.unwrap(); + + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + + assert_eq!( + extension.authority, + Some(authority.pubkey()).try_into().unwrap() + ); + assert_eq!( + extension.auto_approve_new_accounts, + auto_approve_new_accounts.into() + ); + assert_eq!( + extension.auditor_elgamal_pubkey, + Some(auditor_elgamal_pubkey).try_into().unwrap() + ); + + // Change the authority + let new_authority = Keypair::new(); + let wrong_keypair = Keypair::new(); + + let err = token + .set_authority( + token.get_address(), + &wrong_keypair.pubkey(), + Some(&new_authority.pubkey()), + instruction::AuthorityType::ConfidentialTransferMint, + &[&wrong_keypair], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::OwnerMismatch as u32) + ) + ))) + ); + + token + .set_authority( + token.get_address(), + &authority.pubkey(), + Some(&new_authority.pubkey()), + instruction::AuthorityType::ConfidentialTransferMint, + &[&authority], + ) + .await + .unwrap(); + + // New authority can change mint parameters while the old cannot + let new_auto_approve_new_accounts = false; + let new_auditor_elgamal_pubkey = None; + + let err = token + .confidential_transfer_update_mint( + &authority.pubkey(), + new_auto_approve_new_accounts, + new_auditor_elgamal_pubkey, + &[&authority], + ) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::OwnerMismatch as u32) + ) + ))) + ); + + token + .confidential_transfer_update_mint( + &new_authority.pubkey(), + new_auto_approve_new_accounts, + new_auditor_elgamal_pubkey, + &[&new_authority], + ) + .await + .unwrap(); + + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!( + extension.authority, + Some(new_authority.pubkey()).try_into().unwrap() + ); + assert_eq!( + extension.auto_approve_new_accounts, + new_auto_approve_new_accounts.try_into().unwrap(), + ); + assert_eq!( + extension.auditor_elgamal_pubkey, + new_auditor_elgamal_pubkey.try_into().unwrap(), + ); + + // Set new authority to None + token + .set_authority( + token.get_address(), + &new_authority.pubkey(), + None, + instruction::AuthorityType::ConfidentialTransferMint, + &[&new_authority], + ) + .await + .unwrap(); + + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, None.try_into().unwrap()); +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = + ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; + let bob_meta = + ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; + + let transfer_fee_parameters = TransferFee { + epoch: 0.into(), + maximum_fee: TEST_MAXIMUM_FEE.into(), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), + }; + + // Test fee is 2.5% so the withheld fees should be 3 + token + .confidential_transfer_transfer_with_fee( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + transfer_fee_parameters.transfer_fee_basis_points.into(), + transfer_fee_parameters.maximum_fee.into(), + &[&alice], + ) + .await + .unwrap(); + + let new_decryptable_available_balance = alice_meta.aes_key.encrypt(0); + token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + None, + None, + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&withdraw_withheld_authority], + ) + .await + .unwrap(); + + // withheld fees are not harvested to mint yet + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + token + .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) + .await + .unwrap(); + + let state = token + .get_account_info(&bob_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + + // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` instruction data + let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); + let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); + + check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, fee).await; + + token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + None, + None, + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&withdraw_withheld_authority], + ) + .await + .unwrap(); + + // withheld fees are withdrawn back to alice's account + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 3, + decryptable_available_balance: 3, + }, + ) + .await; + + check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, 0).await; +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_withdraw_withheld_tokens_from_accounts() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = + ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; + let bob_meta = + ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; + + let transfer_fee_parameters = TransferFee { + epoch: 0.into(), + maximum_fee: TEST_MAXIMUM_FEE.into(), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), + }; + + // Test fee is 2.5% so the withheld fees should be 3 + token + .confidential_transfer_transfer_with_fee( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + transfer_fee_parameters.transfer_fee_basis_points.into(), + transfer_fee_parameters.maximum_fee.into(), + &[&alice], + ) + .await + .unwrap(); + + let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); + let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); + token + .confidential_transfer_withdraw_withheld_tokens_from_accounts( + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + None, + None, + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&bob_meta.token_account], + &[&withdraw_withheld_authority], + ) + .await + .unwrap(); + + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: fee, + decryptable_available_balance: fee, + }, + ) + .await; + + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 97, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + let state = token + .get_account_info(&bob_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_withdraw_withheld_tokens_from_mint_with_proof_context() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = + ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; + let bob_meta = + ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; + + let transfer_fee_parameters = TransferFee { + epoch: 0.into(), + maximum_fee: TEST_MAXIMUM_FEE.into(), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), + }; + + // Test fee is 2.5% so the withheld fees should be 3 + token + .confidential_transfer_transfer_with_fee( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + transfer_fee_parameters.transfer_fee_basis_points.into(), + transfer_fee_parameters.maximum_fee.into(), + &[&alice], + ) + .await + .unwrap(); + + token + .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) + .await + .unwrap(); + + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyCiphertextCiphertextEquality; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let state = token.get_mint_info().await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let withheld_tokens_info = WithheldTokensInfo::new(&extension.withheld_amount); + + let proof_data = withheld_tokens_info + .generate_proof_data( + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` instruction data + let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); + let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); + token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + Some(&context_state_account.pubkey()), + None, + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&withdraw_withheld_authority], + ) + .await + .unwrap(); + + // withheld fees are withdrawn back to alice's account + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 3, + decryptable_available_balance: 3, + }, + ) + .await; + + check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, 0).await; +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_proof_context() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = + ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; + let bob_meta = + ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; + + let transfer_fee_parameters = TransferFee { + epoch: 0.into(), + maximum_fee: TEST_MAXIMUM_FEE.into(), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), + }; + + // Test fee is 2.5% so the withheld fees should be 3 + token + .confidential_transfer_transfer_with_fee( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + transfer_fee_parameters.transfer_fee_basis_points.into(), + transfer_fee_parameters.maximum_fee.into(), + &[&alice], + ) + .await + .unwrap(); + + let context_state_account = Keypair::new(); + + // create context state + { + let context_state_authority = Keypair::new(); + let space = size_of::>(); + + let instruction_type = ProofInstruction::VerifyCiphertextCiphertextEquality; + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let state = token + .get_account_info(&bob_meta.token_account) + .await + .unwrap(); + let withheld_amount = state + .get_extension::() + .unwrap() + .withheld_amount; + let withheld_tokens_info = WithheldTokensInfo::new(&withheld_amount); + + let proof_data = withheld_tokens_info + .generate_proof_data( + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + ) + .unwrap(); + + let mut ctx = context.context.lock().await; + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let instructions = vec![ + system_instruction::create_account( + &ctx.payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), + ]; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &context_state_account], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + } + + let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); + let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); + token + .confidential_transfer_withdraw_withheld_tokens_from_accounts( + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + Some(&context_state_account.pubkey()), + None, + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&bob_meta.token_account], + &[&withdraw_withheld_authority], + ) + .await + .unwrap(); + + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: fee, + decryptable_available_balance: fee, + }, + ) + .await; + + bob_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 97, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, + ) + .await; + + let state = token + .get_account_info(&bob_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_harvest_withheld_tokens_to_mint() { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(confidential_transfer_authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, + decimals, + .. + } = context.token_context.unwrap(); + + let alice_meta = + ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; + let bob_meta = + ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; + + let transfer_fee_parameters = TransferFee { + epoch: 0.into(), + maximum_fee: TEST_MAXIMUM_FEE.into(), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), + }; + + // there are no withheld fees in bob's account yet, but try harvesting + token + .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) + .await + .unwrap(); + + // Test fee is 2.5% so the withheld fees should be 3 + token + .confidential_transfer_transfer_with_fee( + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + None, + 100, + None, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + transfer_fee_parameters.transfer_fee_basis_points.into(), + transfer_fee_parameters.maximum_fee.into(), + &[&alice], + ) + .await + .unwrap(); + + // disable harvest withheld tokens to mint + token + .confidential_transfer_disable_harvest_to_mint( + &confidential_transfer_fee_authority.pubkey(), + &[&confidential_transfer_fee_authority], + ) + .await + .unwrap(); + + let err = token + .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) + .await + .unwrap_err(); + + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::HarvestToMintDisabled as u32), + ) + ))) + ); + + // enable harvest withheld tokens to mint + token + .confidential_transfer_enable_harvest_to_mint( + &confidential_transfer_fee_authority.pubkey(), + &[&confidential_transfer_fee_authority], + ) + .await + .unwrap(); + + // Refresh the blockhash since we're doing the same thing twice in a row + token.get_new_latest_blockhash().await.unwrap(); + token + .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) + .await + .unwrap(); + + let state = token + .get_account_info(&bob_meta.token_account) + .await + .unwrap(); + let extension = state + .get_extension::() + .unwrap(); + assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + + // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` instruction data + let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); + + check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, fee).await; +} diff --git a/token/program-2022-test/tests/cpi_guard.rs b/token/program-2022-test/tests/cpi_guard.rs index d94ceb3cbcf..45aba1424ca 100644 --- a/token/program-2022-test/tests/cpi_guard.rs +++ b/token/program-2022-test/tests/cpi_guard.rs @@ -516,6 +516,8 @@ async fn test_cpi_guard_approve() { .await .unwrap(); + // refresh the blockhash + token_obj.get_new_latest_blockhash().await.unwrap(); token_obj .process_ixs(&[mk_approve(do_checked)], &[&alice]) .await diff --git a/token/program-2022-test/tests/group_pointer.rs b/token/program-2022-test/tests/group_pointer.rs new file mode 100644 index 00000000000..5dc6922919f --- /dev/null +++ b/token/program-2022-test/tests/group_pointer.rs @@ -0,0 +1,253 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + instruction::InstructionError, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, + transaction::TransactionError, transport::TransportError, + }, + spl_token_2022::{ + error::TokenError, + extension::{group_pointer::GroupPointer, BaseStateWithExtensions}, + instruction, + processor::Processor, + }, + spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, + std::{convert::TryInto, sync::Arc}, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, group_address: &Pubkey, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::GroupPointer { + authority: Some(*authority), + group_address: Some(*group_address), + }], + None, + ) + .await + .unwrap(); + context +} + +#[tokio::test] +async fn success_init() { + let authority = Pubkey::new_unique(); + let group_address = Pubkey::new_unique(); + let mint_keypair = Keypair::new(); + let token = setup(mint_keypair, &group_address, &authority) + .await + .token_context + .take() + .unwrap() + .token; + + let state = token.get_mint_info().await.unwrap(); + assert!(state.base.is_initialized); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, Some(authority).try_into().unwrap()); + assert_eq!( + extension.group_address, + Some(group_address).try_into().unwrap() + ); +} + +#[tokio::test] +async fn fail_init_all_none() { + let mut program_test = ProgramTest::default(); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let err = context + .init_token_with_mint_keypair_and_freeze_authority( + Keypair::new(), + vec![ExtensionInitializationParams::GroupPointer { + authority: None, + group_address: None, + }], + None, + ) + .await + .unwrap_err(); + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 1, + InstructionError::Custom(TokenError::InvalidInstruction as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn set_authority() { + let authority = Keypair::new(); + let group_address = Pubkey::new_unique(); + let mint_keypair = Keypair::new(); + let token = setup(mint_keypair, &group_address, &authority.pubkey()) + .await + .token_context + .take() + .unwrap() + .token; + let new_authority = Keypair::new(); + + // fail, wrong signature + let wrong = Keypair::new(); + let err = token + .set_authority( + token.get_address(), + &wrong.pubkey(), + Some(&new_authority.pubkey()), + instruction::AuthorityType::GroupPointer, + &[&wrong], + ) + .await + .unwrap_err(); + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::OwnerMismatch as u32) + ) + ))) + ); + + // success + token + .set_authority( + token.get_address(), + &authority.pubkey(), + Some(&new_authority.pubkey()), + instruction::AuthorityType::GroupPointer, + &[&authority], + ) + .await + .unwrap(); + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!( + extension.authority, + Some(new_authority.pubkey()).try_into().unwrap(), + ); + + // set to none + token + .set_authority( + token.get_address(), + &new_authority.pubkey(), + None, + instruction::AuthorityType::GroupPointer, + &[&new_authority], + ) + .await + .unwrap(); + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, None.try_into().unwrap(),); + + // fail set again + let err = token + .set_authority( + token.get_address(), + &new_authority.pubkey(), + Some(&authority.pubkey()), + instruction::AuthorityType::GroupPointer, + &[&new_authority], + ) + .await + .unwrap_err(); + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::AuthorityTypeNotSupported as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn update_group_address() { + let authority = Keypair::new(); + let group_address = Pubkey::new_unique(); + let mint_keypair = Keypair::new(); + let token = setup(mint_keypair, &group_address, &authority.pubkey()) + .await + .token_context + .take() + .unwrap() + .token; + let new_group_address = Pubkey::new_unique(); + + // fail, wrong signature + let wrong = Keypair::new(); + let err = token + .update_group_address(&wrong.pubkey(), Some(new_group_address), &[&wrong]) + .await + .unwrap_err(); + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::OwnerMismatch as u32) + ) + ))) + ); + + // success + token + .update_group_address(&authority.pubkey(), Some(new_group_address), &[&authority]) + .await + .unwrap(); + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!( + extension.group_address, + Some(new_group_address).try_into().unwrap(), + ); + + // set to none + token + .update_group_address(&authority.pubkey(), None, &[&authority]) + .await + .unwrap(); + let state = token.get_mint_info().await.unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.group_address, None.try_into().unwrap(),); +} diff --git a/token/program-2022-test/tests/initialize_account.rs b/token/program-2022-test/tests/initialize_account.rs index adaf1499dce..c0cd45250c1 100644 --- a/token/program-2022-test/tests/initialize_account.rs +++ b/token/program-2022-test/tests/initialize_account.rs @@ -32,7 +32,7 @@ async fn no_extensions() { let mint_account = Keypair::new(); let mint_authority_pubkey = Pubkey::new_unique(); - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -60,7 +60,7 @@ async fn no_extensions() { let account = Keypair::new(); let account_owner_pubkey = Pubkey::new_unique(); - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -102,7 +102,7 @@ async fn fail_on_invalid_mint() { let rent = ctx.banks_client.get_rent().await.unwrap(); let mint_account = Keypair::new(); - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let instructions = vec![system_instruction::create_account( &ctx.payer.pubkey(), &mint_account.pubkey(), @@ -120,7 +120,7 @@ async fn fail_on_invalid_mint() { let account = Keypair::new(); let account_owner_pubkey = Pubkey::new_unique(); - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -167,7 +167,8 @@ async fn single_extension() { let mint_authority_pubkey = Pubkey::new_unique(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeConfig]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeConfig]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -205,7 +206,8 @@ async fn single_extension() { let account = Keypair::new(); let account_owner_pubkey = Pubkey::new_unique(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeAmount]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeAmount]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -237,7 +239,8 @@ async fn single_extension() { .expect("account not none"); assert_eq!( account_info.data.len(), - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeAmount]).unwrap(), + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeAmount]) + .unwrap(), ); assert_eq!(account_info.owner, spl_token_2022::id()); assert_eq!(account_info.lamports, rent.minimum_balance(space)); diff --git a/token/program-2022-test/tests/initialize_mint.rs b/token/program-2022-test/tests/initialize_mint.rs index 6a69055fd09..709f38d8481 100644 --- a/token/program-2022-test/tests/initialize_mint.rs +++ b/token/program-2022-test/tests/initialize_mint.rs @@ -111,7 +111,8 @@ async fn fail_extension_after_mint_init() { let mint_authority_pubkey = Pubkey::new_unique(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -196,7 +197,8 @@ async fn fail_init_overallocated_mint() { let mint_authority_pubkey = Pubkey::new_unique(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -242,9 +244,10 @@ async fn fail_account_init_after_mint_extension() { let mint_authority_pubkey = Pubkey::new_unique(); let token_account = Keypair::new(); - let mint_space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let mint_space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let account_space = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -312,7 +315,7 @@ async fn fail_account_init_after_mint_init() { let mint_account = Keypair::new(); let mint_authority_pubkey = Pubkey::new_unique(); - let mint_space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let mint_space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -365,7 +368,8 @@ async fn fail_account_init_after_mint_init_with_extension() { let mint_authority_pubkey = Pubkey::new_unique(); let mint_space = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -424,7 +428,8 @@ async fn fail_fee_init_after_mint_init() { let mint_authority_pubkey = Pubkey::new_unique(); let space = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeConfig]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeConfig]) + .unwrap(); let instructions = vec![ system_instruction::create_account( &ctx.payer.pubkey(), @@ -532,7 +537,7 @@ async fn fail_invalid_extensions_combination() { .unwrap(); // initialize transfer fee and confidential transfers, but no confidential transfer fee - let mint_space = ExtensionType::try_get_account_len::(&[ + let mint_space = ExtensionType::try_calculate_account_len::(&[ ExtensionType::TransferFeeConfig, ExtensionType::ConfidentialTransferMint, ]) @@ -573,7 +578,7 @@ async fn fail_invalid_extensions_combination() { ); // initialize transfer fee and confidential transfer fees, but no confidential transfers - let mint_space = ExtensionType::try_get_account_len::(&[ + let mint_space = ExtensionType::try_calculate_account_len::(&[ ExtensionType::TransferFeeConfig, ExtensionType::ConfidentialTransferFeeConfig, ]) @@ -615,7 +620,7 @@ async fn fail_invalid_extensions_combination() { // initialize all of transfer fee, confidential transfers, and confidential transfer fees // (success case) - let mint_space = ExtensionType::try_get_account_len::(&[ + let mint_space = ExtensionType::try_calculate_account_len::(&[ ExtensionType::TransferFeeConfig, ExtensionType::ConfidentialTransferMint, ExtensionType::ConfidentialTransferFeeConfig, diff --git a/token/program-2022-test/tests/reallocate.rs b/token/program-2022-test/tests/reallocate.rs index bc192a987fb..733dfd1aca0 100644 --- a/token/program-2022-test/tests/reallocate.rs +++ b/token/program-2022-test/tests/reallocate.rs @@ -109,7 +109,8 @@ async fn reallocate() { let account = token.get_account(alice_account).await.unwrap(); assert_eq!( account.data.len(), - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap() + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap() ); // reallocate succeeds with noop if account is already large enough @@ -126,7 +127,8 @@ async fn reallocate() { let account = token.get_account(alice_account).await.unwrap(); assert_eq!( account.data.len(), - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]).unwrap() + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) + .unwrap() ); // reallocate only reallocates enough for new extension, and dedupes extensions @@ -147,7 +149,7 @@ async fn reallocate() { let account = token.get_account(alice_account).await.unwrap(); assert_eq!( account.data.len(), - ExtensionType::try_get_account_len::(&[ + ExtensionType::try_calculate_account_len::(&[ ExtensionType::ImmutableOwner, ExtensionType::TransferFeeAmount ]) @@ -190,7 +192,7 @@ async fn reallocate_without_current_extension_knowledge() { let account = token.get_account(alice_account).await.unwrap(); assert_eq!( account.data.len(), - ExtensionType::try_get_account_len::(&[ + ExtensionType::try_calculate_account_len::(&[ ExtensionType::TransferFeeAmount, ExtensionType::ImmutableOwner ]) @@ -264,7 +266,7 @@ async fn reallocate_updates_native_rent_exemption( let account = token.get_account(alice_account).await.unwrap(); assert_eq!( account.data.len(), - ExtensionType::try_get_account_len::(extensions).unwrap() + ExtensionType::try_calculate_account_len::(extensions).unwrap() ); let expected_rent_exempt_reserve = { let mut context = context.lock().await; diff --git a/token/program-2022-test/tests/token_metadata_emit.rs b/token/program-2022-test/tests/token_metadata_emit.rs new file mode 100644 index 00000000000..cf5eb9932af --- /dev/null +++ b/token/program-2022-test/tests/token_metadata_emit.rs @@ -0,0 +1,142 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + borsh0_10::try_from_slice_unchecked, program::MAX_RETURN_DATA, pubkey::Pubkey, + signature::Signer, signer::keypair::Keypair, transaction::Transaction, + }, + spl_token_2022::processor::Processor, + spl_token_client::token::ExtensionInitializationParams, + spl_token_metadata_interface::{ + borsh::BorshSerialize, instruction::emit, state::TokenMetadata, + }, + std::{convert::TryInto, sync::Arc}, + test_case::test_case, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let metadata_address = Some(mint.pubkey()); + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(*authority), + metadata_address, + }], + None, + ) + .await + .unwrap(); + context +} + +#[test_case(Some(40), Some(40) ; "zero bytes")] +#[test_case(Some(40), Some(41) ; "one byte")] +#[test_case(Some(1_000_000), Some(1_000_001) ; "too far")] +#[test_case(Some(50), Some(49) ; "wrong way")] +#[test_case(Some(50), None ; "truncate start")] +#[test_case(None, Some(50) ; "truncate end")] +#[test_case(None, None ; "full data")] +#[tokio::test] +async fn success(start: Option, end: Option) { + let program_id = spl_token_2022::id(); + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(update_authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + let mut context = test_context.context.lock().await; + + let transaction = Transaction::new_signed_with_payer( + &[emit( + &program_id, + token_context.token.get_address(), + start, + end, + )], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + let simulation = context + .banks_client + .simulate_transaction(transaction) + .await + .unwrap(); + + let metadata_buffer = token_metadata.try_to_vec().unwrap(); + if let Some(check_buffer) = TokenMetadata::get_slice(&metadata_buffer, start, end) { + if !check_buffer.is_empty() { + // pad the data if necessary + let mut return_data = vec![0; MAX_RETURN_DATA]; + if let Some(simulation_details) = simulation.simulation_details { + if let Some(simulation_return_data) = simulation_details.return_data { + assert_eq!(simulation_return_data.program_id, program_id); + return_data[..simulation_return_data.data.len()] + .copy_from_slice(&simulation_return_data.data); + } + } + + assert_eq!(*check_buffer, return_data[..check_buffer.len()]); + // we're sure that we're getting the full data, so also compare the deserialized type + if start.is_none() && end.is_none() { + let emitted_token_metadata = + try_from_slice_unchecked::(&return_data).unwrap(); + assert_eq!(token_metadata, emitted_token_metadata); + } + } else { + assert!(simulation.simulation_details.unwrap().return_data.is_none()); + } + } else { + assert!(simulation.simulation_details.unwrap().return_data.is_none()); + } +} diff --git a/token/program-2022-test/tests/token_metadata_initialize.rs b/token/program-2022-test/tests/token_metadata_initialize.rs new file mode 100644 index 00000000000..e6576d68f68 --- /dev/null +++ b/token/program-2022-test/tests/token_metadata_initialize.rs @@ -0,0 +1,290 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + borsh::BorshDeserialize, + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + instruction::InstructionError, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, + transaction::TransactionError, transport::TransportError, + }, + spl_token_2022::{error::TokenError, extension::BaseStateWithExtensions, processor::Processor}, + spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, + spl_token_metadata_interface::{error::TokenMetadataError, state::TokenMetadata}, + std::{convert::TryInto, sync::Arc}, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let metadata_address = Some(mint.pubkey()); + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(*authority), + metadata_address, + }], + None, + ) + .await + .unwrap(); + context +} + +#[tokio::test] +async fn success_initialize() { + let authority = Pubkey::new_unique(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Pubkey::new_unique(); + let name = "MyTokenNeedsMetadata".to_string(); + let symbol = "NEEDS".to_string(); + let uri = "my.token.needs.metadata".to_string(); + let token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(update_authority).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + // fails without more lamports for new rent-exemption + let error = token_context + .token + .token_metadata_initialize( + &update_authority, + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InsufficientFundsForRent { account_index: 2 } + ))) + ); + + // fail wrong signer + let not_mint_authority = Keypair::new(); + let error = token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority, + ¬_mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[¬_mint_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 1, + InstructionError::Custom(TokenMetadataError::IncorrectMintAuthority as u32) + ) + ))) + ); + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority, + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + // check that the data is correct + let mint_info = token_context.token.get_mint_info().await.unwrap(); + let metadata_bytes = mint_info.get_extension_bytes::().unwrap(); + let fetched_metadata = TokenMetadata::try_from_slice(metadata_bytes).unwrap(); + assert_eq!(fetched_metadata, token_metadata); + + // fail double-init + let error = token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority, + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::ExtensionAlreadyInitialized as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn fail_without_metadata_pointer() { + let mut test_context = { + let mint_keypair = Keypair::new(); + let program_test = setup_program_test(); + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + context + .init_token_with_mint_keypair_and_freeze_authority(mint_keypair, vec![], None) + .await + .unwrap(); + context + }; + + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let error = token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &Pubkey::new_unique(), + &token_context.mint_authority.pubkey(), + "Name".to_string(), + "Symbol".to_string(), + "URI".to_string(), + &[&token_context.mint_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 1, + InstructionError::Custom(TokenError::InvalidExtensionCombination as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn fail_init_in_another_mint() { + let authority = Pubkey::new_unique(); + let first_mint_keypair = Keypair::new(); + let first_mint = first_mint_keypair.pubkey(); + let mut test_context = setup(first_mint_keypair, &authority).await; + let second_mint_keypair = Keypair::new(); + let second_mint = second_mint_keypair.pubkey(); + test_context + .init_token_with_mint_keypair_and_freeze_authority( + second_mint_keypair, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(authority), + metadata_address: Some(second_mint), + }], + None, + ) + .await + .unwrap(); + + let token_context = test_context.token_context.take().unwrap(); + + let error = token_context + .token + .process_ixs( + &[spl_token_metadata_interface::instruction::initialize( + &spl_token_2022::id(), + &first_mint, + &Pubkey::new_unique(), + token_context.token.get_address(), + &token_context.mint_authority.pubkey(), + "Name".to_string(), + "Symbol".to_string(), + "URI".to_string(), + )], + &[&token_context.mint_authority], + ) + .await + .unwrap_err(); + + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::MintMismatch as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn fail_without_signature() { + let authority = Pubkey::new_unique(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority).await; + + let token_context = test_context.token_context.take().unwrap(); + + let mut instruction = spl_token_metadata_interface::instruction::initialize( + &spl_token_2022::id(), + token_context.token.get_address(), + &Pubkey::new_unique(), + token_context.token.get_address(), + &token_context.mint_authority.pubkey(), + "Name".to_string(), + "Symbol".to_string(), + "URI".to_string(), + ); + instruction.accounts[3].is_signer = false; + let error = token_context + .token + .process_ixs(&[instruction], &[] as &[&dyn Signer; 0]) // yuck, but the compiler needs it + .await + .unwrap_err(); + + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ))) + ); +} diff --git a/token/program-2022-test/tests/token_metadata_remove_key.rs b/token/program-2022-test/tests/token_metadata_remove_key.rs new file mode 100644 index 00000000000..8640196df70 --- /dev/null +++ b/token/program-2022-test/tests/token_metadata_remove_key.rs @@ -0,0 +1,263 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + instruction::InstructionError, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + transaction::{Transaction, TransactionError}, + transport::TransportError, + }, + spl_token_2022::{extension::BaseStateWithExtensions, processor::Processor}, + spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, + spl_token_metadata_interface::{ + error::TokenMetadataError, + instruction::remove_key, + state::{Field, TokenMetadata}, + }, + std::{convert::TryInto, sync::Arc}, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let metadata_address = Some(mint.pubkey()); + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(*authority), + metadata_address, + }], + None, + ) + .await + .unwrap(); + context +} + +#[tokio::test] +async fn success_remove() { + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let mut token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(update_authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + let key = "new_field, wow!".to_string(); + let field = Field::Key(key.clone()); + let value = "so impressed with the new field, don't know what to put here".to_string(); + token_metadata.update(field.clone(), value.clone()); + + // add the field + token_context + .token + .token_metadata_update_field_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + field, + value, + None, + &[&update_authority], + ) + .await + .unwrap(); + + // now remove it + token_context + .token + .token_metadata_remove_key( + &update_authority.pubkey(), + key.clone(), + false, // idempotent + &[&update_authority], + ) + .await + .unwrap(); + + // check that the data is correct + token_metadata.remove_key(&key); + let mint = token_context.token.get_mint_info().await.unwrap(); + let fetched_metadata = mint.get_variable_len_extension::().unwrap(); + assert_eq!(fetched_metadata, token_metadata); + + // succeed again with idempotent flag + token_context + .token + .token_metadata_remove_key( + &update_authority.pubkey(), + key.clone(), + true, // idempotent + &[&update_authority], + ) + .await + .unwrap(); + + // fail doing it again without idempotent flag + let mut context = test_context.context.lock().await; + + // refresh blockhash before trying again + context.get_new_latest_blockhash().await.unwrap(); + let transaction = Transaction::new_signed_with_payer( + &[remove_key( + &spl_token_2022::id(), + &mint_pubkey, + &update_authority.pubkey(), + key, + false, // idempotent + )], + Some(&context.payer.pubkey()), + &[&context.payer, &update_authority], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + + assert_eq!( + error, + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenMetadataError::KeyNotFound as u32) + ) + ); +} + +#[tokio::test] +async fn fail_authority_checks() { + let program_id = spl_token_2022::id(); + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(update_authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + let key = "new_field, wow!".to_string(); + + // wrong authority + let error = token_context + .token + .token_metadata_remove_key( + &payer_pubkey, + key, + true, // idempotent + &[] as &[&dyn Signer; 0], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenMetadataError::IncorrectUpdateAuthority as u32) + ) + ))) + ); + + // no signature + let mut context = test_context.context.lock().await; + let mut instruction = remove_key( + &program_id, + &mint_pubkey, + &update_authority.pubkey(), + "new_name".to_string(), + true, // idempotent + ); + instruction.accounts[1].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + error, + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature,) + ); +} diff --git a/token/program-2022-test/tests/token_metadata_update_authority.rs b/token/program-2022-test/tests/token_metadata_update_authority.rs new file mode 100644 index 00000000000..1a22baf63ee --- /dev/null +++ b/token/program-2022-test/tests/token_metadata_update_authority.rs @@ -0,0 +1,228 @@ +#![cfg(feature = "test-sbf")] + +mod program_test; +use { + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + instruction::InstructionError, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + transaction::{Transaction, TransactionError}, + transport::TransportError, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, + spl_token_2022::{extension::BaseStateWithExtensions, processor::Processor}, + spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, + spl_token_metadata_interface::{ + error::TokenMetadataError, instruction::update_authority, state::TokenMetadata, + }, + std::{convert::TryInto, sync::Arc}, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let metadata_address = Some(mint.pubkey()); + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(*authority), + metadata_address, + }], + None, + ) + .await + .unwrap(); + context +} + +#[tokio::test] +async fn success_update() { + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let mut token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + let new_update_authority = Keypair::new(); + let new_update_authority_pubkey = + OptionalNonZeroPubkey::try_from(Some(new_update_authority.pubkey())).unwrap(); + token_metadata.update_authority = new_update_authority_pubkey; + + token_context + .token + .token_metadata_update_authority( + &authority.pubkey(), + Some(new_update_authority.pubkey()), + &[&authority], + ) + .await + .unwrap(); + + // check that the data is correct + let mint = token_context.token.get_mint_info().await.unwrap(); + let fetched_metadata = mint.get_variable_len_extension::().unwrap(); + assert_eq!(fetched_metadata, token_metadata); + + // unset + token_metadata.update_authority = None.try_into().unwrap(); + token_context + .token + .token_metadata_update_authority( + &new_update_authority.pubkey(), + None, + &[&new_update_authority], + ) + .await + .unwrap(); + + let mint = token_context.token.get_mint_info().await.unwrap(); + let fetched_metadata = mint.get_variable_len_extension::().unwrap(); + assert_eq!(fetched_metadata, token_metadata); + + // fail to update + let error = token_context + .token + .token_metadata_update_authority( + &new_update_authority.pubkey(), + Some(new_update_authority.pubkey()), + &[&new_update_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenMetadataError::ImmutableMetadata as u32) + ) + ))) + ); +} + +#[tokio::test] +async fn fail_authority_checks() { + let program_id = spl_token_2022::id(); + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + // wrong authority + let error = token_context + .token + .token_metadata_update_authority(&payer_pubkey, None, &[] as &[&dyn Signer; 0]) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenMetadataError::IncorrectUpdateAuthority as u32), + ) + ))) + ); + + // no signature + let mut context = test_context.context.lock().await; + let mut instruction = update_authority( + &program_id, + &mint_pubkey, + &authority.pubkey(), + None.try_into().unwrap(), + ); + instruction.accounts[1].is_signer = false; + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + error, + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature,) + ); +} diff --git a/token/program-2022-test/tests/token_metadata_update_field.rs b/token/program-2022-test/tests/token_metadata_update_field.rs new file mode 100644 index 00000000000..83417d376f4 --- /dev/null +++ b/token/program-2022-test/tests/token_metadata_update_field.rs @@ -0,0 +1,206 @@ +#![cfg(feature = "test-sbf")] +#![allow(clippy::items_after_test_module)] + +mod program_test; +use { + program_test::TestContext, + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + instruction::InstructionError, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, + transaction::TransactionError, transport::TransportError, + }, + spl_token_2022::{extension::BaseStateWithExtensions, processor::Processor}, + spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, + spl_token_metadata_interface::{ + error::TokenMetadataError, + instruction::update_field, + state::{Field, TokenMetadata}, + }, + std::{convert::TryInto, sync::Arc}, + test_case::test_case, +}; + +fn setup_program_test() -> ProgramTest { + let mut program_test = ProgramTest::default(); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test +} + +async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { + let program_test = setup_program_test(); + + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + let metadata_address = Some(mint.pubkey()); + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::MetadataPointer { + authority: Some(*authority), + metadata_address, + }], + None, + ) + .await + .unwrap(); + context +} + +#[test_case(Field::Name, "This is my larger name".to_string() ; "larger name")] +#[test_case(Field::Name, "Smaller".to_string() ; "smaller name")] +#[test_case(Field::Key("my new field".to_string()), "Some data for the new field!".to_string() ; "new field")] +#[tokio::test] +async fn success_update(field: Field, value: String) { + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Keypair::new(); + let name = "MySuperCoolToken".to_string(); + let symbol = "MINE".to_string(); + let uri = "my.super.cool.token".to_string(); + let mut token_metadata = TokenMetadata { + name, + symbol, + uri, + update_authority: Some(update_authority.pubkey()).try_into().unwrap(), + mint: *token_context.token.get_address(), + ..Default::default() + }; + + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + &token_context.mint_authority.pubkey(), + token_metadata.name.clone(), + token_metadata.symbol.clone(), + token_metadata.uri.clone(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + let old_space = token_metadata.tlv_size_of().unwrap(); + token_metadata.update(field.clone(), value.clone()); + let new_space = token_metadata.tlv_size_of().unwrap(); + + if new_space > old_space { + let error = token_context + .token + .token_metadata_update_field( + &update_authority.pubkey(), + field.clone(), + value.clone(), + &[&update_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InsufficientFundsForRent { account_index: 2 } + ))) + ); + } + + // transfer required lamports + token_context + .token + .token_metadata_update_field_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + field, + value, + None, + &[&update_authority], + ) + .await + .unwrap(); + + // check that the account looks good + let mint_info = token_context.token.get_mint_info().await.unwrap(); + let fetched_metadata = mint_info + .get_variable_len_extension::() + .unwrap(); + assert_eq!(fetched_metadata, token_metadata); +} + +#[tokio::test] +async fn fail_authority_checks() { + let authority = Keypair::new(); + let mint_keypair = Keypair::new(); + let mut test_context = setup(mint_keypair, &authority.pubkey()).await; + let payer_pubkey = test_context.context.lock().await.payer.pubkey(); + let token_context = test_context.token_context.take().unwrap(); + + let update_authority = Keypair::new(); + token_context + .token + .token_metadata_initialize_with_rent_transfer( + &payer_pubkey, + &update_authority.pubkey(), + &token_context.mint_authority.pubkey(), + "MySuperCoolToken".to_string(), + "MINE".to_string(), + "my.super.cool.token".to_string(), + &[&token_context.mint_authority], + ) + .await + .unwrap(); + + // no signature + let mut instruction = update_field( + &spl_token_2022::id(), + token_context.token.get_address(), + &update_authority.pubkey(), + Field::Name, + "new_name".to_string(), + ); + instruction.accounts[1].is_signer = false; + + let error = token_context + .token + .process_ixs(&[instruction], &[] as &[&dyn Signer; 0]) // yuck, but the compiler needs it + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ))) + ); + + // wrong authority + let wrong_authority = Keypair::new(); + let error = token_context + .token + .token_metadata_update_field( + &wrong_authority.pubkey(), + Field::Name, + "new_name".to_string(), + &[&wrong_authority], + ) + .await + .unwrap_err(); + assert_eq!( + error, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenMetadataError::IncorrectUpdateAuthority as u32) + ) + ))) + ); +} diff --git a/token/program-2022-test/tests/transfer_fee.rs b/token/program-2022-test/tests/transfer_fee.rs index dff1a4afa7b..c0251ec53d2 100644 --- a/token/program-2022-test/tests/transfer_fee.rs +++ b/token/program-2022-test/tests/transfer_fee.rs @@ -279,9 +279,14 @@ async fn set_fee() { .unwrap(); // warp to first normal slot to easily calculate epochs - let epoch_schedule = context.context.lock().await.genesis_config().epoch_schedule; - let first_normal_slot = epoch_schedule.first_normal_slot; - let slots_per_epoch = epoch_schedule.slots_per_epoch; + let (first_normal_slot, slots_per_epoch) = { + let context = context.context.lock().await; + ( + context.genesis_config().epoch_schedule.first_normal_slot, + context.genesis_config().epoch_schedule.slots_per_epoch, + ) + }; + context .context .lock() diff --git a/token/program-2022-test/tests/transfer_hook.rs b/token/program-2022-test/tests/transfer_hook.rs index bab3dc57778..eb333536793 100644 --- a/token/program-2022-test/tests/transfer_hook.rs +++ b/token/program-2022-test/tests/transfer_hook.rs @@ -176,23 +176,25 @@ fn setup_program_test(program_id: &Pubkey) -> ProgramTest { fn add_validation_account(program_test: &mut ProgramTest, mint: &Pubkey, program_id: &Pubkey) { let validation_address = get_extra_account_metas_address(mint, program_id); - let account_metas = vec![ + let extra_account_metas = vec![ AccountMeta { pubkey: Pubkey::new_unique(), is_signer: false, is_writable: false, - }, + } + .into(), AccountMeta { pubkey: Pubkey::new_unique(), is_signer: false, is_writable: false, - }, + } + .into(), ]; program_test.add_account( validation_address, Account { lamports: 1_000_000_000, // a lot, just to be safe - data: spl_transfer_hook_example::state::example_data(&account_metas).unwrap(), + data: spl_transfer_hook_example::state::example_data(&extra_account_metas).unwrap(), owner: *program_id, ..Account::default() }, @@ -565,23 +567,25 @@ async fn success_downgrade_writable_and_signer_accounts() { let alice = Keypair::new(); let alice_account = Keypair::new(); let validation_address = get_extra_account_metas_address(&mint.pubkey(), &program_id); - let account_metas = vec![ + let extra_account_metas = vec![ AccountMeta { pubkey: alice_account.pubkey(), is_signer: false, is_writable: true, - }, + } + .into(), AccountMeta { pubkey: alice.pubkey(), is_signer: true, is_writable: false, - }, + } + .into(), ]; program_test.add_account( validation_address, Account { lamports: 1_000_000_000, // a lot, just to be safe - data: spl_transfer_hook_example::state::example_data(&account_metas).unwrap(), + data: spl_transfer_hook_example::state::example_data(&extra_account_metas).unwrap(), owner: program_id, ..Account::default() }, @@ -684,7 +688,7 @@ async fn success_transfers_using_onchain_helper() { let (source_b_account, destination_b_account) = setup_accounts(&token_b_context, Keypair::new(), Keypair::new(), amount).await; let authority_b = token_b_context.alice; - let mut account_metas = vec![ + let account_metas = vec![ AccountMeta::new(source_a_account, false), AccountMeta::new_readonly(mint_a, false), AccountMeta::new(destination_a_account, false), @@ -696,25 +700,34 @@ async fn success_transfers_using_onchain_helper() { AccountMeta::new_readonly(authority_b.pubkey(), true), AccountMeta::new_readonly(spl_token_2022::id(), false), ]; - offchain::get_extra_transfer_account_metas( - &mut account_metas, + + let mut instruction = Instruction::new_with_bytes(swap_program_id, &[], account_metas); + + offchain::resolve_extra_transfer_account_metas( + &mut instruction, |address| { - token_a - .get_account(address) - .map_ok(|acc| Some(acc.data)) - .map_err(offchain::AccountFetchError::from) + token_a.get_account(address).map_ok_or_else( + |e| match e { + TokenClientError::AccountNotFound => Ok(None), + _ => Err(offchain::AccountFetchError::from(e)), + }, + |acc| Ok(Some(acc.data)), + ) }, &mint_a, ) .await .unwrap(); - offchain::get_extra_transfer_account_metas( - &mut account_metas, + offchain::resolve_extra_transfer_account_metas( + &mut instruction, |address| { - token_a - .get_account(address) - .map_ok(|acc| Some(acc.data)) - .map_err(offchain::AccountFetchError::from) + token_a.get_account(address).map_ok_or_else( + |e| match e { + TokenClientError::AccountNotFound => Ok(None), + _ => Err(offchain::AccountFetchError::from(e)), + }, + |acc| Ok(Some(acc.data)), + ) }, &mint_b, ) @@ -722,14 +735,7 @@ async fn success_transfers_using_onchain_helper() { .unwrap(); token_a - .process_ixs( - &[Instruction::new_with_bytes( - swap_program_id, - &[], - account_metas, - )], - &[&authority_a, &authority_b], - ) + .process_ixs(&[instruction], &[&authority_a, &authority_b]) .await .unwrap(); } diff --git a/token/program-2022/Cargo.toml b/token/program-2022/Cargo.toml index dc2495c349e..7d9c2a1be07 100644 --- a/token/program-2022/Cargo.toml +++ b/token/program-2022/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token-2022" -version = "0.7.0" +version = "0.9.0" description = "Solana Program Library Token 2022" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -11,7 +11,7 @@ exclude = ["js/**"] [features] no-entrypoint = [] test-sbf = [] -serde-traits = ["serde", "serde_with"] +serde-traits = ["dep:serde", "dep:serde_with", "dep:base64", "spl-pod/serde-traits"] # Remove these features once the underlying syscalls are released on all networks default = ["zk-ops"] zk-ops = [] @@ -19,26 +19,30 @@ proof-program = [] [dependencies] arrayref = "0.3.7" -bytemuck = { version = "1.13.1", features = ["derive"] } +bytemuck = { version = "1.14.0", features = ["derive"] } num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" -solana-program = "1.16.1" -solana-zk-token-sdk = "1.16.1" +num_enum = "0.7.1" +solana-program = "1.17.2" +solana-zk-token-sdk = "1.17.2" spl-memo = { version = "4.0.0", path = "../../memo/program", features = [ "no-entrypoint" ] } -spl-transfer-hook-interface = { version = "0.1.0", path = "../transfer-hook-interface" } spl-token = { version = "4.0", path = "../program", features = ["no-entrypoint"] } +spl-token-metadata-interface = { version = "0.2.0", path = "../../token-metadata/interface" } +spl-transfer-hook-interface = { version = "0.3.0", path = "../transfer-hook/interface" } +spl-type-length-value = { version = "0.3.0", path = "../../libraries/type-length-value" } +spl-pod = { version = "0.1.0", path = "../../libraries/pod" } thiserror = "1.0" -serde = { version = "1.0.164", optional = true } -serde_with = { version = "2.0.0", optional = true } +serde = { version = "1.0.190", optional = true } +serde_with = { version = "3.4.0", optional = true } +base64 = { version = "0.21.5", optional = true } [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.2" +proptest = "1.3" serial_test = "2.0.0" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" -serde_json = "1.0.99" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" +serde_json = "1.0.108" [lib] crate-type = ["cdylib", "lib"] diff --git a/token/program-2022/src/error.rs b/token/program-2022/src/error.rs index dee60fe4a13..6da2fdb2b3d 100644 --- a/token/program-2022/src/error.rs +++ b/token/program-2022/src/error.rs @@ -204,6 +204,34 @@ pub enum TokenError { /// Extension allocation with overwrite must use the same length #[error("Extension allocation with overwrite must use the same length")] InvalidLengthForAlloc, + /// Failed to decrypt a confidential transfer account + #[error("Failed to decrypt a confidential transfer account")] + AccountDecryption, + /// Failed to generate a zero-knowledge proof needed for a token instruction + #[error("Failed to generate proof")] + ProofGeneration, + + // 55 + /// An invalid proof instruction offset was provided + #[error("An invalid proof instruction offset was provided")] + InvalidProofInstructionOffset, + /// Harvest of withheld tokens to mint is disabled + #[error("Harvest of withheld tokens to mint is disabled")] + HarvestToMintDisabled, + /// Split proof context state accounts not supported for instruction + #[error("Split proof context state accounts not supported for instruction")] + SplitProofContextStateAccountsNotSupported, + /// Not enough proof context state accounts provided + #[error("Not enough proof context state accounts provided")] + NotEnoughProofContextStateAccounts, + /// Ciphertext is malformed + #[error("Ciphertext is malformed")] + MalformedCiphertext, + + // 60 + /// Ciphertext arithmetic failed + #[error("Ciphertext arithmetic failed")] + CiphertextArithmeticFailed, } impl From for ProgramError { fn from(e: TokenError) -> Self { @@ -355,6 +383,30 @@ impl PrintProgramError for TokenError { TokenError::InvalidLengthForAlloc => { msg!("Extension allocation with overwrite must use the same length") } + TokenError::AccountDecryption => { + msg!("Failed to decrypt a confidential transfer account") + } + TokenError::ProofGeneration => { + msg!("Failed to generate proof") + } + TokenError::InvalidProofInstructionOffset => { + msg!("An invalid proof instruction offset was provided") + } + TokenError::HarvestToMintDisabled => { + msg!("Harvest of withheld tokens to mint is disabled") + } + TokenError::SplitProofContextStateAccountsNotSupported => { + msg!("Split proof context state accounts not supported for instruction") + } + TokenError::NotEnoughProofContextStateAccounts => { + msg!("Not enough proof context state accounts provided") + } + TokenError::MalformedCiphertext => { + msg!("Ciphertext is malformed") + } + TokenError::CiphertextArithmeticFailed => { + msg!("Ciphertext arithmetic failed") + } } } } diff --git a/token/program-2022/src/extension/confidential_transfer/account_info.rs b/token/program-2022/src/extension/confidential_transfer/account_info.rs new file mode 100644 index 00000000000..741321a8438 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/account_info.rs @@ -0,0 +1,369 @@ +use { + crate::{ + error::TokenError, + extension::confidential_transfer::{ + ciphertext_extraction::SourceDecryptHandles, + split_proof_generation::transfer_split_proof_data, ConfidentialTransferAccount, + DecryptableBalance, EncryptedBalance, PENDING_BALANCE_LO_BIT_LENGTH, + }, + }, + bytemuck::{Pod, Zeroable}, + solana_zk_token_sdk::{ + encryption::{ + auth_encryption::{AeCiphertext, AeKey}, + elgamal::{ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey}, + }, + instruction::{ + transfer::{FeeParameters, TransferData, TransferWithFeeData}, + withdraw::WithdrawData, + zero_balance::ZeroBalanceProofData, + BatchedGroupedCiphertext2HandlesValidityProofData, BatchedRangeProofU128Data, + CiphertextCommitmentEqualityProofData, + }, + }, + spl_pod::primitives::PodU64, +}; + +/// Confidential transfer extension information needed to construct an `EmptyAccount` instruction. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct EmptyAccountAccountInfo { + /// The available balance + pub(crate) available_balance: EncryptedBalance, +} +impl EmptyAccountAccountInfo { + /// Create the `EmptyAccount` instruction account information from `ConfidentialTransferAccount`. + pub fn new(account: &ConfidentialTransferAccount) -> Self { + Self { + available_balance: account.available_balance, + } + } + + /// Create an empty account proof data. + pub fn generate_proof_data( + &self, + elgamal_keypair: &ElGamalKeypair, + ) -> Result { + let available_balance = self + .available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + + ZeroBalanceProofData::new(elgamal_keypair, &available_balance) + .map_err(|_| TokenError::ProofGeneration) + } +} + +/// Confidential Transfer extension information needed to construct an `ApplyPendingBalance` +/// instruction. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct ApplyPendingBalanceAccountInfo { + /// The total number of `Deposit` and `Transfer` instructions that have credited + /// `pending_balance` + pub(crate) pending_balance_credit_counter: PodU64, + /// The low 16 bits of the pending balance (encrypted by `elgamal_pubkey`) + pub(crate) pending_balance_lo: EncryptedBalance, + /// The high 48 bits of the pending balance (encrypted by `elgamal_pubkey`) + pub(crate) pending_balance_hi: EncryptedBalance, + /// The decryptable available balance + pub(crate) decryptable_available_balance: DecryptableBalance, +} +impl ApplyPendingBalanceAccountInfo { + /// Create the `ApplyPendingBalance` instruction account information from + /// `ConfidentialTransferAccount`. + pub fn new(account: &ConfidentialTransferAccount) -> Self { + Self { + pending_balance_credit_counter: account.pending_balance_credit_counter, + pending_balance_lo: account.pending_balance_lo, + pending_balance_hi: account.pending_balance_hi, + decryptable_available_balance: account.decryptable_available_balance, + } + } + + /// Return the pending balance credit counter of the account. + pub fn pending_balance_credit_counter(&self) -> u64 { + self.pending_balance_credit_counter.into() + } + + fn decrypted_pending_balance_lo( + &self, + elgamal_secret_key: &ElGamalSecretKey, + ) -> Result { + let pending_balance_lo = self + .pending_balance_lo + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + elgamal_secret_key + .decrypt_u32(&pending_balance_lo) + .ok_or(TokenError::AccountDecryption) + } + + fn decrypted_pending_balance_hi( + &self, + elgamal_secret_key: &ElGamalSecretKey, + ) -> Result { + let pending_balance_hi = self + .pending_balance_hi + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + elgamal_secret_key + .decrypt_u32(&pending_balance_hi) + .ok_or(TokenError::AccountDecryption) + } + + fn decrypted_available_balance(&self, aes_key: &AeKey) -> Result { + let decryptable_available_balance = self + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + aes_key + .decrypt(&decryptable_available_balance) + .ok_or(TokenError::AccountDecryption) + } + + /// Update the decryptable available balance. + pub fn new_decryptable_available_balance( + &self, + elgamal_secret_key: &ElGamalSecretKey, + aes_key: &AeKey, + ) -> Result { + let decrypted_pending_balance_lo = self.decrypted_pending_balance_lo(elgamal_secret_key)?; + let decrypted_pending_balance_hi = self.decrypted_pending_balance_hi(elgamal_secret_key)?; + let pending_balance = + combine_balances(decrypted_pending_balance_lo, decrypted_pending_balance_hi) + .ok_or(TokenError::AccountDecryption)?; + let current_available_balance = self.decrypted_available_balance(aes_key)?; + let new_decrypted_available_balance = current_available_balance + .checked_add(pending_balance) + .unwrap(); // total balance cannot exceed `u64` + + Ok(aes_key.encrypt(new_decrypted_available_balance)) + } +} + +/// Confidential Transfer extension information needed to construct a `Withdraw` instruction. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct WithdrawAccountInfo { + /// The available balance (encrypted by `encrypiton_pubkey`) + pub available_balance: EncryptedBalance, + /// The decryptable available balance + pub decryptable_available_balance: DecryptableBalance, +} +impl WithdrawAccountInfo { + /// Create the `ApplyPendingBalance` instruction account information from + /// `ConfidentialTransferAccount`. + pub fn new(account: &ConfidentialTransferAccount) -> Self { + Self { + available_balance: account.available_balance, + decryptable_available_balance: account.decryptable_available_balance, + } + } + + fn decrypted_available_balance(&self, aes_key: &AeKey) -> Result { + let decryptable_available_balance = self + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + aes_key + .decrypt(&decryptable_available_balance) + .ok_or(TokenError::AccountDecryption) + } + + /// Create a withdraw proof data. + pub fn generate_proof_data( + &self, + withdraw_amount: u64, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + ) -> Result { + let current_available_balance = self + .available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + let current_decrypted_available_balance = self.decrypted_available_balance(aes_key)?; + + WithdrawData::new( + withdraw_amount, + elgamal_keypair, + current_decrypted_available_balance, + ¤t_available_balance, + ) + .map_err(|_| TokenError::ProofGeneration) + } + + /// Update the decryptable available balance. + pub fn new_decryptable_available_balance( + &self, + withdraw_amount: u64, + aes_key: &AeKey, + ) -> Result { + let current_decrypted_available_balance = self.decrypted_available_balance(aes_key)?; + let new_decrypted_available_balance = current_decrypted_available_balance + .checked_sub(withdraw_amount) + .ok_or(TokenError::InsufficientFunds)?; + + Ok(aes_key.encrypt(new_decrypted_available_balance)) + } +} + +/// Confidential Transfer extension information needed to construct a `Transfer` instruction. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct TransferAccountInfo { + /// The available balance (encrypted by `encrypiton_pubkey`) + pub available_balance: EncryptedBalance, + /// The decryptable available balance + pub decryptable_available_balance: DecryptableBalance, +} +impl TransferAccountInfo { + /// Create the `Transfer` instruction account information from `ConfidentialTransferAccount`. + pub fn new(account: &ConfidentialTransferAccount) -> Self { + Self { + available_balance: account.available_balance, + decryptable_available_balance: account.decryptable_available_balance, + } + } + + fn decrypted_available_balance(&self, aes_key: &AeKey) -> Result { + let decryptable_available_balance = self + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + aes_key + .decrypt(&decryptable_available_balance) + .ok_or(TokenError::AccountDecryption) + } + + /// Create a transfer proof data. + pub fn generate_transfer_proof_data( + &self, + transfer_amount: u64, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + ) -> Result { + let current_source_available_balance = self + .available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + let current_source_decrypted_available_balance = + self.decrypted_available_balance(aes_key)?; + + let default_auditor_pubkey = ElGamalPubkey::default(); + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); + + TransferData::new( + transfer_amount, + ( + current_source_decrypted_available_balance, + ¤t_source_available_balance, + ), + elgamal_keypair, + (destination_elgamal_pubkey, auditor_elgamal_pubkey), + ) + .map_err(|_| TokenError::ProofGeneration) + } + + /// Create a transfer proof data that is split into equality, ciphertext validity, and range + /// proofs. + pub fn generate_split_transfer_proof_data( + &self, + transfer_amount: u64, + source_elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + ) -> Result< + ( + CiphertextCommitmentEqualityProofData, + BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedRangeProofU128Data, + SourceDecryptHandles, + ), + TokenError, + > { + let current_available_balance = self + .available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + let current_decryptable_available_balance = self + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + + transfer_split_proof_data( + ¤t_available_balance, + ¤t_decryptable_available_balance, + transfer_amount, + source_elgamal_keypair, + aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + } + + /// Create a transfer with fee proof data + #[allow(clippy::too_many_arguments)] + pub fn generate_transfer_with_fee_proof_data( + &self, + transfer_amount: u64, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + fee_rate_basis_points: u16, + maximum_fee: u64, + ) -> Result { + let current_source_available_balance = self + .available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + let current_source_decrypted_available_balance = + self.decrypted_available_balance(aes_key)?; + + let default_auditor_pubkey = ElGamalPubkey::default(); + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); + + let fee_parameters = FeeParameters { + fee_rate_basis_points, + maximum_fee, + }; + + TransferWithFeeData::new( + transfer_amount, + ( + current_source_decrypted_available_balance, + ¤t_source_available_balance, + ), + elgamal_keypair, + (destination_elgamal_pubkey, auditor_elgamal_pubkey), + fee_parameters, + withdraw_withheld_authority_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration) + } + + /// Update the decryptable available balance. + pub fn new_decryptable_available_balance( + &self, + transfer_amount: u64, + aes_key: &AeKey, + ) -> Result { + let current_decrypted_available_balance = self.decrypted_available_balance(aes_key)?; + let new_decrypted_available_balance = current_decrypted_available_balance + .checked_sub(transfer_amount) + .ok_or(TokenError::InsufficientFunds)?; + + Ok(aes_key.encrypt(new_decrypted_available_balance)) + } +} + +fn combine_balances(balance_lo: u64, balance_hi: u64) -> Option { + balance_hi + .checked_shl(PENDING_BALANCE_LO_BIT_LENGTH)? + .checked_add(balance_lo) +} diff --git a/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs b/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs new file mode 100644 index 00000000000..660bc417f85 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs @@ -0,0 +1,652 @@ +//! Ciphertext extraction and proof related helper logic +//! +//! This submodule should be removed with the next upgrade to the Solana program + +use crate::{ + extension::{ + confidential_transfer::*, confidential_transfer_fee::EncryptedFee, + transfer_fee::TransferFee, + }, + solana_program::program_error::ProgramError, + solana_zk_token_sdk::{ + curve25519::{ + ristretto::{self, PodRistrettoPoint}, + scalar::PodScalar, + }, + instruction::{ + transfer::{TransferProofContext, TransferWithFeeProofContext}, + BatchedGroupedCiphertext2HandlesValidityProofContext, BatchedRangeProofContext, + CiphertextCommitmentEqualityProofContext, FeeSigmaProofContext, + }, + zk_token_elgamal::pod::{ + DecryptHandle, FeeEncryption, GroupedElGamalCiphertext2Handles, + GroupedElGamalCiphertext3Handles, PedersenCommitment, TransferAmountCiphertext, + }, + }, +}; + +#[cfg(feature = "serde-traits")] +use { + crate::serialization::decrypthandle_fromstr, + serde::{Deserialize, Serialize}, +}; + +/// Extract the commitment component from a grouped ciphertext with 2 handles. +/// +/// A grouped ciphertext with 2 handles consists of the following 32-bytes components that are +/// serialized in order: +/// 1. The `commitment` component that encodes the fee amount. +/// 3. The `decryption handle` component with respect to the destination public key. +/// 4. The `decryption handle` component with respect to the withdraw withheld authority public key. +/// +/// The fee commitment component consists of the first 32-byte. +pub(crate) fn extract_commitment_from_grouped_ciphertext( + transfer_amount_ciphertext: &GroupedElGamalCiphertext2Handles, +) -> PedersenCommitment { + let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); + let transfer_amount_commitment_bytes = + transfer_amount_ciphertext_bytes[..32].try_into().unwrap(); + PedersenCommitment(transfer_amount_commitment_bytes) +} + +/// Extract the transfer amount ciphertext encrypted under the source ElGamal public key. +/// +/// A transfer amount ciphertext consists of the following 32-byte components that are serialized +/// in order: +/// 1. The `commitment` component that encodes the transfer amount. +/// 2. The `decryption handle` component with respect to the source public key. +/// 3. The `decryption handle` component with respect to the destination public key. +/// 4. The `decryption handle` component with respect to the auditor public key. +/// +/// An ElGamal ciphertext for the source consists of the `commitment` component and the `decryption +/// handle` component with respect to the source. +pub fn transfer_amount_source_ciphertext( + transfer_amount_ciphertext: &TransferAmountCiphertext, +) -> ElGamalCiphertext { + let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); + + let mut source_ciphertext_bytes = [0u8; 64]; + source_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); + source_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[32..64]); + + ElGamalCiphertext(source_ciphertext_bytes) +} + +/// Extract the transfer amount ciphertext encrypted under the destination ElGamal public key. +/// +/// A transfer amount ciphertext consists of the following 32-byte components that are serialized +/// in order: +/// 1. The `commitment` component that encodes the transfer amount. +/// 2. The `decryption handle` component with respect to the source public key. +/// 3. The `decryption handle` component with respect to the destination public key. +/// 4. The `decryption handle` component with respect to the auditor public key. +/// +/// An ElGamal ciphertext for the destination consists of the `commitment` component and the +/// `decryption handle` component with respect to the destination public key. +#[cfg(feature = "zk-ops")] +pub(crate) fn transfer_amount_destination_ciphertext( + transfer_amount_ciphertext: &TransferAmountCiphertext, +) -> ElGamalCiphertext { + let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); + + let mut destination_ciphertext_bytes = [0u8; 64]; + destination_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); + destination_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[64..96]); + + ElGamalCiphertext(destination_ciphertext_bytes) +} + +/// Extract the fee amount ciphertext encrypted under the destination ElGamal public key. +/// +/// A fee encryption amount consists of the following 32-byte components that are serialized in +/// order: +/// 1. The `commitment` component that encodes the fee amount. +/// 2. The `decryption handle` component with respect to the destination public key. +/// 3. The `decryption handle` component with respect to the withdraw withheld authority public +/// key. +/// +/// An ElGamal ciphertext for the destination consists of the `commitment` component and the +/// `decryption handle` component with respect to the destination public key. +#[cfg(feature = "zk-ops")] +pub(crate) fn fee_amount_destination_ciphertext( + transfer_amount_ciphertext: &EncryptedFee, +) -> ElGamalCiphertext { + let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); + + let mut source_ciphertext_bytes = [0u8; 64]; + source_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); + source_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[32..64]); + + ElGamalCiphertext(source_ciphertext_bytes) +} + +/// Extract the transfer amount ciphertext encrypted under the withdraw withheld authority ElGamal +/// public key. +/// +/// A fee encryption amount consists of the following 32-byte components that are serialized in +/// order: +/// 1. The `commitment` component that encodes the fee amount. +/// 2. The `decryption handle` component with respect to the destination public key. +/// 3. The `decryption handle` component with respect to the withdraw withheld authority public +/// key. +/// +/// An ElGamal ciphertext for the destination consists of the `commitment` component and the +/// `decryption handle` component with respect to the withdraw withheld authority public key. +#[cfg(feature = "zk-ops")] +pub(crate) fn fee_amount_withdraw_withheld_authority_ciphertext( + transfer_amount_ciphertext: &EncryptedFee, +) -> ElGamalCiphertext { + let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); + + let mut destination_ciphertext_bytes = [0u8; 64]; + destination_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); + destination_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[64..96]); + + ElGamalCiphertext(destination_ciphertext_bytes) +} + +#[cfg(feature = "zk-ops")] +pub(crate) fn transfer_amount_encryption_from_decrypt_handle( + source_decrypt_handle: &DecryptHandle, + grouped_ciphertext: &GroupedElGamalCiphertext2Handles, +) -> TransferAmountCiphertext { + let source_decrypt_handle_bytes = bytemuck::bytes_of(source_decrypt_handle); + let grouped_ciphertext_bytes = bytemuck::bytes_of(grouped_ciphertext); + + let mut transfer_amount_ciphertext_bytes = [0u8; 128]; + transfer_amount_ciphertext_bytes[..32].copy_from_slice(&grouped_ciphertext_bytes[..32]); + transfer_amount_ciphertext_bytes[32..64].copy_from_slice(source_decrypt_handle_bytes); + transfer_amount_ciphertext_bytes[64..128].copy_from_slice(&grouped_ciphertext_bytes[32..96]); + + TransferAmountCiphertext(GroupedElGamalCiphertext3Handles( + transfer_amount_ciphertext_bytes, + )) +} + +/// The transfer public keys associated with a transfer. +#[cfg(feature = "zk-ops")] +pub struct TransferPubkeysInfo { + /// Source ElGamal public key + pub source: ElGamalPubkey, + /// Destination ElGamal public key + pub destination: ElGamalPubkey, + /// Auditor ElGamal public key + pub auditor: ElGamalPubkey, +} + +/// The proof context information needed to process a [Transfer] instruction. +#[cfg(feature = "zk-ops")] +pub struct TransferProofContextInfo { + /// Ciphertext containing the low 16 bits of the transafer amount + pub ciphertext_lo: TransferAmountCiphertext, + /// Ciphertext containing the high 32 bits of the transafer amount + pub ciphertext_hi: TransferAmountCiphertext, + /// The transfer public keys associated with a transfer + pub transfer_pubkeys: TransferPubkeysInfo, + /// The new source available balance ciphertext + pub new_source_ciphertext: ElGamalCiphertext, +} + +#[cfg(feature = "zk-ops")] +impl From for TransferProofContextInfo { + fn from(context: TransferProofContext) -> Self { + let transfer_pubkeys = TransferPubkeysInfo { + source: context.transfer_pubkeys.source, + destination: context.transfer_pubkeys.destination, + auditor: context.transfer_pubkeys.auditor, + }; + + TransferProofContextInfo { + ciphertext_lo: context.ciphertext_lo, + ciphertext_hi: context.ciphertext_hi, + transfer_pubkeys, + new_source_ciphertext: context.new_source_ciphertext, + } + } +} + +#[cfg(feature = "zk-ops")] +impl TransferProofContextInfo { + /// Create a transfer proof context information needed to process a [Transfer] instruction from + /// split proof contexts after verifying their consistency. + pub fn verify_and_extract( + equality_proof_context: &CiphertextCommitmentEqualityProofContext, + ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, + range_proof_context: &BatchedRangeProofContext, + source_decrypt_handles: &SourceDecryptHandles, + ) -> Result { + // The equality proof context consists of the source ElGamal public key, the new source + // available balance ciphertext, and the new source available commitment. The public key + // and ciphertext should be returned as parts of `TransferProofContextInfo` and the + // commitment should be checked with range proof for consistency. + let CiphertextCommitmentEqualityProofContext { + pubkey: source_pubkey, + ciphertext: new_source_ciphertext, + commitment: new_source_commitment, + } = equality_proof_context; + + // The ciphertext validity proof context consists of the destination ElGamal public key, + // auditor ElGamal public key, and the transfer amount ciphertexts. All of these fields + // should be returned as part of `TransferProofContextInfo`. In addition, the commitments + // pertaining to the transfer amount ciphertexts should be checked with range proof for + // consistency. + let BatchedGroupedCiphertext2HandlesValidityProofContext { + destination_pubkey, + auditor_pubkey, + grouped_ciphertext_lo: transfer_amount_ciphertext_lo, + grouped_ciphertext_hi: transfer_amount_ciphertext_hi, + } = ciphertext_validity_proof_context; + + // The range proof context consists of the Pedersen commitments and bit-lengths for which + // the range proof is proved. The commitments must consist of three commitments pertaining + // to the new source available balance, the low bits of the transfer amount, and high bits + // of the transfer amount. These commitments must be checked for bit lengths `64`, `16`, + // and `32`. + let BatchedRangeProofContext { + commitments: range_proof_commitments, + bit_lengths: range_proof_bit_lengths, + } = range_proof_context; + + // check that the range proof was created for the correct set of Pedersen commitments + let transfer_amount_commitment_lo = + extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_lo); + let transfer_amount_commitment_hi = + extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_hi); + + let expected_commitments = [ + *new_source_commitment, + transfer_amount_commitment_lo, + transfer_amount_commitment_hi, + // the fourth dummy commitment can be any commitment + ]; + + if !range_proof_commitments + .iter() + .zip(expected_commitments.iter()) + .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) + { + return Err(ProgramError::InvalidInstructionData); + } + + // check that the range proof was created for the correct number of bits + const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; + const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16; + const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32; + const PADDING_BIT_LENGTH: u8 = 16; + let expected_bit_lengths = [ + REMAINING_BALANCE_BIT_LENGTH, + TRANSFER_AMOUNT_LO_BIT_LENGTH, + TRANSFER_AMOUNT_HI_BIT_LENGTH, + PADDING_BIT_LENGTH, + ] + .iter(); + + if !range_proof_bit_lengths + .iter() + .zip(expected_bit_lengths) + .all(|(proof_len, expected_len)| proof_len == expected_len) + { + return Err(ProgramError::InvalidInstructionData); + } + + let transfer_pubkeys = TransferPubkeysInfo { + source: *source_pubkey, + destination: *destination_pubkey, + auditor: *auditor_pubkey, + }; + + let transfer_amount_ciphertext_lo = transfer_amount_encryption_from_decrypt_handle( + &source_decrypt_handles.lo, + transfer_amount_ciphertext_lo, + ); + + let transfer_amount_ciphertext_hi = transfer_amount_encryption_from_decrypt_handle( + &source_decrypt_handles.hi, + transfer_amount_ciphertext_hi, + ); + + Ok(Self { + ciphertext_lo: transfer_amount_ciphertext_lo, + ciphertext_hi: transfer_amount_ciphertext_hi, + transfer_pubkeys, + new_source_ciphertext: *new_source_ciphertext, + }) + } +} + +/// The transfer public keys associated with a transfer with fee. +#[cfg(feature = "zk-ops")] +pub struct TransferWithFeePubkeysInfo { + /// Source ElGamal public key + pub source: ElGamalPubkey, + /// Destination ElGamal public key + pub destination: ElGamalPubkey, + /// Auditor ElGamal public key + pub auditor: ElGamalPubkey, + /// Withdraw withheld authority public key + pub withdraw_withheld_authority: ElGamalPubkey, +} + +/// The proof context information needed to process a [Transfer] instruction with fee. +#[cfg(feature = "zk-ops")] +pub struct TransferWithFeeProofContextInfo { + /// Group encryption of the low 16 bits of the transfer amount + pub ciphertext_lo: TransferAmountCiphertext, + /// Group encryption of the high 48 bits of the transfer amount + pub ciphertext_hi: TransferAmountCiphertext, + /// The public encryption keys associated with the transfer: source, dest, auditor, and withdraw withheld authority + pub transfer_with_fee_pubkeys: TransferWithFeePubkeysInfo, + /// The final spendable ciphertext after the transfer, + pub new_source_ciphertext: ElGamalCiphertext, + /// The transfer fee encryption of the low 16 bits of the transfer fee amount + pub fee_ciphertext_lo: EncryptedFee, + /// The transfer fee encryption of the hi 32 bits of the transfer fee amount + pub fee_ciphertext_hi: EncryptedFee, +} + +#[cfg(feature = "zk-ops")] +impl From for TransferWithFeeProofContextInfo { + fn from(context: TransferWithFeeProofContext) -> Self { + let transfer_with_fee_pubkeys = TransferWithFeePubkeysInfo { + source: context.transfer_with_fee_pubkeys.source, + destination: context.transfer_with_fee_pubkeys.destination, + auditor: context.transfer_with_fee_pubkeys.auditor, + withdraw_withheld_authority: context + .transfer_with_fee_pubkeys + .withdraw_withheld_authority, + }; + + TransferWithFeeProofContextInfo { + ciphertext_lo: context.ciphertext_lo, + ciphertext_hi: context.ciphertext_hi, + transfer_with_fee_pubkeys, + new_source_ciphertext: context.new_source_ciphertext, + fee_ciphertext_lo: context.fee_ciphertext_lo, + fee_ciphertext_hi: context.fee_ciphertext_hi, + } + } +} + +#[cfg(feature = "zk-ops")] +impl TransferWithFeeProofContextInfo { + /// Create a transfer proof context information needed to process a [Transfer] instruction from + /// split proof contexts after verifying their consistency. + pub fn verify_and_extract( + equality_proof_context: &CiphertextCommitmentEqualityProofContext, + transfer_amount_ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, + fee_sigma_proof_context: &FeeSigmaProofContext, + fee_ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, + range_proof_context: &BatchedRangeProofContext, + source_decrypt_handles: &SourceDecryptHandles, + fee_parameters: &TransferFee, + ) -> Result { + // The equality proof context consists of the source ElGamal public key, the new source + // available balance ciphertext, and the new source available commitment. The public key + // and ciphertext should be returned as part of `TransferWithFeeProofContextInfo` and the + // commitment should be checked with range proof for consistency. + let CiphertextCommitmentEqualityProofContext { + pubkey: source_pubkey, + ciphertext: new_source_ciphertext, + commitment: new_source_commitment, + } = equality_proof_context; + + // The transfer amount ciphertext validity proof context consists of the destination + // ElGamal public key, auditor ElGamal public key, and the transfer amount ciphertexts. All + // of these fields should be returned as part of `TransferWithFeeProofContextInfo`. In + // addition, the commitments pertaining to the transfer amount ciphertexts should be + // checked with range proof for consistency. + let BatchedGroupedCiphertext2HandlesValidityProofContext { + destination_pubkey, + auditor_pubkey, + grouped_ciphertext_lo: transfer_amount_ciphertext_lo, + grouped_ciphertext_hi: transfer_amount_ciphertext_hi, + } = transfer_amount_ciphertext_validity_proof_context; + + // The fee sigma proof context consists of the fee commitment, delta commitment, claimed + // commitment, and max fee. The fee and claimed commitment should be checked with range + // proof for consistency. The delta commitment should be checked whether it is properly + // generated with respect to the fee parameters. The max fee should be checked for + // consistency with the fee parameters. + let FeeSigmaProofContext { + fee_commitment, + delta_commitment, + claimed_commitment, + max_fee, + } = fee_sigma_proof_context; + + let expected_maximum_fee: u64 = fee_parameters.maximum_fee.into(); + let proof_maximum_fee: u64 = (*max_fee).into(); + if expected_maximum_fee != proof_maximum_fee { + return Err(ProgramError::InvalidInstructionData); + } + + // The transfer fee ciphertext validity proof context consists of the destination ElGamal + // public key, withdraw withheld authority ElGamal public key, and the transfer fee + // ciphertexts. The rest of the fields should be return as part of + // `TransferWithFeeProofContextInfo`. In addition, the destination public key should be + // checked for consistency with the destination public key contained in the transfer amount + // ciphertext validity proof, and the commitments pertaining to the transfer fee amount + // ciphertexts should be checked with range proof for consistency. + let BatchedGroupedCiphertext2HandlesValidityProofContext { + destination_pubkey: destination_pubkey_from_transfer_fee_validity_proof, + auditor_pubkey: withdraw_withheld_authority_pubkey, + grouped_ciphertext_lo: fee_ciphertext_lo, + grouped_ciphertext_hi: fee_ciphertext_hi, + } = fee_ciphertext_validity_proof_context; + + if destination_pubkey != destination_pubkey_from_transfer_fee_validity_proof { + return Err(ProgramError::InvalidInstructionData); + } + + // The range proof context consists of the Pedersen commitments and bit-lengths for which + // the range proof is proved. The commitments must consist of seven commitments pertaining + // to + // - the new source available balance (64 bits) + // - the low bits of the transfer amount (16 bits) + // - the high bits of the transfer amount (32 bits) + // - the delta amount for the fee (48 bits) + // - the complement of the delta amount for the fee (48 bits) + // - the low bits of the fee amount (16 bits) + // - the high bits of the fee amount (32 bits) + let BatchedRangeProofContext { + commitments: range_proof_commitments, + bit_lengths: range_proof_bit_lengths, + } = range_proof_context; + + // check that the range proof was created for the correct set of Pedersen commitments + let transfer_amount_commitment_lo = + extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_lo); + let transfer_amount_commitment_hi = + extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_hi); + + let fee_commitment_lo = extract_commitment_from_grouped_ciphertext(fee_ciphertext_lo); + let fee_commitment_hi = extract_commitment_from_grouped_ciphertext(fee_ciphertext_hi); + + const MAX_FEE_BASIS_POINTS: u64 = 10_000; + let max_fee_basis_points_scalar = u64_to_scalar(MAX_FEE_BASIS_POINTS); + let max_fee_basis_points_commitment = + ristretto::multiply_ristretto(&max_fee_basis_points_scalar, &G) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + let claimed_complement_commitment = ristretto::subtract_ristretto( + &max_fee_basis_points_commitment, + &(*claimed_commitment).into(), + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + let expected_commitments = [ + *new_source_commitment, + transfer_amount_commitment_lo, + transfer_amount_commitment_hi, + *claimed_commitment, + claimed_complement_commitment.into(), + fee_commitment_lo, + fee_commitment_hi, + ]; + + if !range_proof_commitments + .iter() + .zip(expected_commitments.iter()) + .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) + { + return Err(ProgramError::InvalidInstructionData); + } + + // check that the range proof was created for the correct number of bits + const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; + const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16; + const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32; + const DELTA_BIT_LENGTH: u8 = 48; + const FEE_AMOUNT_LO_BIT_LENGTH: u8 = 16; + const FEE_AMOUNT_HI_BIT_LENGTH: u8 = 32; + + let expected_bit_lengths = [ + REMAINING_BALANCE_BIT_LENGTH, + TRANSFER_AMOUNT_LO_BIT_LENGTH, + TRANSFER_AMOUNT_HI_BIT_LENGTH, + DELTA_BIT_LENGTH, + DELTA_BIT_LENGTH, + FEE_AMOUNT_LO_BIT_LENGTH, + FEE_AMOUNT_HI_BIT_LENGTH, + ] + .iter(); + + if !range_proof_bit_lengths + .iter() + .zip(expected_bit_lengths) + .all(|(proof_len, expected_len)| proof_len == expected_len) + { + return Err(ProgramError::InvalidInstructionData); + } + + // check consistency between fee sigma and fee ciphertext validity proofs + let sigma_proof_fee_commitment_point: PodRistrettoPoint = (*fee_commitment).into(); + let validity_proof_fee_point = + combine_lo_hi_pedersen_points(&fee_commitment_lo.into(), &fee_commitment_hi.into()) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + if validity_proof_fee_point != sigma_proof_fee_commitment_point { + return Err(ProgramError::InvalidInstructionData); + } + + verify_delta_commitment( + &transfer_amount_commitment_lo, + &transfer_amount_commitment_hi, + fee_commitment, + delta_commitment, + fee_parameters.transfer_fee_basis_points.into(), + )?; + + // create transfer with fee proof context info and return + let transfer_with_fee_pubkeys = TransferWithFeePubkeysInfo { + source: *source_pubkey, + destination: *destination_pubkey, + auditor: *auditor_pubkey, + withdraw_withheld_authority: *withdraw_withheld_authority_pubkey, + }; + + let transfer_amount_ciphertext_lo = transfer_amount_encryption_from_decrypt_handle( + &source_decrypt_handles.lo, + transfer_amount_ciphertext_lo, + ); + + let transfer_amount_ciphertext_hi = transfer_amount_encryption_from_decrypt_handle( + &source_decrypt_handles.hi, + transfer_amount_ciphertext_hi, + ); + + Ok(Self { + ciphertext_lo: transfer_amount_ciphertext_lo, + ciphertext_hi: transfer_amount_ciphertext_hi, + transfer_with_fee_pubkeys, + new_source_ciphertext: *new_source_ciphertext, + fee_ciphertext_lo: FeeEncryption(*fee_ciphertext_lo), + fee_ciphertext_hi: FeeEncryption(*fee_ciphertext_hi), + }) + } +} + +/// The ElGamal ciphertext decryption handle pertaining to the low and high bits of the transfer +/// amount under the source public key of the transfer. +/// +/// The `TransferProofContext` contains decryption handles for the low and high bits of the +/// transfer amount. Howver, these decryption handles were (mistakenly) removed from the split +/// proof contexts as a form of optimization. These components should be added back into these +/// split proofs in `zk-token-sdk`. Until this modifications is made, include `SourceDecryptHandle` +/// in the transfer instruction data. +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct SourceDecryptHandles { + /// The ElGamal decryption handle pertaining to the low 16 bits of the transfer amount. + #[cfg_attr(feature = "serde-traits", serde(with = "decrypthandle_fromstr"))] + pub lo: DecryptHandle, + /// The ElGamal decryption handle pertaining to the low 32 bits of the transfer amount. + #[cfg_attr(feature = "serde-traits", serde(with = "decrypthandle_fromstr"))] + pub hi: DecryptHandle, +} + +/// Ristretto generator point for curve25519 +const G: PodRistrettoPoint = PodRistrettoPoint([ + 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, 165, + 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118, +]); + +/// Convert a `u16` amount into a curve25519 scalar +fn u16_to_scalar(amount: u16) -> PodScalar { + let mut bytes = [0u8; 32]; + bytes[..2].copy_from_slice(&amount.to_le_bytes()); + PodScalar(bytes) +} + +/// Convert a `u64` amount into a curve25519 scalar +fn u64_to_scalar(amount: u64) -> PodScalar { + let mut bytes = [0u8; 32]; + bytes[..8].copy_from_slice(&amount.to_le_bytes()); + PodScalar(bytes) +} + +/// Combine lo and hi Pedersen commitment points +fn combine_lo_hi_pedersen_points( + point_lo: &PodRistrettoPoint, + point_hi: &PodRistrettoPoint, +) -> Option { + const SCALING_CONSTANT: u64 = 65536; + let scaling_constant_scalar = u64_to_scalar(SCALING_CONSTANT); + let scaled_point_hi = ristretto::multiply_ristretto(&scaling_constant_scalar, point_hi)?; + ristretto::add_ristretto(point_lo, &scaled_point_hi) +} + +/// Compute fee delta commitment +fn verify_delta_commitment( + transfer_amount_commitment_lo: &PedersenCommitment, + transfer_amount_commitment_hi: &PedersenCommitment, + fee_commitment: &PedersenCommitment, + proof_delta_commitment: &PedersenCommitment, + transfer_fee_basis_points: u16, +) -> Result<(), ProgramError> { + let transfer_amount_point = combine_lo_hi_pedersen_points( + &(*transfer_amount_commitment_lo).into(), + &(*transfer_amount_commitment_hi).into(), + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + let transfer_fee_basis_points_scalar = u16_to_scalar(transfer_fee_basis_points); + let scaled_transfer_amount_point = + ristretto::multiply_ristretto(&transfer_fee_basis_points_scalar, &transfer_amount_point) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + const MAX_FEE_BASIS_POINTS: u64 = 10_000; + let max_fee_basis_points_scalar = u64_to_scalar(MAX_FEE_BASIS_POINTS); + let fee_point: PodRistrettoPoint = (*fee_commitment).into(); + let scaled_fee_point = ristretto::multiply_ristretto(&max_fee_basis_points_scalar, &fee_point) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + let expected_delta_commitment_point = + ristretto::subtract_ristretto(&scaled_fee_point, &scaled_transfer_amount_point) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + let proof_delta_commitment_point = (*proof_delta_commitment).into(); + if expected_delta_commitment_point != proof_delta_commitment_point { + return Err(ProgramError::InvalidInstructionData); + } + Ok(()) +} diff --git a/token/program-2022/src/extension/confidential_transfer/instruction.rs b/token/program-2022/src/extension/confidential_transfer/instruction.rs index 6b110cbe1d6..0e6c23d7f2b 100644 --- a/token/program-2022/src/extension/confidential_transfer/instruction.rs +++ b/token/program-2022/src/extension/confidential_transfer/instruction.rs @@ -1,13 +1,16 @@ #[cfg(not(target_os = "solana"))] use solana_zk_token_sdk::encryption::auth_encryption::AeCiphertext; -pub use solana_zk_token_sdk::zk_token_proof_instruction::*; +pub use solana_zk_token_sdk::{ + zk_token_proof_instruction::*, zk_token_proof_state::ProofContextState, +}; use { crate::{ check_program_account, - extension::confidential_transfer::*, + extension::confidential_transfer::{ciphertext_extraction::SourceDecryptHandles, *}, instruction::{encode_instruction, TokenInstruction}, + proof::ProofLocation, }, - bytemuck::{Pod, Zeroable}, + bytemuck::Zeroable, // `Pod` comes from zk_token_proof_instruction num_enum::{IntoPrimitive, TryFromPrimitive}, solana_program::{ instruction::{AccountMeta, Instruction}, @@ -17,7 +20,15 @@ use { }, }; +#[cfg(feature = "serde-traits")] +use { + crate::serialization::aeciphertext_fromstr, + serde::{Deserialize, Serialize}, +}; + /// Confidential Transfer extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum ConfidentialTransferInstruction { @@ -65,21 +76,26 @@ pub enum ConfidentialTransferInstruction { /// `DisableConfidentialCredits` and `DisableNonConfidentialCredits` instructions to disable. /// /// In order for this instruction to be successfully processed, it must be accompanied by the - /// `VerifyPubkey` instruction of the `zk_token_proof` program in the same transaction. + /// `VerifyPubkeyValidityProof` instruction of the `zk_token_proof` program in the same + /// transaction or the address of a context state account for the proof must be provided. /// /// Accounts expected by this instruction: /// /// * Single owner/delegate /// 0. `[writeable]` The SPL Token account. /// 1. `[]` The corresponding SPL Token mint. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidityProof` is included in the same + /// transaction or context state account if `VerifyPubkeyValidityProof` is pre-verified + /// into a context state account. /// 3. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 0. `[writeable]` The SPL Token account. /// 1. `[]` The corresponding SPL Token mint. - /// 2. `[]` The multisig source account owner. - /// 3. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidityProof` is included in the same + /// transaction or context state account if `VerifyPubkeyValidityProof` is pre-verified + /// into a context state account. + /// 3. `[]` The multisig source account owner. /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig account. /// /// Data expected by this instruction: @@ -118,16 +134,21 @@ pub enum ConfidentialTransferInstruction { /// `ConfidentialTransferInstruction::ConfigureAccount` have affected the token account. /// /// In order for this instruction to be successfully processed, it must be accompanied by the - /// `VerifyCloseAccount` instruction of the `zk_token_proof` program in the same transaction. + /// `VerifyZeroBalanceProof` instruction of the `zk_token_proof` program in the same + /// transaction or the address of a context state account for the proof must be provided. /// /// * Single owner/delegate /// 0. `[writable]` The SPL Token account. - /// 1. `[]` Instructions sysvar. + /// 1. `[]` Instructions sysvar if `VerifyZeroBalanceProof` is included in the same + /// transaction or context state account if `VerifyZeroBalanceProof` is pre-verified into + /// a context state account. /// 2. `[signer]` The single account owner. /// /// * Multisignature owner/delegate /// 0. `[writable]` The SPL Token account. - /// 1. `[]` Instructions sysvar. + /// 1. `[]` Instructions sysvar if `VerifyZeroBalanceProof` is included in the same + /// transaction or context state account if `VerifyZeroBalanceProof` is pre-verified into + /// a context state account. /// 2. `[]` The multisig account owner. /// 3.. `[signer]` Required M signer accounts for the SPL Token Multisig account. /// @@ -168,20 +189,25 @@ pub enum ConfidentialTransferInstruction { /// Fails if the associated mint is extended as `NonTransferable`. /// /// In order for this instruction to be successfully processed, it must be accompanied by the - /// `VerifyWithdraw` instruction of the `zk_token_proof` program in the same transaction. + /// `VerifyWithdraw` instruction of the `zk_token_proof` program in the same transaction or the + /// address of a context state account for the proof must be provided. /// /// Accounts expected by this instruction: /// /// * Single owner/delegate /// 0. `[writable]` The SPL Token account. /// 1. `[]` The token mint. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyWithdraw` is included in the same transaction or + /// context state account if `VerifyWithdraw` is pre-verified into a context state + /// account. /// 3. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 0. `[writable]` The SPL Token account. /// 1. `[]` The token mint. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyWithdraw` is included in the same transaction or + /// context state account if `VerifyWithdraw` is pre-verified into a context state + /// account. /// 3. `[]` The multisig source account owner. /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig account. /// @@ -194,22 +220,27 @@ pub enum ConfidentialTransferInstruction { /// /// In order for this instruction to be successfully processed, it must be accompanied by /// either the `VerifyTransfer` or `VerifyTransferWithFee` instruction of the `zk_token_proof` - /// program in the same transaction. + /// program in the same transaction or the address of a context state account for the proof + /// must be provided. /// /// Fails if the associated mint is extended as `NonTransferable`. /// /// * Single owner/delegate /// 1. `[writable]` The source SPL Token account. - /// 2. `[writable]` The destination SPL Token account. - /// 3. `[]` The token mint. - /// 4. `[]` Instructions sysvar. + /// 2. `[]` The token mint. + /// 3. `[writable]` The destination SPL Token account. + /// 4. `[]` Instructions sysvar if `VerifyTransfer` or `VerifyTransferWithFee` is included in + /// the same transaction or context state account if these proofs are pre-verified into a + /// context state account. /// 5. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 1. `[writable]` The source SPL Token account. - /// 2. `[writable]` The destination SPL Token account. - /// 3. `[]` The token mint. - /// 4. `[]` Instructions sysvar. + /// 2. `[]` The token mint. + /// 3. `[writable]` The destination SPL Token account. + /// 4. `[]` Instructions sysvar if `VerifyTransfer` or `VerifyTransferWithFee` is included in + /// the same transaction or context state account if these proofs are pre-verified into a + /// context state account. /// 5. `[]` The multisig source account owner. /// 6.. `[signer]` Required M signer accounts for the SPL Token Multisig account. /// @@ -326,9 +357,55 @@ pub enum ConfidentialTransferInstruction { /// None /// DisableNonConfidentialCredits, + + /// Transfer tokens confidentially with zero-knowledge proofs that are split into smaller + /// components. + /// + /// In order for this instruction to be successfully processed, it must be accompanied by + /// suitable zero-knowledge proof context accounts listed below. + /// + /// The same restrictions for the `Transfer` applies to `TransferWithSplitProofs`. Namely, the + /// instruction fails if the associated mint is extended as `NonTransferable`. + /// + /// * Transfer without fee + /// 1. `[writable]` The source SPL Token account. + /// 2. `[]` The token mint. + /// 3. `[writable]` The destination SPL Token account. + /// 4. `[]` Context state account for `VerifyCiphertextCommitmentEqualityProof`. + /// 5. `[]` Context state account for `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. + /// 6. `[]` Context state account for `VerifyBatchedRangeProofU128`. + /// 7. `[signer]` The source account owner. + /// If `close_split_context_state_on_execution` is set, all context state accounts must be + /// `writable` and the following additional sequence of accounts are needed: + /// 8. `[]` The destination account for lamports from the context state accounts. + /// 9. `[signer]` The context state account owner. + /// 10. `[]` The zk token proof program. + /// + /// * Transfer with fee + /// 1. `[writable]` The source SPL Token account. + /// 2. `[]` The token mint. + /// 3. `[writable]` The destination SPL Token account. + /// 4. `[]` Context state account for `VerifyCiphertextCommitmentEqualityProof`. + /// 5. `[]` Context state account for `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. + /// 6. `[]` Context state account for `VerifyFeeSigmaProof`. + /// 7. `[]` Context state account for `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. + /// 8. `[]` Context state account for `VerifyBatchedRangeProofU256`. + /// 9. `[signer]` The source account owner. + /// If `close_split_context_state_on_execution` is set, all context state accounts must be + /// `writable` and the following additional sequence of accounts are needed: + /// 10. `[]` The destination account for lamports from the context state accounts. + /// 11. `[signer]` The context state account owner. + /// 12. `[]` The zk token proof program. + /// + /// Data expected by this instruction: + /// `TransferWithSplitProofsInstructionData` + /// + TransferWithSplitProofs, } /// Data expected by `ConfidentialTransferInstruction::InitializeMint` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct InitializeMintData { @@ -343,6 +420,8 @@ pub struct InitializeMintData { } /// Data expected by `ConfidentialTransferInstruction::UpdateMint` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct UpdateMintData { @@ -354,29 +433,38 @@ pub struct UpdateMintData { } /// Data expected by `ConfidentialTransferInstruction::ConfigureAccount` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct ConfigureAccountInstructionData { /// The decryptable balance (always 0) once the configure account succeeds + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub decryptable_zero_balance: DecryptableBalance, /// The maximum number of despots and transfers that an account can receiver before the /// `ApplyPendingBalance` is executed pub maximum_pending_balance_credit_counter: PodU64, - /// Relative location of the `ProofInstruction::VerifyPubkey` instruction to the - /// `ConfigureAccount` instruction in the transaction + /// Relative location of the `ProofInstruction::ZeroBalanceProof` instruction to the + /// `ConfigureAccount` instruction in the transaction. If the offset is `0`, then use a context + /// state account for the proof. pub proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::EmptyAccount` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct EmptyAccountInstructionData { /// Relative location of the `ProofInstruction::VerifyCloseAccount` instruction to the - /// `EmptyAccount` instruction in the transaction + /// `EmptyAccount` instruction in the transaction. If the offset is `0`, then use a context + /// state account for the proof. pub proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::Deposit` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct DepositInstructionData { @@ -387,6 +475,8 @@ pub struct DepositInstructionData { } /// Data expected by `ConfidentialTransferInstruction::Withdraw` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct WithdrawInstructionData { @@ -395,24 +485,32 @@ pub struct WithdrawInstructionData { /// Expected number of base 10 digits to the right of the decimal place pub decimals: u8, /// The new decryptable balance if the withdrawal succeeds + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_decryptable_available_balance: DecryptableBalance, /// Relative location of the `ProofInstruction::VerifyWithdraw` instruction to the `Withdraw` - /// instruction in the transaction + /// instruction in the transaction. If the offset is `0`, then use a context state account for + /// the proof. pub proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::Transfer` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct TransferInstructionData { /// The new source decryptable balance if the transfer succeeds + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_source_decryptable_available_balance: DecryptableBalance, /// Relative location of the `ProofInstruction::VerifyTransfer` instruction to the - /// `Transfer` instruction in the transaction + /// `Transfer` instruction in the transaction. If the offset is `0`, then use a context state + /// account for the proof. pub proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::ApplyPendingBalance` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct ApplyPendingBalanceData { @@ -420,9 +518,81 @@ pub struct ApplyPendingBalanceData { /// `ApplyPendingBalance` instruction pub expected_pending_balance_credit_counter: PodU64, /// The new decryptable balance if the pending balance is applied successfully + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_decryptable_available_balance: DecryptableBalance, } +/// Data expected by `ConfidentialTransferInstruction::TransferWithSplitProofs` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] +#[repr(C)] +pub struct TransferWithSplitProofsInstructionData { + /// The new source decryptable balance if the transfer succeeds + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] + pub new_source_decryptable_available_balance: DecryptableBalance, + /// If true, execute no op when an associated context state account is not initialized. + /// Otherwise, fail on an uninitialized context state account. + pub no_op_on_uninitialized_split_context_state: PodBool, + /// Close associated context states after a complete execution of the transfer instruction. + pub close_split_context_state_on_execution: PodBool, + /// The ElGamal decryption handle pertaining to the low and high bits of the transfer amount. + /// This field is used when the transfer proofs are split and verified as smaller components. + /// + /// NOTE: This field is to be removed in the next Solana upgrade. + pub source_decrypt_handles: SourceDecryptHandles, +} + +/// Type for split transfer (without fee) instruction proof context state account addresses +/// intended to be used as parameters to functions. +#[derive(Clone, Copy)] +pub struct TransferSplitContextStateAccounts<'a> { + /// The context state account address for an equality proof needed for a transfer. + pub equality_proof: &'a Pubkey, + /// The context state account address for a ciphertext validity proof needed for a transfer. + pub ciphertext_validity_proof: &'a Pubkey, + /// The context state account address for a range proof needed for a transfer. + pub range_proof: &'a Pubkey, + /// The context state accounts authority + pub authority: &'a Pubkey, + /// No op if an associated split proof context state account is not initialized. + pub no_op_on_uninitialized_split_context_state: bool, + /// Accounts needed if `close_split_context_state_on_execution` flag is enabled. + pub close_split_context_state_accounts: Option>, +} + +/// Type for split transfer (with fee) instruction proof context state account addresses intended +/// to be used as parameters to functions. +#[derive(Clone, Copy)] +pub struct TransferWithFeeSplitContextStateAccounts<'a> { + /// The context state account address for an equality proof needed for a transfer with fee. + pub equality_proof: &'a Pubkey, + /// The context state account address for a transfer amount ciphertext validity proof needed + /// for a transfer with fee. + pub transfer_amount_ciphertext_validity_proof: &'a Pubkey, + /// The context state account address for a fee sigma proof needed for a transfer with fee. + pub fee_sigma_proof: &'a Pubkey, + /// The context state account address for a fee ciphertext validity proof needed for a transfer + /// with fee. + pub fee_ciphertext_validity_proof: &'a Pubkey, + /// The context state account address for a range proof needed for a transfer with fee. + pub range_proof: &'a Pubkey, + /// The context state accounts authority + pub authority: &'a Pubkey, + /// No op if an associated split proof context state account is not initialized. + pub no_op_on_uninitialized_split_context_state: bool, + /// Accounts needed if `close_split_context_state_on_execution` flag is enabled. + pub close_split_context_state_accounts: Option>, +} + +/// Accounts needed if `close_split_context_state_on_execution` flag is enabled on a transfer. +#[derive(Clone, Copy)] +pub struct CloseSplitContextStateAccounts<'a> { + /// The lamport destination account. + pub lamport_destination: &'a Pubkey, + /// The ZK Token proof program. + pub zk_token_proof_program: &'a Pubkey, +} + /// Create a `InitializeMint` instruction #[cfg(not(target_os = "solana"))] pub fn initialize_mint( @@ -454,16 +624,18 @@ pub fn update_mint( token_program_id: &Pubkey, mint: &Pubkey, authority: &Pubkey, + multisig_signers: &[&Pubkey], auto_approve_new_accounts: bool, auditor_elgamal_pubkey: Option, ) -> Result { check_program_account(token_program_id)?; - - let accounts = vec![ + let mut accounts = vec![ AccountMeta::new(*mint, false), - AccountMeta::new_readonly(*authority, true), + AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; - + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } Ok(encode_instruction( token_program_id, accounts, @@ -489,16 +661,31 @@ pub fn inner_configure_account( maximum_pending_balance_credit_counter: u64, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; + let mut accounts = vec![ AccountMeta::new(*token_account, false), AccountMeta::new_readonly(*mint, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); } @@ -519,7 +706,6 @@ pub fn inner_configure_account( /// Create a `ConfigureAccount` instruction #[allow(clippy::too_many_arguments)] #[cfg(not(target_os = "solana"))] -#[cfg(feature = "proof-program")] pub fn configure_account( token_program_id: &Pubkey, token_account: &Pubkey, @@ -528,22 +714,34 @@ pub fn configure_account( maximum_pending_balance_credit_counter: u64, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &PubkeyValidityData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_configure_account( - token_program_id, - token_account, - mint, - decryptable_zero_balance, - maximum_pending_balance_credit_counter, - authority, - multisig_signers, - 1, - )?, - #[cfg(feature = "proof-program")] - verify_pubkey_validity(proof_data), - ]) + let mut instructions = vec![inner_configure_account( + token_program_id, + token_account, + mint, + decryptable_zero_balance, + maximum_pending_balance_credit_counter, + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the `ConfigureAccount` + // instruction. This means that the proof instruction offset must be always be 1. To + // use an arbitrary proof instruction offset, use the `inner_configure_account` + // constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_pubkey_validity(None, proof_data)); + }; + + Ok(instructions) } /// Create an `ApproveAccount` instruction @@ -552,13 +750,17 @@ pub fn approve_account( account_to_approve: &Pubkey, mint: &Pubkey, authority: &Pubkey, + multisig_signers: &[&Pubkey], ) -> Result { check_program_account(token_program_id)?; - let accounts = vec![ + let mut accounts = vec![ AccountMeta::new(*account_to_approve, false), AccountMeta::new_readonly(*mint, false), - AccountMeta::new_readonly(*authority, true), + AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } Ok(encode_instruction( token_program_id, accounts, @@ -576,14 +778,26 @@ pub fn inner_empty_account( token_account: &Pubkey, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; - let mut accounts = vec![ - AccountMeta::new(*token_account, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), - ]; + let mut accounts = vec![AccountMeta::new(*token_account, false)]; + + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); @@ -601,25 +815,35 @@ pub fn inner_empty_account( } /// Create a `EmptyAccount` instruction -#[cfg(feature = "proof-program")] pub fn empty_account( token_program_id: &Pubkey, token_account: &Pubkey, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &CloseAccountData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_empty_account( - token_program_id, - token_account, - authority, - multisig_signers, - 1, - )?, // calls check_program_account - #[cfg(feature = "proof-program")] - verify_close_account(proof_data), - ]) + let mut instructions = vec![inner_empty_account( + token_program_id, + token_account, + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the `EmptyAccount` + // instruction. This means that the proof instruction offset must be always be 1. To use an + // arbitrary proof instruction offset, use the `inner_empty_account` constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_zero_balance(None, proof_data)); + }; + + Ok(instructions) } /// Create a `Deposit` instruction @@ -669,16 +893,30 @@ pub fn inner_withdraw( new_decryptable_available_balance: DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ AccountMeta::new(*token_account, false), AccountMeta::new_readonly(*mint, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); } @@ -700,7 +938,6 @@ pub fn inner_withdraw( /// Create a `Withdraw` instruction #[allow(clippy::too_many_arguments)] #[cfg(not(target_os = "solana"))] -#[cfg(feature = "proof-program")] pub fn withdraw( token_program_id: &Pubkey, token_account: &Pubkey, @@ -710,23 +947,34 @@ pub fn withdraw( new_decryptable_available_balance: AeCiphertext, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &WithdrawData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_withdraw( - token_program_id, - token_account, - mint, - amount, - decimals, - new_decryptable_available_balance.into(), - authority, - multisig_signers, - 1, - )?, // calls check_program_account - #[cfg(feature = "proof-program")] - verify_withdraw(proof_data), - ]) + let mut instructions = vec![inner_withdraw( + token_program_id, + token_account, + mint, + amount, + decimals, + new_decryptable_available_balance.into(), + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the `Withdraw` instruction. + // This means that the proof instruction offset must be always be 1. To use an arbitrary + // proof instruction offset, use the `inner_withdraw` constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_withdraw(None, proof_data)); + }; + + Ok(instructions) } /// Create a inner `Transfer` instruction @@ -736,22 +984,36 @@ pub fn withdraw( pub fn inner_transfer( token_program_id: &Pubkey, source_token_account: &Pubkey, - destination_token_account: &Pubkey, mint: &Pubkey, + destination_token_account: &Pubkey, new_source_decryptable_available_balance: DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ AccountMeta::new(*source_token_account, false), - AccountMeta::new(*destination_token_account, false), AccountMeta::new_readonly(*mint, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), + AccountMeta::new(*destination_token_account, false), ]; + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); } @@ -771,60 +1033,135 @@ pub fn inner_transfer( /// Create a `Transfer` instruction with regular (no-fee) proof #[allow(clippy::too_many_arguments)] #[cfg(not(target_os = "solana"))] -#[cfg(feature = "proof-program")] pub fn transfer( token_program_id: &Pubkey, source_token_account: &Pubkey, - destination_token_account: &Pubkey, mint: &Pubkey, + destination_token_account: &Pubkey, new_source_decryptable_available_balance: AeCiphertext, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &TransferData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_transfer( - token_program_id, - source_token_account, - destination_token_account, - mint, - new_source_decryptable_available_balance.into(), - authority, - multisig_signers, - 1, - )?, // calls check_program_account - #[cfg(feature = "proof-program")] - verify_transfer(proof_data), - ]) + let mut instructions = vec![inner_transfer( + token_program_id, + source_token_account, + mint, + destination_token_account, + new_source_decryptable_available_balance.into(), + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the `Transfer` instruction. + // This means that the proof instruction offset must be always be 1. To use an arbitrary + // proof instruction offset, use the `inner_transfer` constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_transfer(None, proof_data)); + }; + + Ok(instructions) +} + +/// Create a inner `Transfer` instruction with fee +/// +/// This instruction is suitable for use with a cross-program `invoke` +#[allow(clippy::too_many_arguments)] +pub fn inner_transfer_with_fee( + token_program_id: &Pubkey, + source_token_account: &Pubkey, + mint: &Pubkey, + destination_token_account: &Pubkey, + new_source_decryptable_available_balance: DecryptableBalance, + authority: &Pubkey, + multisig_signers: &[&Pubkey], + proof_data_location: ProofLocation, +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*source_token_account, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new(*destination_token_account, false), + ]; + + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } + + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::ConfidentialTransferExtension, + ConfidentialTransferInstruction::Transfer, + &TransferInstructionData { + new_source_decryptable_available_balance, + proof_instruction_offset, + }, + )) } /// Create a `Transfer` instruction with fee proof #[allow(clippy::too_many_arguments)] #[cfg(not(target_os = "solana"))] -#[cfg(feature = "proof-program")] pub fn transfer_with_fee( token_program_id: &Pubkey, source_token_account: &Pubkey, - destination_token_account: &Pubkey, mint: &Pubkey, + destination_token_account: &Pubkey, new_source_decryptable_available_balance: AeCiphertext, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &TransferWithFeeData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_transfer( - token_program_id, - source_token_account, - destination_token_account, - mint, - new_source_decryptable_available_balance.into(), - authority, - multisig_signers, - 1, - )?, // calls check_program_account - verify_transfer_with_fee(proof_data), - ]) + let mut instructions = vec![inner_transfer_with_fee( + token_program_id, + source_token_account, + destination_token_account, + mint, + new_source_decryptable_available_balance.into(), + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the `TransferWithFee` + // instruction. This means that the proof instruction offset must be always be 1. To + // use an arbitrary proof instruction offset, use the `inner_transfer_with_fee` + // constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_transfer_with_fee(None, proof_data)); + }; + + Ok(instructions) } /// Create a inner `ApplyPendingBalance` instruction @@ -969,3 +1306,174 @@ pub fn disable_non_confidential_credits( multisig_signers, ) } + +/// Create a `TransferWithSplitProof` instruction without fee +#[allow(clippy::too_many_arguments)] +#[cfg(not(target_os = "solana"))] +pub fn transfer_with_split_proofs( + token_program_id: &Pubkey, + source_token_account: &Pubkey, + mint: &Pubkey, + destination_token_account: &Pubkey, + new_source_decryptable_available_balance: DecryptableBalance, + source_account_authority: &Pubkey, + context_accounts: TransferSplitContextStateAccounts, + source_decrypt_handles: &SourceDecryptHandles, +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*source_token_account, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new(*destination_token_account, false), + ]; + + let close_split_context_state_on_execution = + if let Some(close_split_context_state_on_execution_accounts) = + context_accounts.close_split_context_state_accounts + { + // If `close_split_context_state_accounts` is set, then all context state accounts must + // be `writable`. + accounts.push(AccountMeta::new(*context_accounts.equality_proof, false)); + accounts.push(AccountMeta::new( + *context_accounts.ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new(*context_accounts.range_proof, false)); + accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); + accounts.push(AccountMeta::new( + *close_split_context_state_on_execution_accounts.lamport_destination, + false, + )); + accounts.push(AccountMeta::new_readonly(*context_accounts.authority, true)); + accounts.push(AccountMeta::new_readonly( + *close_split_context_state_on_execution_accounts.zk_token_proof_program, + false, + )); + true + } else { + // If `close_split_context_state_accounts` is not set, then context state accounts can + // be read-only. + accounts.push(AccountMeta::new_readonly( + *context_accounts.equality_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.range_proof, + false, + )); + accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); + + false + }; + + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::ConfidentialTransferExtension, + ConfidentialTransferInstruction::TransferWithSplitProofs, + &TransferWithSplitProofsInstructionData { + new_source_decryptable_available_balance, + no_op_on_uninitialized_split_context_state: context_accounts + .no_op_on_uninitialized_split_context_state + .into(), + close_split_context_state_on_execution: close_split_context_state_on_execution.into(), + source_decrypt_handles: *source_decrypt_handles, + }, + )) +} + +/// Create a `TransferWithSplitProof` instruction with fee +#[allow(clippy::too_many_arguments)] +#[cfg(not(target_os = "solana"))] +pub fn transfer_with_fee_and_split_proofs( + token_program_id: &Pubkey, + source_token_account: &Pubkey, + mint: &Pubkey, + destination_token_account: &Pubkey, + new_source_decryptable_available_balance: DecryptableBalance, + source_account_authority: &Pubkey, + context_accounts: TransferWithFeeSplitContextStateAccounts, + source_decrypt_handles: &SourceDecryptHandles, +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*source_token_account, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new(*destination_token_account, false), + ]; + + let close_split_context_state_on_execution = + if let Some(close_split_context_state_on_execution_accounts) = + context_accounts.close_split_context_state_accounts + { + // If `close_split_context_state_accounts` is set, then all context state accounts must + // be `writable`. + accounts.push(AccountMeta::new(*context_accounts.equality_proof, false)); + accounts.push(AccountMeta::new( + *context_accounts.transfer_amount_ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new(*context_accounts.fee_sigma_proof, false)); + accounts.push(AccountMeta::new( + *context_accounts.fee_ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new(*context_accounts.range_proof, false)); + accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); + accounts.push(AccountMeta::new( + *close_split_context_state_on_execution_accounts.lamport_destination, + false, + )); + accounts.push(AccountMeta::new_readonly(*context_accounts.authority, true)); + accounts.push(AccountMeta::new_readonly( + *close_split_context_state_on_execution_accounts.zk_token_proof_program, + false, + )); + true + } else { + // If `close_split_context_state_accounts` is not set, then context state accounts can + // be read-only. + accounts.push(AccountMeta::new_readonly( + *context_accounts.equality_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.transfer_amount_ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.fee_sigma_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.fee_ciphertext_validity_proof, + false, + )); + accounts.push(AccountMeta::new_readonly( + *context_accounts.range_proof, + false, + )); + accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); + + false + }; + + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::ConfidentialTransferExtension, + ConfidentialTransferInstruction::TransferWithSplitProofs, + &TransferWithSplitProofsInstructionData { + new_source_decryptable_available_balance, + no_op_on_uninitialized_split_context_state: context_accounts + .no_op_on_uninitialized_split_context_state + .into(), + close_split_context_state_on_execution: close_split_context_state_on_execution.into(), + source_decrypt_handles: *source_decrypt_handles, + }, + )) +} diff --git a/token/program-2022/src/extension/confidential_transfer/mod.rs b/token/program-2022/src/extension/confidential_transfer/mod.rs index e851a01cc7d..a55102656e6 100644 --- a/token/program-2022/src/extension/confidential_transfer/mod.rs +++ b/token/program-2022/src/extension/confidential_transfer/mod.rs @@ -2,22 +2,24 @@ use { crate::{ error::TokenError, extension::{Extension, ExtensionType}, - pod::*, }, bytemuck::{Pod, Zeroable}, solana_program::entrypoint::ProgramResult, solana_zk_token_sdk::zk_token_elgamal::pod::{AeCiphertext, ElGamalCiphertext, ElGamalPubkey}, + spl_pod::{ + bytemuck::pod_from_bytes, + optional_keys::{OptionalNonZeroElGamalPubkey, OptionalNonZeroPubkey}, + primitives::{PodBool, PodU64}, + }, }; /// Maximum bit length of any deposit or transfer amount /// /// Any deposit or transfer amount must be less than 2^48 -pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH: usize = 48; +pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT: u64 = (u16::MAX as u64) + (1 << 16) * (u32::MAX as u64); /// Bit length of the low bits of pending balance plaintext -pub const PENDING_BALANCE_LO_BIT_LENGTH: usize = 16; -/// Bit length of the high bits of pending balance plaintext -pub const PENDING_BALANCE_HI_BIT_LENGTH: usize = 48; +pub const PENDING_BALANCE_LO_BIT_LENGTH: u32 = 16; /// Confidential Transfer Extension instructions pub mod instruction; @@ -25,6 +27,26 @@ pub mod instruction; /// Confidential Transfer Extension processor pub mod processor; +/// Helper functions to verify zero-knowledge proofs in the Confidential Transfer Extension +pub mod verify_proof; + +/// Helper functions to generate split zero-knowledge proofs for confidential transfers in the +/// Confidential Transfer Extension. +/// +/// The logic in this submodule should belong to the `solana-zk-token-sdk` and will be removed with +/// the next upgrade to the Solana program. +#[cfg(not(target_os = "solana"))] +pub mod split_proof_generation; + +/// Confidential Transfer Extension account information needed for instructions +#[cfg(not(target_os = "solana"))] +pub mod account_info; + +/// Ciphertext extraction and proof related helper logic +/// +/// This submodule should be removed with the next upgrade to the Solana program +pub mod ciphertext_extraction; + /// ElGamal ciphertext containing an account balance pub type EncryptedBalance = ElGamalCiphertext; /// Authenticated encryption containing an account balance @@ -148,7 +170,7 @@ impl ConfidentialTransferAccount { /// A destination account can receive funds if the following conditions are satisfied: /// 1. The account is approved by the confidential transfer mint authority /// 2. The account is not disabled by the account owner - /// 3. The number of credits into the account has reached the maximum credit counter + /// 3. The number of credits into the account has not reached the maximum credit counter pub fn valid_as_destination(&self) -> ProgramResult { self.approved()?; diff --git a/token/program-2022/src/extension/confidential_transfer/processor.rs b/token/program-2022/src/extension/confidential_transfer/processor.rs index a92ab337a31..a0556415a0d 100644 --- a/token/program-2022/src/extension/confidential_transfer/processor.rs +++ b/token/program-2022/src/extension/confidential_transfer/processor.rs @@ -3,7 +3,13 @@ use { check_program_account, error::TokenError, extension::{ - confidential_transfer::{instruction::*, *}, + confidential_transfer::{ciphertext_extraction::*, instruction::*, verify_proof::*, *}, + confidential_transfer_fee::{ + ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, + EncryptedWithheldAmount, + }, + memo_transfer::{check_previous_sibling_instruction_is_memo, memo_required}, + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, StateWithExtensions, StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, @@ -12,10 +18,12 @@ use { }, solana_program::{ account_info::{next_account_info, AccountInfo}, + clock::Clock, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, + sysvar::Sysvar, }, }; // Remove feature once zk ops syscalls are enabled on all networks @@ -25,42 +33,6 @@ use { solana_zk_token_sdk::zk_token_elgamal::ops as syscall, }; -#[cfg(feature = "proof-program")] -use { - crate::extension::{ - confidential_transfer_fee::{ - ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, EncryptedFee, - EncryptedWithheldAmount, - }, - memo_transfer::{check_previous_sibling_instruction_is_memo, memo_required}, - transfer_fee::TransferFeeConfig, - }, - solana_program::instruction::Instruction, - solana_program::sysvar::instructions::get_instruction_relative, - solana_program::{clock::Clock, sysvar::Sysvar}, - solana_zk_token_sdk::zk_token_proof_program, -}; - -/// Decodes the zero-knowledge proof instruction associated with the token instruction. -/// -/// `ConfigureAccount`, `EmptyAccount`, `Withdraw`, `Transfer`, `WithdrawWithheldTokensFromMint`, -/// and `WithdrawWithheldTokensFromAccounts` instructions require corresponding zero-knowledge -/// proof instructions. -#[cfg(feature = "proof-program")] -pub(crate) fn decode_proof_instruction( - expected: ProofInstruction, - instruction: &Instruction, -) -> Result<&T, ProgramError> { - if instruction.program_id != zk_token_proof_program::id() - || ProofInstruction::decode_type(&instruction.data) != Some(expected) - { - msg!("Unexpected proof instruction"); - return Err(ProgramError::InvalidInstructionData); - } - - ProofInstruction::decode_data(&instruction.data).ok_or(ProgramError::InvalidInstructionData) -} - /// Processes an [InitializeMint] instruction. fn process_initialize_mint( accounts: &[AccountInfo], @@ -116,7 +88,6 @@ fn process_update_mint( } /// Processes a [ConfigureAccount] instruction. -#[cfg(feature = "proof-program")] fn process_configure_account( program_id: &Pubkey, accounts: &[AccountInfo], @@ -127,7 +98,11 @@ fn process_configure_account( let account_info_iter = &mut accounts.iter(); let token_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; + + // zero-knowledge proof certifies that the supplied ElGamal public key is valid + let proof_context = + verify_configure_account_proof(account_info_iter, proof_instruction_offset)?; + let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -152,20 +127,12 @@ fn process_configure_account( let mint = StateWithExtensions::::unpack(mint_data)?; let confidential_transfer_mint = mint.get_extension::()?; - // zero-knowledge proof certifies that the supplied encryption (ElGamal) public key is valid - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyPubkeyValidity, - &zkp_instruction, - )?; - // Note: The caller is expected to use the `Reallocate` instruction to ensure there is // sufficient room in their token account for the new `ConfidentialTransferAccount` extension - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.init_extension::(false)?; confidential_transfer_account.approved = confidential_transfer_mint.auto_approve_new_accounts; - confidential_transfer_account.encryption_pubkey = proof_data.pubkey; + confidential_transfer_account.elgamal_pubkey = proof_context.pubkey; confidential_transfer_account.maximum_pending_balance_credit_counter = *maximum_pending_balance_credit_counter; @@ -182,8 +149,8 @@ fn process_configure_account( confidential_transfer_account.allow_non_confidential_credits = true.into(); // if the mint is extended for fees, then initialize account for confidential transfer fees - if mint.get_extension::()? { - let mut confidential_transfer_fee_amount = + if mint.get_extension::().is_ok() { + let confidential_transfer_fee_amount = token_account.init_extension::(false)?; confidential_transfer_fee_amount.withheld_amount = EncryptedWithheldAmount::zeroed(); } @@ -212,7 +179,7 @@ fn process_approve_account(accounts: &[AccountInfo]) -> ProgramResult { maybe_confidential_transfer_mint_authority.ok_or(TokenError::NoAuthorityExists)?; if authority_info.is_signer && *authority_info.key == confidential_transfer_mint_authority { - let mut confidential_transfer_state = + let confidential_transfer_state = token_account.get_extension_mut::()?; confidential_transfer_state.approved = true.into(); Ok(()) @@ -222,7 +189,6 @@ fn process_approve_account(accounts: &[AccountInfo]) -> ProgramResult { } /// Processes an [EmptyAccount] instruction. -#[cfg(feature = "proof-program")] fn process_empty_account( program_id: &Pubkey, accounts: &[AccountInfo], @@ -230,7 +196,10 @@ fn process_empty_account( ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let token_account_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; + + // zero-knowledge proof certifies that the available balance ciphertext holds the balance of 0. + let proof_context = verify_empty_account_proof(account_info_iter, proof_instruction_offset)?; + let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -246,24 +215,16 @@ fn process_empty_account( account_info_iter.as_slice(), )?; - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; - // An `EmptyAccount` instruction must be accompanied by a zero-knowledge proof instruction that - // certifies that the available balance ciphertext holds the balance of 0. - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyCloseAccount, - &zkp_instruction, - )?; // Check that the encryption public key and ciphertext associated with the confidential // extension account are consistent with those that were actually used to generate the zkp. - if confidential_transfer_account.encryption_pubkey != proof_data.pubkey { + if confidential_transfer_account.elgamal_pubkey != proof_context.pubkey { msg!("Encryption public-key mismatch"); return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } - if confidential_transfer_account.available_balance != proof_data.ciphertext { + if confidential_transfer_account.available_balance != proof_context.ciphertext { msg!("Available balance mismatch"); return Err(ProgramError::InvalidInstructionData); } @@ -331,7 +292,7 @@ fn process_deposit( .ok_or(TokenError::Overflow)?; token_account.pack_base(); - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.valid_as_destination()?; @@ -342,12 +303,12 @@ fn process_deposit( if amount_lo > 0 { confidential_transfer_account.pending_balance_lo = syscall::add_to(&confidential_transfer_account.pending_balance_lo, amount_lo) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; } if amount_hi > 0 { confidential_transfer_account.pending_balance_hi = syscall::add_to(&confidential_transfer_account.pending_balance_hi, amount_hi) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; } confidential_transfer_account.increment_pending_balance_credit_counter()?; @@ -358,19 +319,17 @@ fn process_deposit( /// Verifies that a deposit amount is a 48-bit number and returns the least significant 16 bits and /// most significant 32 bits of the amount. #[cfg(feature = "zk-ops")] -fn verify_and_split_deposit_amount(amount: u64) -> Result<(u64, u64), TokenError> { - if amount >> MAXIMUM_DEPOSIT_TRANSFER_AMOUNT_BIT_LENGTH > 0 { +pub fn verify_and_split_deposit_amount(amount: u64) -> Result<(u64, u64), TokenError> { + if amount > MAXIMUM_DEPOSIT_TRANSFER_AMOUNT { return Err(TokenError::MaximumDepositAmountExceeded); } - let deposit_amount_lo = - amount << (64 - PENDING_BALANCE_LO_BIT_LENGTH) >> PENDING_BALANCE_HI_BIT_LENGTH; - let deposit_amount_hi = amount >> PENDING_BALANCE_LO_BIT_LENGTH; - + let deposit_amount_lo = amount & (u16::MAX as u64); + let deposit_amount_hi = amount.checked_shr(u16::BITS).unwrap(); Ok((deposit_amount_lo, deposit_amount_hi)) } /// Processes a [Withdraw] instruction. -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] fn process_withdraw( program_id: &Pubkey, accounts: &[AccountInfo], @@ -382,7 +341,11 @@ fn process_withdraw( let account_info_iter = &mut accounts.iter(); let token_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; + + // zero-knowledge proof certifies that the account has enough available balance to withdraw the + // amount. + let proof_context = verify_withdraw_proof(account_info_iter, proof_instruction_offset)?; + let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -421,21 +384,13 @@ fn process_withdraw( // Wrapped SOL withdrawals are not supported because lamports cannot be apparated. assert!(!token_account.base.is_native()); - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.valid_as_source()?; - // Zero-knowledge proof certifies that the account has enough available balance to withdraw the - // amount. - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyWithdraw, - &zkp_instruction, - )?; // Check that the encryption public key associated with the confidential extension is // consistent with the public key that was actually used to generate the zkp. - if confidential_transfer_account.encryption_pubkey != proof_data.pubkey { + if confidential_transfer_account.elgamal_pubkey != proof_context.pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } @@ -443,11 +398,11 @@ fn process_withdraw( if amount > 0 { confidential_transfer_account.available_balance = syscall::subtract_from(&confidential_transfer_account.available_balance, amount) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; } // Check that the final available balance ciphertext is consistent with the actual ciphertext // for which the zero-knowledge proof was generated for. - if confidential_transfer_account.available_balance != proof_data.final_ciphertext { + if confidential_transfer_account.available_balance != proof_context.final_ciphertext { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } @@ -462,20 +417,23 @@ fn process_withdraw( Ok(()) } -/// Processes an [Transfer] instruction. -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +/// Processes a [Transfer] or [TransferWithSplitProofs] instruction. +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "zk-ops")] fn process_transfer( program_id: &Pubkey, accounts: &[AccountInfo], new_source_decryptable_available_balance: DecryptableBalance, proof_instruction_offset: i64, + split_proof_context_state_accounts: bool, + no_op_on_uninitialized_split_context_state: bool, + close_split_context_state_on_execution: bool, + source_decrypt_handles: &SourceDecryptHandles, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; - let destination_token_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; + let destination_token_account_info = next_account_info(account_info_iter)?; check_program_account(mint_info.owner)?; let mint_data = &mint_info.data.borrow_mut(); @@ -493,171 +451,170 @@ fn process_transfer( // transfer fee is not required. // - If the mint is extended for fees and the instruction is not a self-transfer, then // transfer fee is required. - if mint.get_extension::().is_err() - || source_account_info.key == destination_token_account_info.key - { + if mint.get_extension::().is_err() { // Transfer fee is not required. Decode the zero-knowledge proof as `TransferData`. // // The zero-knowledge proof certifies that: // 1. the transfer amount is encrypted in the correct form // 2. the source account has enough balance to send the transfer amount - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyTransfer, - &zkp_instruction, + let maybe_proof_context = verify_transfer_proof( + account_info_iter, + proof_instruction_offset, + split_proof_context_state_accounts, + no_op_on_uninitialized_split_context_state, + close_split_context_state_on_execution, + source_decrypt_handles, )?; + // If `maybe_proof_context` is `None`, then this means that + // `no_op_on_uninitialized_split_context_state` is true and a required context state + // account is not yet initialized. Even if this is the case, we follow through with the + // rest of the transfer logic to perform all the necessary checks for a transfer to be + // safe. + + // If `close_split_context_state_on_execution` is `true`, then the source account authority + // info is located after the lamport destination, context state authority, and zk token + // proof program account infos. Flush out these account infos. + if close_split_context_state_on_execution && maybe_proof_context.is_none() { + let _lamport_destination_account_info = next_account_info(account_info_iter)?; + let _context_state_authority_info = next_account_info(account_info_iter)?; + let _zk_token_proof_program_info = next_account_info(account_info_iter)?; + } + + let authority_info = next_account_info(account_info_iter)?; + // Check that the auditor encryption public key associated wth the confidential mint is // consistent with what was actually used to generate the zkp. - if !confidential_transfer_mint - .auditor_encryption_pubkey - .equals(&proof_data.transfer_pubkeys.auditor_pubkey) - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + if let Some(ref proof_context) = maybe_proof_context { + if !confidential_transfer_mint + .auditor_elgamal_pubkey + .equals(&proof_context.transfer_pubkeys.auditor) + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } } - let source_ciphertext_lo = EncryptedBalance::from(( - proof_data.ciphertext_lo.commitment, - proof_data.ciphertext_lo.source_handle, - )); - let source_ciphertext_hi = EncryptedBalance::from(( - proof_data.ciphertext_hi.commitment, - proof_data.ciphertext_hi.source_handle, - )); - process_source_for_transfer( program_id, source_account_info, mint_info, authority_info, account_info_iter.as_slice(), - &proof_data.transfer_pubkeys.source_pubkey, - &source_ciphertext_lo, - &source_ciphertext_hi, - &proof_data.new_source_ciphertext, + maybe_proof_context.as_ref(), new_source_decryptable_available_balance, )?; - let destination_ciphertext_lo = EncryptedBalance::from(( - proof_data.ciphertext_lo.commitment, - proof_data.ciphertext_lo.destination_handle, - )); - let destination_ciphertext_hi = EncryptedBalance::from(( - proof_data.ciphertext_hi.commitment, - proof_data.ciphertext_hi.destination_handle, - )); - process_destination_for_transfer( destination_token_account_info, mint_info, - &proof_data.transfer_pubkeys.destination_pubkey, - &destination_ciphertext_lo, - &destination_ciphertext_hi, - None, + maybe_proof_context.as_ref(), )?; + + if maybe_proof_context.is_none() { + msg!( + "Context states not fully initialized: returning with no op; transfer is NOT yet + executed" + ); + } } else { - // Transfer fee is required. Decode the zero-knowledge proof as `TransferWithFeeData`. + // Transfer fee is required. + let transfer_fee_config = mint.get_extension::()?; + let fee_parameters = transfer_fee_config.get_epoch_fee(Clock::get()?.epoch); + + // Decode the zero-knowledge proof as `TransferWithFeeData`. // // The zero-knowledge proof certifies that: // 1. the transfer amount is encrypted in the correct form // 2. the source account has enough balance to send the transfer amount // 3. the transfer fee is computed correctly and encrypted in the correct form - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyTransferWithFee, - &zkp_instruction, + let maybe_proof_context = verify_transfer_with_fee_proof( + account_info_iter, + proof_instruction_offset, + split_proof_context_state_accounts, + no_op_on_uninitialized_split_context_state, + close_split_context_state_on_execution, + source_decrypt_handles, + fee_parameters, )?; + + // If `maybe_proof_context` is `None`, then this means that + // `no_op_on_uninitialized_split_context_state` is true and a required context state + // account is not yet initialized. Even if this is the case, we follow through with the + // rest of the transfer with fee logic to perform all the necessary checks to be safe. + + // If `close_split_context_state_on_execution` is `true`, then the source account authority + // info is located after the lamport destination, context state authority, and zk token + // proof program account infos. Flush out these account infos. + if close_split_context_state_on_execution && maybe_proof_context.is_none() { + let _lamport_destination_account_info = next_account_info(account_info_iter)?; + let _context_state_authority_info = next_account_info(account_info_iter)?; + let _zk_token_proof_program_info = next_account_info(account_info_iter)?; + } + + let authority_info = next_account_info(account_info_iter)?; + // Check that the encryption public keys associated with the mint confidential transfer and // confidential transfer fee extensions are consistent with the keys that were used to // generate the zkp. - if !confidential_transfer_mint - .auditor_encryption_pubkey - .equals(&proof_data.transfer_with_fee_pubkeys.auditor_pubkey) - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + if let Some(ref proof_context) = maybe_proof_context { + if !confidential_transfer_mint + .auditor_elgamal_pubkey + .equals(&proof_context.transfer_with_fee_pubkeys.auditor) + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } } + let confidential_transfer_fee_config = mint.get_extension::()?; - if proof_data - .transfer_with_fee_pubkeys - .withdraw_withheld_authority_pubkey - != confidential_transfer_fee_config.withdraw_withheld_authority_encryption_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - // Check that the fee parameters in the mint are consistent with what were used to generate - // the zkp. - let transfer_fee_config = mint.get_extension::()?; - let fee_parameters = transfer_fee_config.get_epoch_fee(Clock::get()?.epoch); - if u64::from(fee_parameters.maximum_fee) != u64::from(proof_data.fee_parameters.maximum_fee) - || u16::from(fee_parameters.transfer_fee_basis_points) - != u16::from(proof_data.fee_parameters.fee_rate_basis_points) - { - return Err(TokenError::FeeParametersMismatch.into()); + // Check that the withdraw withheld authority ElGamal public key in the mint is + // consistent with what was used to generate the zkp. + if let Some(ref proof_context) = maybe_proof_context { + if proof_context + .transfer_with_fee_pubkeys + .withdraw_withheld_authority + != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } } - // From the proof data, decode lo and hi transfer amounts encrypted under the source - // encryption public key - let source_transfer_amount_lo = EncryptedBalance::from(( - proof_data.ciphertext_lo.commitment, - proof_data.ciphertext_lo.source_handle, - )); - let source_transfer_amount_hi = EncryptedBalance::from(( - proof_data.ciphertext_hi.commitment, - proof_data.ciphertext_hi.source_handle, - )); - - process_source_for_transfer( + process_source_for_transfer_with_fee( program_id, source_account_info, mint_info, authority_info, account_info_iter.as_slice(), - &proof_data.transfer_with_fee_pubkeys.source_pubkey, - &source_transfer_amount_lo, - &source_transfer_amount_hi, - &proof_data.new_source_ciphertext, + maybe_proof_context.as_ref(), new_source_decryptable_available_balance, )?; - // From the proof data decode lo and hi transfer amounts encrypted under the destination - // encryption public key - let destination_transfer_amount_lo = EncryptedBalance::from(( - proof_data.ciphertext_lo.commitment, - proof_data.ciphertext_lo.destination_handle, - )); - let destination_transfer_amount_hi = EncryptedBalance::from(( - proof_data.ciphertext_hi.commitment, - proof_data.ciphertext_hi.destination_handle, - )); - - process_destination_for_transfer( + let is_self_transfer = source_account_info.key == destination_token_account_info.key; + process_destination_for_transfer_with_fee( destination_token_account_info, mint_info, - &proof_data.transfer_with_fee_pubkeys.destination_pubkey, - &destination_transfer_amount_lo, - &destination_transfer_amount_hi, - Some((&proof_data.fee_ciphertext_lo, &proof_data.fee_ciphertext_hi)), + maybe_proof_context.as_ref(), + is_self_transfer, )?; + + if maybe_proof_context.is_none() { + msg!( + "Context state not fully initialized: returning with no op; transfer is NOT yet executed" + ); + } } Ok(()) } -#[allow(clippy::too_many_arguments)] -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] fn process_source_for_transfer( program_id: &Pubkey, source_account_info: &AccountInfo, mint_info: &AccountInfo, authority_info: &AccountInfo, signers: &[AccountInfo], - source_encryption_pubkey: &EncryptionPubkey, - source_transfer_amount_lo: &EncryptedBalance, - source_transfer_amount_hi: &EncryptedBalance, - expected_new_source_available_balance: &EncryptedBalance, + maybe_proof_context: Option<&TransferProofContextInfo>, new_source_decryptable_available_balance: DecryptableBalance, ) -> ProgramResult { check_program_account(source_account_info.owner)?; @@ -681,44 +638,48 @@ fn process_source_for_transfer( return Err(TokenError::MintMismatch.into()); } - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.valid_as_source()?; - // Check that the source encryption public key is consistent with what was actually used to - // generate the zkp. - if *source_encryption_pubkey != confidential_transfer_account.encryption_pubkey { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } + if let Some(proof_context) = maybe_proof_context { + // Check that the source encryption public key is consistent with what was actually used to + // generate the zkp. + if proof_context.transfer_pubkeys.source != confidential_transfer_account.elgamal_pubkey { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - let new_source_available_balance = syscall::subtract_with_lo_hi( - &confidential_transfer_account.available_balance, - source_transfer_amount_lo, - source_transfer_amount_hi, - ) - .ok_or(ProgramError::InvalidInstructionData)?; + let source_transfer_amount_lo = + transfer_amount_source_ciphertext(&proof_context.ciphertext_lo); + let source_transfer_amount_hi = + transfer_amount_source_ciphertext(&proof_context.ciphertext_hi); - // Check that the computed available balance is consistent with what was actually used to - // generate the zkp on the client side. - if new_source_available_balance != *expected_new_source_available_balance { - return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); - } + let new_source_available_balance = syscall::subtract_with_lo_hi( + &confidential_transfer_account.available_balance, + &source_transfer_amount_lo, + &source_transfer_amount_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - confidential_transfer_account.available_balance = new_source_available_balance; - confidential_transfer_account.decryptable_available_balance = - new_source_decryptable_available_balance; + // Check that the computed available balance is consistent with what was actually used to + // generate the zkp on the client side. + if new_source_available_balance != proof_context.new_source_ciphertext { + return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); + } + + confidential_transfer_account.available_balance = new_source_available_balance; + confidential_transfer_account.decryptable_available_balance = + new_source_decryptable_available_balance; + } Ok(()) } -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] fn process_destination_for_transfer( destination_token_account_info: &AccountInfo, mint_info: &AccountInfo, - destination_encryption_pubkey: &EncryptionPubkey, - destination_transfer_amount_lo: &EncryptedBalance, - destination_transfer_amount_hi: &EncryptedBalance, - encrypted_fee: Option<(&EncryptedFee, &EncryptedFee)>, + maybe_transfer_proof_context_info: Option<&TransferProofContextInfo>, ) -> ProgramResult { check_program_account(destination_token_account_info.owner)?; let destination_token_account_data = &mut destination_token_account_info.data.borrow_mut(); @@ -737,78 +698,203 @@ fn process_destination_for_transfer( check_previous_sibling_instruction_is_memo()?; } - let mut destination_confidential_transfer_account = + let destination_confidential_transfer_account = destination_token_account.get_extension_mut::()?; destination_confidential_transfer_account.valid_as_destination()?; - if *destination_encryption_pubkey != destination_confidential_transfer_account.encryption_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - - destination_confidential_transfer_account.pending_balance_lo = syscall::add( - &destination_confidential_transfer_account.pending_balance_lo, - destination_transfer_amount_lo, - ) - .ok_or(ProgramError::InvalidInstructionData)?; - - destination_confidential_transfer_account.pending_balance_hi = syscall::add( - &destination_confidential_transfer_account.pending_balance_hi, - destination_transfer_amount_hi, - ) - .ok_or(ProgramError::InvalidInstructionData)?; - - destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + if let Some(proof_context) = maybe_transfer_proof_context_info { + if proof_context.transfer_pubkeys.destination + != destination_confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - // Process transfer fee - if let Some((ciphertext_fee_lo, ciphertext_fee_hi)) = encrypted_fee { - // Decode lo and hi fee amounts encrypted under the destination encryption public key - let destination_fee_lo: EncryptedWithheldAmount = ( - ciphertext_fee_lo.commitment, - ciphertext_fee_lo.destination_handle, - ) - .into(); - let destination_fee_hi: EncryptedWithheldAmount = ( - ciphertext_fee_hi.commitment, - ciphertext_fee_hi.destination_handle, - ) - .into(); + let destination_ciphertext_lo = + transfer_amount_destination_ciphertext(&proof_context.ciphertext_lo); + let destination_ciphertext_hi = + transfer_amount_destination_ciphertext(&proof_context.ciphertext_hi); - // Subtract the fee amount from the destination pending balance - destination_confidential_transfer_account.pending_balance_lo = syscall::subtract( + destination_confidential_transfer_account.pending_balance_lo = syscall::add( &destination_confidential_transfer_account.pending_balance_lo, - &destination_fee_lo, + &destination_ciphertext_lo, ) - .ok_or(ProgramError::InvalidInstructionData)?; - destination_confidential_transfer_account.pending_balance_hi = syscall::subtract( + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + destination_confidential_transfer_account.pending_balance_hi = syscall::add( &destination_confidential_transfer_account.pending_balance_hi, - &destination_fee_hi, + &destination_ciphertext_hi, ) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; - // Decode lo and hi fee amounts encrypted under the withdraw authority encryption public - // key - let withdraw_withheld_authority_fee_lo: EncryptedWithheldAmount = ( - ciphertext_fee_lo.commitment, - ciphertext_fee_lo.withdraw_withheld_authority_handle, - ) - .into(); - let withdraw_withheld_authority_fee_hi: EncryptedWithheldAmount = ( - ciphertext_fee_hi.commitment, - ciphertext_fee_hi.withdraw_withheld_authority_handle, + destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + } + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "zk-ops")] +fn process_source_for_transfer_with_fee( + program_id: &Pubkey, + source_account_info: &AccountInfo, + mint_info: &AccountInfo, + authority_info: &AccountInfo, + signers: &[AccountInfo], + maybe_proof_context: Option<&TransferWithFeeProofContextInfo>, + new_source_decryptable_available_balance: DecryptableBalance, +) -> ProgramResult { + check_program_account(source_account_info.owner)?; + let authority_info_data_len = authority_info.data_len(); + let token_account_data = &mut source_account_info.data.borrow_mut(); + let mut token_account = StateWithExtensionsMut::::unpack(token_account_data)?; + + Processor::validate_owner( + program_id, + &token_account.base.owner, + authority_info, + authority_info_data_len, + signers, + )?; + + if token_account.base.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + if token_account.base.mint != *mint_info.key { + return Err(TokenError::MintMismatch.into()); + } + + let confidential_transfer_account = + token_account.get_extension_mut::()?; + confidential_transfer_account.valid_as_source()?; + + if let Some(proof_context) = maybe_proof_context { + // Check that the source encryption public key is consistent with what was actually used to + // generate the zkp. + if proof_context.transfer_with_fee_pubkeys.source + != confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } + + let source_transfer_amount_lo = + transfer_amount_source_ciphertext(&proof_context.ciphertext_lo); + let source_transfer_amount_hi = + transfer_amount_source_ciphertext(&proof_context.ciphertext_hi); + + let new_source_available_balance = syscall::subtract_with_lo_hi( + &confidential_transfer_account.available_balance, + &source_transfer_amount_lo, + &source_transfer_amount_hi, ) - .into(); + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + // Check that the computed available balance is consistent with what was actually used to + // generate the zkp on the client side. + if new_source_available_balance != proof_context.new_source_ciphertext { + return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); + } + + confidential_transfer_account.available_balance = new_source_available_balance; + confidential_transfer_account.decryptable_available_balance = + new_source_decryptable_available_balance; + } - let mut destination_confidential_transfer_fee_amount = - destination_token_account.get_extension_mut::()?; + Ok(()) +} + +#[cfg(feature = "zk-ops")] +fn process_destination_for_transfer_with_fee( + destination_token_account_info: &AccountInfo, + mint_info: &AccountInfo, + maybe_proof_context: Option<&TransferWithFeeProofContextInfo>, + is_self_transfer: bool, +) -> ProgramResult { + check_program_account(destination_token_account_info.owner)?; + let destination_token_account_data = &mut destination_token_account_info.data.borrow_mut(); + let mut destination_token_account = + StateWithExtensionsMut::::unpack(destination_token_account_data)?; - // Add the fee amount to the destination withheld fee - destination_confidential_transfer_fee_amount.withheld_amount = syscall::add_with_lo_hi( - &destination_confidential_transfer_fee_amount.withheld_amount, - &withdraw_withheld_authority_fee_lo, - &withdraw_withheld_authority_fee_hi, + if destination_token_account.base.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + if destination_token_account.base.mint != *mint_info.key { + return Err(TokenError::MintMismatch.into()); + } + + if memo_required(&destination_token_account) { + check_previous_sibling_instruction_is_memo()?; + } + + let destination_confidential_transfer_account = + destination_token_account.get_extension_mut::()?; + destination_confidential_transfer_account.valid_as_destination()?; + + if let Some(proof_context) = maybe_proof_context { + if proof_context.transfer_with_fee_pubkeys.destination + != destination_confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } + + let destination_transfer_amount_lo = + transfer_amount_destination_ciphertext(&proof_context.ciphertext_lo); + let destination_transfer_amount_hi = + transfer_amount_destination_ciphertext(&proof_context.ciphertext_hi); + + destination_confidential_transfer_account.pending_balance_lo = syscall::add( + &destination_confidential_transfer_account.pending_balance_lo, + &destination_transfer_amount_lo, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + destination_confidential_transfer_account.pending_balance_hi = syscall::add( + &destination_confidential_transfer_account.pending_balance_hi, + &destination_transfer_amount_hi, ) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + + // process transfer fee + if !is_self_transfer { + // Decode lo and hi fee amounts encrypted under the destination encryption public key + let destination_fee_lo = + fee_amount_destination_ciphertext(&proof_context.fee_ciphertext_lo); + let destination_fee_hi = + fee_amount_destination_ciphertext(&proof_context.fee_ciphertext_hi); + + // Subtract the fee amount from the destination pending balance + destination_confidential_transfer_account.pending_balance_lo = syscall::subtract( + &destination_confidential_transfer_account.pending_balance_lo, + &destination_fee_lo, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + destination_confidential_transfer_account.pending_balance_hi = syscall::subtract( + &destination_confidential_transfer_account.pending_balance_hi, + &destination_fee_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + + // Decode lo and hi fee amounts encrypted under the withdraw authority encryption public + // key + let withdraw_withheld_authority_fee_lo = + fee_amount_withdraw_withheld_authority_ciphertext(&proof_context.fee_ciphertext_lo); + let withdraw_withheld_authority_fee_hi = + fee_amount_withdraw_withheld_authority_ciphertext(&proof_context.fee_ciphertext_hi); + + let destination_confidential_transfer_fee_amount = + destination_token_account.get_extension_mut::()?; + + // Add the fee amount to the destination withheld fee + destination_confidential_transfer_fee_amount.withheld_amount = syscall::add_with_lo_hi( + &destination_confidential_transfer_fee_amount.withheld_amount, + &withdraw_withheld_authority_fee_lo, + &withdraw_withheld_authority_fee_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + } } Ok(()) @@ -841,7 +927,7 @@ fn process_apply_pending_balance( account_info_iter.as_slice(), )?; - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.available_balance = syscall::add_with_lo_hi( @@ -849,7 +935,7 @@ fn process_apply_pending_balance( &confidential_transfer_account.pending_balance_lo, &confidential_transfer_account.pending_balance_hi, ) - .ok_or(ProgramError::InvalidInstructionData)?; + .ok_or(TokenError::CiphertextArithmeticFailed)?; confidential_transfer_account.actual_pending_balance_credit_counter = confidential_transfer_account.pending_balance_credit_counter; @@ -887,7 +973,7 @@ fn process_allow_confidential_credits( account_info_iter.as_slice(), )?; - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.allow_confidential_credits = allow_confidential_credits.into(); @@ -917,7 +1003,7 @@ fn process_allow_non_confidential_credits( account_info_iter.as_slice(), )?; - let mut confidential_transfer_account = + let confidential_transfer_account = token_account.get_extension_mut::()?; confidential_transfer_account.allow_non_confidential_credits = allow_non_confidential_credits.into(); @@ -955,19 +1041,14 @@ pub(crate) fn process_instruction( } ConfidentialTransferInstruction::ConfigureAccount => { msg!("ConfidentialTransferInstruction::ConfigureAccount"); - #[cfg(feature = "proof-program")] - { - let data = decode_instruction_data::(input)?; - process_configure_account( - program_id, - accounts, - &data.decryptable_zero_balance, - &data.maximum_pending_balance_credit_counter, - data.proof_instruction_offset as i64, - ) - } - #[cfg(not(feature = "proof-program"))] - Err(ProgramError::InvalidInstructionData) + let data = decode_instruction_data::(input)?; + process_configure_account( + program_id, + accounts, + &data.decryptable_zero_balance, + &data.maximum_pending_balance_credit_counter, + data.proof_instruction_offset as i64, + ) } ConfidentialTransferInstruction::ApproveAccount => { msg!("ConfidentialTransferInstruction::ApproveAccount"); @@ -975,13 +1056,8 @@ pub(crate) fn process_instruction( } ConfidentialTransferInstruction::EmptyAccount => { msg!("ConfidentialTransferInstruction::EmptyAccount"); - #[cfg(feature = "proof-program")] - { - let data = decode_instruction_data::(input)?; - process_empty_account(program_id, accounts, data.proof_instruction_offset as i64) - } - #[cfg(not(feature = "proof-program"))] - Err(ProgramError::InvalidInstructionData) + let data = decode_instruction_data::(input)?; + process_empty_account(program_id, accounts, data.proof_instruction_offset as i64) } ConfidentialTransferInstruction::Deposit => { msg!("ConfidentialTransferInstruction::Deposit"); @@ -995,7 +1071,7 @@ pub(crate) fn process_instruction( } ConfidentialTransferInstruction::Withdraw => { msg!("ConfidentialTransferInstruction::Withdraw"); - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] + #[cfg(feature = "zk-ops")] { let data = decode_instruction_data::(input)?; process_withdraw( @@ -1007,12 +1083,12 @@ pub(crate) fn process_instruction( data.proof_instruction_offset as i64, ) } - #[cfg(not(all(feature = "zk-ops", feature = "proof-program")))] + #[cfg(not(feature = "zk-ops"))] Err(ProgramError::InvalidInstructionData) } ConfidentialTransferInstruction::Transfer => { msg!("ConfidentialTransferInstruction::Transfer"); - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] + #[cfg(feature = "zk-ops")] { let data = decode_instruction_data::(input)?; process_transfer( @@ -1020,9 +1096,13 @@ pub(crate) fn process_instruction( accounts, data.new_source_decryptable_available_balance, data.proof_instruction_offset as i64, + false, + false, + false, + &SourceDecryptHandles::zeroed(), ) } - #[cfg(not(all(feature = "zk-ops", feature = "proof-program")))] + #[cfg(not(feature = "zk-ops"))] Err(ProgramError::InvalidInstructionData) } ConfidentialTransferInstruction::ApplyPendingBalance => { @@ -1056,5 +1136,32 @@ pub(crate) fn process_instruction( msg!("ConfidentialTransferInstruction::EnableNonConfidentialCredits"); process_allow_non_confidential_credits(program_id, accounts, true) } + ConfidentialTransferInstruction::TransferWithSplitProofs => { + msg!("ConfidentialTransferInstruction::TransferWithSplitProofs"); + #[cfg(feature = "zk-ops")] + { + let data = + decode_instruction_data::(input)?; + + // Remove this error on the next Solana version upgrade. + if data.close_split_context_state_on_execution.into() { + msg!("Auto-close of context state account is not yet supported"); + return Err(ProgramError::InvalidInstructionData); + } + + process_transfer( + program_id, + accounts, + data.new_source_decryptable_available_balance, + 0, + true, + data.no_op_on_uninitialized_split_context_state.into(), + data.close_split_context_state_on_execution.into(), + &data.source_decrypt_handles, + ) + } + #[cfg(not(feature = "zk-ops"))] + Err(ProgramError::InvalidInstructionData) + } } } diff --git a/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs b/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs new file mode 100644 index 00000000000..1e1955646e9 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs @@ -0,0 +1,189 @@ +//! Helper functions to generate split zero-knowledge proofs for confidential transfers in the +//! Confidential Transfer Extension. +//! +//! The logic in this submodule should belong to the `solana-zk-token-sdk` and will be removed with +//! the next upgrade to the Solana program. + +use crate::{ + extension::confidential_transfer::{ + ciphertext_extraction::{transfer_amount_source_ciphertext, SourceDecryptHandles}, + processor::verify_and_split_deposit_amount, + *, + }, + solana_zk_token_sdk::{ + encryption::{ + auth_encryption::{AeCiphertext, AeKey}, + elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + grouped_elgamal::GroupedElGamal, + pedersen::Pedersen, + }, + instruction::{ + transfer::TransferAmountCiphertext, BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedRangeProofU128Data, CiphertextCommitmentEqualityProofData, + }, + zk_token_elgamal::ops::subtract_with_lo_hi, + }, +}; + +/// The main logic to create the three split proof data for a transfer. +pub fn transfer_split_proof_data( + current_available_balance: &ElGamalCiphertext, + current_decryptable_available_balance: &AeCiphertext, + transfer_amount: u64, + source_elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, +) -> Result< + ( + CiphertextCommitmentEqualityProofData, + BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedRangeProofU128Data, + SourceDecryptHandles, + ), + TokenError, +> { + let default_auditor_pubkey = ElGamalPubkey::default(); + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); + + // Split the transfer amount into the low and high bit components. + let (transfer_amount_lo, transfer_amount_hi) = + verify_and_split_deposit_amount(transfer_amount)?; + + // Encrypt the `lo` and `hi` transfer amounts. + let (transfer_amount_grouped_ciphertext_lo, transfer_amount_opening_lo) = + TransferAmountCiphertext::new( + transfer_amount_lo, + source_elgamal_keypair.pubkey(), + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ); + + let (transfer_amount_grouped_ciphertext_hi, transfer_amount_opening_hi) = + TransferAmountCiphertext::new( + transfer_amount_hi, + source_elgamal_keypair.pubkey(), + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ); + + // Decrypt the current available balance at the source + let current_decrypted_available_balance = current_decryptable_available_balance + .decrypt(aes_key) + .ok_or(TokenError::AccountDecryption)?; + + // Compute the remaining balance at the source + let new_decrypted_available_balance = current_decrypted_available_balance + .checked_sub(transfer_amount) + .ok_or(TokenError::InsufficientFunds)?; + + // Create a new Pedersen commitment for the remaining balance at the source + let (new_available_balance_commitment, new_source_opening) = + Pedersen::new(new_decrypted_available_balance); + + // Compute the remaining balance at the source as ElGamal ciphertexts + let transfer_amount_source_ciphertext_lo = + transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_lo.into()); + let transfer_amount_source_ciphertext_hi = + transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_hi.into()); + + let current_available_balance = (*current_available_balance).into(); + let new_available_balance_ciphertext = subtract_with_lo_hi( + ¤t_available_balance, + &transfer_amount_source_ciphertext_lo, + &transfer_amount_source_ciphertext_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; + let new_available_balance_ciphertext: ElGamalCiphertext = new_available_balance_ciphertext + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; + + // generate equality proof data + let equality_proof_data = CiphertextCommitmentEqualityProofData::new( + source_elgamal_keypair, + &new_available_balance_ciphertext, + &new_available_balance_commitment, + &new_source_opening, + new_decrypted_available_balance, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // create source decrypt handle + let source_decrypt_handle_lo = + DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_lo); + let source_decrypt_handle_hi = + DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_hi); + + let source_decrypt_handles = SourceDecryptHandles { + lo: source_decrypt_handle_lo.into(), + hi: source_decrypt_handle_hi.into(), + }; + + // encrypt the transfer amount under the destination and auditor ElGamal public key + let transfer_amount_destination_auditor_ciphertext_lo = GroupedElGamal::encrypt_with( + [destination_elgamal_pubkey, auditor_elgamal_pubkey], + transfer_amount_lo, + &transfer_amount_opening_lo, + ); + let transfer_amount_destination_auditor_ciphertext_hi = GroupedElGamal::encrypt_with( + [destination_elgamal_pubkey, auditor_elgamal_pubkey], + transfer_amount_hi, + &transfer_amount_opening_hi, + ); + + // generate ciphertext validity data + let ciphertext_validity_proof_data = BatchedGroupedCiphertext2HandlesValidityProofData::new( + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + &transfer_amount_destination_auditor_ciphertext_lo, + &transfer_amount_destination_auditor_ciphertext_hi, + transfer_amount_lo, + transfer_amount_hi, + &transfer_amount_opening_lo, + &transfer_amount_opening_hi, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // generate range proof data + const REMAINING_BALANCE_BIT_LENGTH: usize = 64; + const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 16; + const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32; + const PADDING_BIT_LENGTH: usize = 16; + + let (padding_commitment, padding_opening) = Pedersen::new(0_u64); + + let range_proof_data = BatchedRangeProofU128Data::new( + vec![ + &new_available_balance_commitment, + transfer_amount_grouped_ciphertext_lo.get_commitment(), + transfer_amount_grouped_ciphertext_hi.get_commitment(), + &padding_commitment, + ], + vec![ + new_decrypted_available_balance, + transfer_amount_lo, + transfer_amount_hi, + 0, + ], + vec![ + REMAINING_BALANCE_BIT_LENGTH, + TRANSFER_AMOUNT_LO_BIT_LENGTH, + TRANSFER_AMOUNT_HI_BIT_LENGTH, + PADDING_BIT_LENGTH, + ], + vec![ + &new_source_opening, + &transfer_amount_opening_lo, + &transfer_amount_opening_hi, + &padding_opening, + ], + ) + .map_err(|_| TokenError::ProofGeneration)?; + + Ok(( + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + source_decrypt_handles, + )) +} diff --git a/token/program-2022/src/extension/confidential_transfer/verify_proof.rs b/token/program-2022/src/extension/confidential_transfer/verify_proof.rs new file mode 100644 index 00000000000..67c3ed09a8b --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/verify_proof.rs @@ -0,0 +1,593 @@ +use { + crate::{ + check_system_program_account, check_zk_token_proof_program_account, + extension::{ + confidential_transfer::{ciphertext_extraction::*, instruction::*, *}, + transfer_fee::TransferFee, + }, + proof::decode_proof_instruction_context, + }, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + msg, + program::invoke, + program_error::ProgramError, + sysvar::instructions::get_instruction_relative, + }, + solana_zk_token_sdk::zk_token_proof_instruction::{self, ContextStateInfo}, + std::slice::Iter, +}; + +/// Verify zero-knowledge proof needed for a [ConfigureAccount] instruction and return the +/// corresponding proof context. +pub fn verify_configure_account_proof( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, + proof_instruction_offset: i64, +) -> Result { + if proof_instruction_offset == 0 { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_token_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = pod_from_bytes::>( + &context_state_account_data, + )?; + + if context_state.proof_type != ProofType::PubkeyValidity.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(context_state.proof_context) + } else { + // interpret `account_info` as a sysvar + let sysvar_account_info = next_account_info(account_info_iter)?; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + Ok(*decode_proof_instruction_context::< + PubkeyValidityData, + PubkeyValidityProofContext, + >( + ProofInstruction::VerifyPubkeyValidity, &zkp_instruction + )?) + } +} + +/// Verify zero-knowledge proof needed for a [EmptyAccount] instruction and return the +/// corresponding proof context. +pub fn verify_empty_account_proof( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, + proof_instruction_offset: i64, +) -> Result { + if proof_instruction_offset == 0 { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_token_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = pod_from_bytes::>( + &context_state_account_data, + )?; + + if context_state.proof_type != ProofType::ZeroBalance.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(context_state.proof_context) + } else { + // interpret `account_info` as a sysvar + let sysvar_account_info = next_account_info(account_info_iter)?; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + Ok(*decode_proof_instruction_context::< + ZeroBalanceProofData, + ZeroBalanceProofContext, + >( + ProofInstruction::VerifyZeroBalance, &zkp_instruction + )?) + } +} + +/// Verify zero-knowledge proof needed for a [Withdraw] instruction and return the +/// corresponding proof context. +pub fn verify_withdraw_proof( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, + proof_instruction_offset: i64, +) -> Result { + if proof_instruction_offset == 0 { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_token_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = + pod_from_bytes::>(&context_state_account_data)?; + + if context_state.proof_type != ProofType::Withdraw.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(context_state.proof_context) + } else { + // interpret `account_info` as a sysvar + let sysvar_account_info = next_account_info(account_info_iter)?; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + Ok(*decode_proof_instruction_context::< + WithdrawData, + WithdrawProofContext, + >( + ProofInstruction::VerifyWithdraw, &zkp_instruction + )?) + } +} + +/// Verify zero-knowledge proof needed for a [Transfer] instruction without fee and return the +/// corresponding proof context. +/// +/// This returns a `Result` type for an `Option` type. If the proof +/// verification fails, then the function returns a suitable error variant. If the proof succeeds +/// to verify, then the function returns a `TransferProofContextInfo` that is wrapped inside +/// `Ok(Some(TransferProofContextInfo))`. If `no_op_on_split_proof_context_state` is `true` and +/// some a split context state account is not initialized, then it returns `Ok(None)`. +#[cfg(feature = "zk-ops")] +pub fn verify_transfer_proof( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, + proof_instruction_offset: i64, + split_proof_context_state_accounts: bool, + no_op_on_split_proof_context_state: bool, + close_split_context_state_on_execution: bool, + source_decrypt_handles: &SourceDecryptHandles, +) -> Result, ProgramError> { + if proof_instruction_offset == 0 && split_proof_context_state_accounts { + let equality_proof_context_state_account_info = next_account_info(account_info_iter)?; + let ciphertext_validity_proof_context_state_account_info = + next_account_info(account_info_iter)?; + let range_proof_context_state_account_info = next_account_info(account_info_iter)?; + + if no_op_on_split_proof_context_state + && check_system_program_account(equality_proof_context_state_account_info.owner).is_ok() + { + msg!("Equality proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account( + ciphertext_validity_proof_context_state_account_info.owner, + ) + .is_ok() + { + msg!("Ciphertext validity proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account(range_proof_context_state_account_info.owner).is_ok() + { + msg!("Range proof context state account not initialized"); + return Ok(None); + } + + let equality_proof_context = + verify_equality_proof(equality_proof_context_state_account_info)?; + let ciphertext_validity_proof_context = + verify_ciphertext_validity_proof(ciphertext_validity_proof_context_state_account_info)?; + let range_proof_context = + verify_transfer_range_proof(range_proof_context_state_account_info)?; + + // The `TransferProofContextInfo` constructor verifies the consistency of the + // individual proof context and generates a `TransferWithFeeProofInfo` struct that is used + // to process the rest of the token-2022 logic. + let transfer_proof_context = TransferProofContextInfo::verify_and_extract( + &equality_proof_context, + &ciphertext_validity_proof_context, + &range_proof_context, + source_decrypt_handles, + )?; + + if close_split_context_state_on_execution { + let lamport_destination_account_info = next_account_info(account_info_iter)?; + let context_state_account_authority_info = next_account_info(account_info_iter)?; + + msg!("Closing equality proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: equality_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + equality_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing ciphertext validity proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: ciphertext_validity_proof_context_state_account_info + .key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + ciphertext_validity_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing range proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: range_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + range_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + } + + Ok(Some(transfer_proof_context)) + } else if proof_instruction_offset == 0 && !split_proof_context_state_accounts { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_token_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = + pod_from_bytes::>(&context_state_account_data)?; + + if context_state.proof_type != ProofType::Transfer.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(Some(context_state.proof_context.into())) + } else { + // interpret `account_info` as sysvar + let sysvar_account_info = next_account_info(account_info_iter)?; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + let proof_context = (*decode_proof_instruction_context::< + TransferData, + TransferProofContext, + >(ProofInstruction::VerifyTransfer, &zkp_instruction)?) + .into(); + + Ok(Some(proof_context)) + } +} + +/// Verify zero-knowledge proof needed for a [Transfer] instruction with fee and return the +/// corresponding proof context. +#[cfg(feature = "zk-ops")] +pub fn verify_transfer_with_fee_proof( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, + proof_instruction_offset: i64, + split_proof_context_state_accounts: bool, + no_op_on_split_proof_context_state: bool, + close_split_context_state_on_execution: bool, + source_decrypt_handles: &SourceDecryptHandles, + fee_parameters: &TransferFee, +) -> Result, ProgramError> { + if proof_instruction_offset == 0 && split_proof_context_state_accounts { + let equality_proof_context_state_account_info = next_account_info(account_info_iter)?; + let transfer_amount_ciphertext_validity_proof_context_state_account_info = + next_account_info(account_info_iter)?; + let fee_sigma_proof_context_state_account_info = next_account_info(account_info_iter)?; + let fee_ciphertext_validity_proof_context_state_account_info = + next_account_info(account_info_iter)?; + let range_proof_context_state_account_info = next_account_info(account_info_iter)?; + + if no_op_on_split_proof_context_state + && check_system_program_account(equality_proof_context_state_account_info.owner).is_ok() + { + msg!("Equality proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account( + transfer_amount_ciphertext_validity_proof_context_state_account_info.owner, + ) + .is_ok() + { + msg!("Transfer amount ciphertext validity proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account(fee_sigma_proof_context_state_account_info.owner) + .is_ok() + { + msg!("Fee sigma proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account( + fee_ciphertext_validity_proof_context_state_account_info.owner, + ) + .is_ok() + { + msg!("Fee ciphertext validity proof context state account not initialized"); + return Ok(None); + } + + if no_op_on_split_proof_context_state + && check_system_program_account(range_proof_context_state_account_info.owner).is_ok() + { + msg!("Range proof context state account not initialized"); + return Ok(None); + } + + let equality_proof_context = + verify_equality_proof(equality_proof_context_state_account_info)?; + let transfer_amount_ciphertext_validity_proof_context = verify_ciphertext_validity_proof( + transfer_amount_ciphertext_validity_proof_context_state_account_info, + )?; + let fee_sigma_proof_context = + verify_fee_sigma_proof(fee_sigma_proof_context_state_account_info)?; + let fee_ciphertext_validity_proof_context = verify_ciphertext_validity_proof( + fee_ciphertext_validity_proof_context_state_account_info, + )?; + let range_proof_context = + verify_transfer_with_fee_range_proof(range_proof_context_state_account_info)?; + + // The `TransferWithFeeProofContextInfo` constructor verifies the consistency of the + // individual proof context and generates a `TransferWithFeeProofInfo` struct that is used + // to process the rest of the token-2022 logic. The consistency check includes verifying + // whether the fee-related zkps were generated with respect to the correct fee parameter + // that is stored in the mint extension. + let transfer_with_fee_proof_context = TransferWithFeeProofContextInfo::verify_and_extract( + &equality_proof_context, + &transfer_amount_ciphertext_validity_proof_context, + &fee_sigma_proof_context, + &fee_ciphertext_validity_proof_context, + &range_proof_context, + source_decrypt_handles, + fee_parameters, + )?; + + if close_split_context_state_on_execution { + let lamport_destination_account_info = next_account_info(account_info_iter)?; + let context_state_account_authority_info = next_account_info(account_info_iter)?; + + msg!("Closing equality proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: equality_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + equality_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing transfer amount ciphertext validity proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: + transfer_amount_ciphertext_validity_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + transfer_amount_ciphertext_validity_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing fee sigma proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: fee_sigma_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + fee_sigma_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing fee ciphertext validity proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: + fee_ciphertext_validity_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + fee_ciphertext_validity_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + + msg!("Closing range proof context state account"); + invoke( + &zk_token_proof_instruction::close_context_state( + ContextStateInfo { + context_state_account: range_proof_context_state_account_info.key, + context_state_authority: context_state_account_authority_info.key, + }, + lamport_destination_account_info.key, + ), + &[ + range_proof_context_state_account_info.clone(), + lamport_destination_account_info.clone(), + context_state_account_authority_info.clone(), + ], + )?; + } + + Ok(Some(transfer_with_fee_proof_context)) + } else if proof_instruction_offset == 0 && !split_proof_context_state_accounts { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_token_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = pod_from_bytes::>( + &context_state_account_data, + )?; + + if context_state.proof_type != ProofType::TransferWithFee.into() { + return Err(ProgramError::InvalidInstructionData); + } + + let proof_tranfer_fee_basis_points: u16 = context_state + .proof_context + .fee_parameters + .fee_rate_basis_points + .into(); + let proof_maximum_fee: u64 = context_state + .proof_context + .fee_parameters + .maximum_fee + .into(); + + // check consistency of the transfer fee parameters in the mint extension with what were + // used to generate the zkp, which is not checked in the + // `From` implementation for + // `TransferWithFeeProofContextInfo`. + if u16::from(fee_parameters.transfer_fee_basis_points) != proof_tranfer_fee_basis_points + || u64::from(fee_parameters.maximum_fee) != proof_maximum_fee + { + return Err(TokenError::FeeParametersMismatch.into()); + } + + Ok(Some(context_state.proof_context.into())) + } else { + // interpret `account_info` as sysvar + let sysvar_account_info = next_account_info(account_info_iter)?; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + let proof_context = decode_proof_instruction_context::< + TransferWithFeeData, + TransferWithFeeProofContext, + >(ProofInstruction::VerifyTransferWithFee, &zkp_instruction)?; + + let proof_tranfer_fee_basis_points: u16 = + proof_context.fee_parameters.fee_rate_basis_points.into(); + let proof_maximum_fee: u64 = proof_context.fee_parameters.maximum_fee.into(); + + // check consistency of the transfer fee parameters in the mint extension with what were + // used to generate the zkp, which is not checked in the + // `From` implementation for + // `TransferWithFeeProofContextInfo`. + if u16::from(fee_parameters.transfer_fee_basis_points) != proof_tranfer_fee_basis_points + || u64::from(fee_parameters.maximum_fee) != proof_maximum_fee + { + return Err(TokenError::FeeParametersMismatch.into()); + } + + Ok(Some((*proof_context).into())) + } +} + +/// Verify and process equality proof for [Transfer] and [TransferWithFee] instructions. +fn verify_equality_proof( + account_info: &AccountInfo<'_>, +) -> Result { + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let equality_proof_context_state = pod_from_bytes::< + ProofContextState, + >(&context_state_account_data)?; + + if equality_proof_context_state.proof_type != ProofType::CiphertextCommitmentEquality.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(equality_proof_context_state.proof_context) +} + +/// Verify and process ciphertext validity proof for [Transfer] and [TransferWithFee] instructions. +fn verify_ciphertext_validity_proof( + account_info: &AccountInfo<'_>, +) -> Result { + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let ciphertext_validity_proof_context_state = pod_from_bytes::< + ProofContextState, + >(&context_state_account_data)?; + + if ciphertext_validity_proof_context_state.proof_type + != ProofType::BatchedGroupedCiphertext2HandlesValidity.into() + { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(ciphertext_validity_proof_context_state.proof_context) +} + +/// Verify and process range proof for [Transfer] instruction. +fn verify_transfer_range_proof( + account_info: &AccountInfo<'_>, +) -> Result { + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let range_proof_context_state = + pod_from_bytes::>(&context_state_account_data)?; + + if range_proof_context_state.proof_type != ProofType::BatchedRangeProofU128.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(range_proof_context_state.proof_context) +} + +/// Verify and process range proof for [Transfer] instruction with fee. +fn verify_transfer_with_fee_range_proof( + account_info: &AccountInfo<'_>, +) -> Result { + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let range_proof_context_state = + pod_from_bytes::>(&context_state_account_data)?; + + if range_proof_context_state.proof_type != ProofType::BatchedRangeProofU256.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(range_proof_context_state.proof_context) +} + +/// Verify and process fee sigma proof for [TransferWithFee] instruction. +fn verify_fee_sigma_proof( + account_info: &AccountInfo<'_>, +) -> Result { + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let fee_sigma_proof_context_state = + pod_from_bytes::>(&context_state_account_data)?; + + if fee_sigma_proof_context_state.proof_type != ProofType::FeeSigma.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(fee_sigma_proof_context_state.proof_context) +} diff --git a/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs b/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs new file mode 100644 index 00000000000..720b3081838 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs @@ -0,0 +1,59 @@ +use { + crate::{error::TokenError, extension::confidential_transfer_fee::EncryptedWithheldAmount}, + bytemuck::{Pod, Zeroable}, + solana_zk_token_sdk::{ + encryption::{ + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + pedersen::PedersenOpening, + }, + instruction::ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProofData, + }, +}; + +/// Confidential transfer fee extension information needed to construct a +/// `WithdrawWithheldTokensFromMint` or `WithdrawWithheldTokensFromAccounts` instruction. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct WithheldTokensInfo { + /// The available balance + pub(crate) withheld_amount: EncryptedWithheldAmount, +} +impl WithheldTokensInfo { + /// Create a `WithheldTokensInfo` from an ElGamal ciphertext. + pub fn new(withheld_amount: &EncryptedWithheldAmount) -> Self { + Self { + withheld_amount: *withheld_amount, + } + } + + /// Create withdraw withheld proof data. + pub fn generate_proof_data( + &self, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, + destination_elgamal_pubkey: &ElGamalPubkey, + ) -> Result { + let withheld_amount_in_mint: ElGamalCiphertext = self + .withheld_amount + .try_into() + .map_err(|_| TokenError::AccountDecryption)?; + + let decrypted_withheld_amount_in_mint = withheld_amount_in_mint + .decrypt_u32(withdraw_withheld_authority_elgamal_keypair.secret()) + .ok_or(TokenError::AccountDecryption)?; + + let destination_opening = PedersenOpening::new_rand(); + + let destination_ciphertext = destination_elgamal_pubkey + .encrypt_with(decrypted_withheld_amount_in_mint, &destination_opening); + + CiphertextCiphertextEqualityProofData::new( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + &withheld_amount_in_mint, + &destination_ciphertext, + &destination_opening, + decrypted_withheld_amount_in_mint, + ) + .map_err(|_| TokenError::ProofGeneration) + } +} diff --git a/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs b/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs index 80d4e103b62..11831701785 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs @@ -1,12 +1,15 @@ -#[cfg(feature = "proof-program")] -use crate::extension::confidential_transfer::instruction::{ - verify_withdraw_withheld_tokens, WithdrawWithheldTokensData, -}; use { crate::{ check_program_account, + error::TokenError, + extension::confidential_transfer::{ + instruction::{ + verify_ciphertext_ciphertext_equality, CiphertextCiphertextEqualityProofData, + }, + DecryptableBalance, + }, instruction::{encode_instruction, TokenInstruction}, - pod::OptionalNonZeroPubkey, + proof::ProofLocation, solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, }, bytemuck::{Pod, Zeroable}, @@ -17,10 +20,19 @@ use { pubkey::Pubkey, sysvar, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, std::convert::TryFrom, }; +#[cfg(feature = "serde-traits")] +use { + crate::serialization::{aeciphertext_fromstr, elgamalpubkey_fromstr}, + serde::{Deserialize, Serialize}, +}; + /// Confidential Transfer extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum ConfidentialTransferFeeInstruction { @@ -46,9 +58,12 @@ pub enum ConfidentialTransferFeeInstruction { /// Transfer all withheld confidential tokens in the mint to an account. Signed by the mint's /// withdraw withheld tokens authority. /// + /// The withheld confidential tokens are aggregated directly into the destination available + /// balance. + /// /// In order for this instruction to be successfully processed, it must be accompanied by the - /// `VerifyWithdrawWithheldTokens` instruction of the `zk_token_proof` program in the same - /// transaction. + /// `VerifyCiphertextCiphertextEquality` instruction of the `zk_token_proof` program in the + /// same transaction or the address of a context state account for the proof must be provided. /// /// Accounts expected by this instruction: /// @@ -56,14 +71,18 @@ pub enum ConfidentialTransferFeeInstruction { /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension. /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and /// `ConfidentialTransferAccount` extensions. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyCiphertextCiphertextEquality` is included in the same + /// transaction or context state account if `VerifyCiphertextCiphertextEquality` is + /// pre-verified into a context state account. /// 3. `[signer]` The mint's `withdraw_withheld_authority`. /// /// * Multisignature owner/delegate /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension. /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and /// `ConfidentialTransferAccount` extensions. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyCiphertextCiphertextEquality` is included in the same + /// transaction or context state account if `VerifyCiphertextCiphertextEquality` is + /// pre-verified into a context state account. /// 3. `[]` The mint's multisig `withdraw_withheld_authority`. /// 4. ..3+M `[signer]` M signer accounts. /// @@ -76,6 +95,9 @@ pub enum ConfidentialTransferFeeInstruction { /// authority. This instruction is susceptible to front-running. Use /// `HarvestWithheldTokensToMint` and `WithdrawWithheldTokensFromMint` as an alternative. /// + /// The withheld confidential tokens are aggregated directly into the destination available + /// balance. + /// /// Note on front-running: This instruction requires a zero-knowledge proof verification /// instruction that is checked with respect to the account state (the currently withheld /// fees). Suppose that a withdraw withheld authority generates the @@ -93,7 +115,7 @@ pub enum ConfidentialTransferFeeInstruction { /// /// In order for this instruction to be successfully processed, it must be accompanied by the /// `VerifyWithdrawWithheldTokens` instruction of the `zk_token_proof` program in the same - /// transaction. + /// transaction or the address of a context state account for the proof must be provided. /// /// Accounts expected by this instruction: /// @@ -101,7 +123,9 @@ pub enum ConfidentialTransferFeeInstruction { /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension. /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and /// `ConfidentialTransferAccount` extensions. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyCiphertextCiphertextEquality` is included in the + /// same transaction or context state account if `VerifyCiphertextCiphertextEquality` is + /// pre-verified into a context state account. /// 3. `[signer]` The mint's `withdraw_withheld_authority`. /// 4. ..3+N `[writable]` The source accounts to withdraw from. /// @@ -109,10 +133,12 @@ pub enum ConfidentialTransferFeeInstruction { /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension. /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` and /// `ConfidentialTransferAccount` extensions. - /// 2. `[]` Instructions sysvar. + /// 2. `[]` Instructions sysvar if `VerifyCiphertextCiphertextEquality` is included in the + /// same transaction or context state account if `VerifyCiphertextCiphertextEquality` is + /// pre-verified into a context state account. /// 3. `[]` The mint's multisig `withdraw_withheld_authority`. /// 4. ..4+M `[signer]` M signer accounts. - /// 4+M+1. ..3+M+N `[writable]` The source accounts to withdraw from. + /// 4+M+1. ..4+M+N `[writable]` The source accounts to withdraw from. /// /// Data expected by this instruction: /// WithdrawWithheldTokensFromAccountsData @@ -135,9 +161,45 @@ pub enum ConfidentialTransferFeeInstruction { /// None /// HarvestWithheldTokensToMint, + + /// Configure a confidential transfer fee mint to accept harvested confidential fees. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The token mint. + /// 1. `[signer]` The confidential transfer fee authority. + /// + /// *Multisignature owner/delegate + /// 0. `[writable]` The token mint. + /// 1. `[]` The confidential transfer fee multisig authority, + /// 2. `[signer]` Required M signer accounts for the SPL Token Multisig account. + /// + /// Data expected by this instruction: + /// None + EnableHarvestToMint, + + /// Configure a confidential transfer fee mint to reject any harvested confidential fees. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The token mint. + /// 1. `[signer]` The confidential transfer fee authority. + /// + /// *Multisignature owner/delegate + /// 0. `[writable]` The token mint. + /// 1. `[]` The confidential transfer fee multisig authority, + /// 2. `[signer]` Required M signer accounts for the SPL Token Multisig account. + /// + /// Data expected by this instruction: + /// None + DisableHarvestToMint, } /// Data expected by `InitializeConfidentialTransferFeeConfig` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct InitializeConfidentialTransferFeeConfigData { @@ -145,27 +207,40 @@ pub struct InitializeConfidentialTransferFeeConfigData { pub authority: OptionalNonZeroPubkey, /// ElGamal public key used to encrypt withheld fees. + #[cfg_attr(feature = "serde-traits", serde(with = "elgamalpubkey_fromstr"))] pub withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey, } /// Data expected by `ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromMint` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct WithdrawWithheldTokensFromMintData { /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the - /// `WithdrawWithheldTokensFromMint` instruction in the transaction + /// `WithdrawWithheldTokensFromMint` instruction in the transaction. If the offset is `0`, then + /// use a context state account for the proof. pub proof_instruction_offset: i8, + /// The new decryptable balance in the destination token account. + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] + pub new_decryptable_available_balance: DecryptableBalance, } /// Data expected by `ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromAccounts` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] pub struct WithdrawWithheldTokensFromAccountsData { /// Number of token accounts harvested pub num_token_accounts: u8, /// Relative location of the `ProofInstruction::VerifyWithdrawWithheld` instruction to the - /// `VerifyWithdrawWithheldTokensFromAccounts` instruction in the transaction + /// `VerifyWithdrawWithheldTokensFromAccounts` instruction in the transaction. If the offset is + /// `0`, then use a context state account for the proof. pub proof_instruction_offset: i8, + /// The new decryptable balance in the destination token account. + #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] + pub new_decryptable_available_balance: DecryptableBalance, } /// Create a `InitializeConfidentialTransferFeeConfig` instruction @@ -197,18 +272,33 @@ pub fn inner_withdraw_withheld_tokens_from_mint( token_program_id: &Pubkey, mint: &Pubkey, destination: &Pubkey, + new_decryptable_available_balance: &DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ AccountMeta::new(*mint, false), AccountMeta::new(*destination, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new(**multisig_signer, false)); } @@ -216,49 +306,65 @@ pub fn inner_withdraw_withheld_tokens_from_mint( Ok(encode_instruction( token_program_id, accounts, - TokenInstruction::ConfidentialTransferExtension, + TokenInstruction::ConfidentialTransferFeeExtension, ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromMint, &WithdrawWithheldTokensFromMintData { proof_instruction_offset, + new_decryptable_available_balance: *new_decryptable_available_balance, }, )) } /// Create an `WithdrawWithheldTokensFromMint` instruction -#[cfg(feature = "proof-program")] pub fn withdraw_withheld_tokens_from_mint( token_program_id: &Pubkey, mint: &Pubkey, destination: &Pubkey, + new_decryptable_available_balance: &DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data: &WithdrawWithheldTokensData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_withdraw_withheld_tokens_from_mint( - token_program_id, - mint, - destination, - authority, - multisig_signers, - 1, - )?, - #[cfg(feature = "proof-program")] - verify_withdraw_withheld_tokens(proof_data), - ]) + let mut instructions = vec![inner_withdraw_withheld_tokens_from_mint( + token_program_id, + mint, + destination, + new_decryptable_available_balance, + authority, + multisig_signers, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the + // `WithdrawWithheldTokensFromMint` instruction. This means that the proof instruction + // offset must be always be 1. To use an arbitrary proof instruction offset, use the + // `inner_withdraw_withheld_tokens_from_mint` constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_ciphertext_ciphertext_equality(None, proof_data)); + }; + + Ok(instructions) } /// Create an inner `WithdrawWithheldTokensFromMint` instruction /// /// This instruction is suitable for use with a cross-program `invoke` +#[allow(clippy::too_many_arguments)] pub fn inner_withdraw_withheld_tokens_from_accounts( token_program_id: &Pubkey, mint: &Pubkey, destination: &Pubkey, + new_decryptable_available_balance: &DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], sources: &[&Pubkey], - proof_instruction_offset: i8, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let num_token_accounts = @@ -266,10 +372,24 @@ pub fn inner_withdraw_withheld_tokens_from_accounts( let mut accounts = vec![ AccountMeta::new(*mint, false), AccountMeta::new(*destination, false), - AccountMeta::new_readonly(sysvar::instructions::id(), false), - AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), ]; + let proof_instruction_offset = match proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + for multisig_signer in multisig_signers.iter() { accounts.push(AccountMeta::new(**multisig_signer, false)); } @@ -281,39 +401,54 @@ pub fn inner_withdraw_withheld_tokens_from_accounts( Ok(encode_instruction( token_program_id, accounts, - TokenInstruction::ConfidentialTransferExtension, + TokenInstruction::ConfidentialTransferFeeExtension, ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromAccounts, &WithdrawWithheldTokensFromAccountsData { proof_instruction_offset, num_token_accounts, + new_decryptable_available_balance: *new_decryptable_available_balance, }, )) } /// Create a `WithdrawWithheldTokensFromAccounts` instruction -#[cfg(feature = "proof-program")] +#[allow(clippy::too_many_arguments)] pub fn withdraw_withheld_tokens_from_accounts( token_program_id: &Pubkey, mint: &Pubkey, destination: &Pubkey, + new_decryptable_available_balance: &DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], sources: &[&Pubkey], - proof_data: &WithdrawWithheldTokensData, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { - Ok(vec![ - inner_withdraw_withheld_tokens_from_accounts( - token_program_id, - mint, - destination, - authority, - multisig_signers, - sources, - 1, - )?, - #[cfg(feature = "proof-program")] - verify_withdraw_withheld_tokens(proof_data), - ]) + let mut instructions = vec![inner_withdraw_withheld_tokens_from_accounts( + token_program_id, + mint, + destination, + new_decryptable_available_balance, + authority, + multisig_signers, + sources, + proof_data_location, + )?]; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + proof_data_location + { + // This constructor appends the proof instruction right after the + // `WithdrawWithheldTokensFromAccounts` instruction. This means that the proof instruction + // offset must always be 1. To use an arbitrary proof instruction offset, use the + // `inner_withdraw_withheld_tokens_from_accounts` constructor. + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != 1 { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + instructions.push(verify_ciphertext_ciphertext_equality(None, proof_data)); + }; + + Ok(instructions) } /// Creates a `HarvestWithheldTokensToMint` instruction @@ -332,8 +467,60 @@ pub fn harvest_withheld_tokens_to_mint( Ok(encode_instruction( token_program_id, accounts, - TokenInstruction::ConfidentialTransferExtension, + TokenInstruction::ConfidentialTransferFeeExtension, ConfidentialTransferFeeInstruction::HarvestWithheldTokensToMint, &(), )) } + +/// Create an `EnableHarvestToMint` instruction +pub fn enable_harvest_to_mint( + token_program_id: &Pubkey, + mint: &Pubkey, + authority: &Pubkey, + multisig_signers: &[&Pubkey], +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*mint, false), + AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), + ]; + + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } + + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::ConfidentialTransferFeeExtension, + ConfidentialTransferFeeInstruction::EnableHarvestToMint, + &(), + )) +} + +/// Create a `DisableHarvestToMint` instruction +pub fn disable_harvest_to_mint( + token_program_id: &Pubkey, + mint: &Pubkey, + authority: &Pubkey, + multisig_signers: &[&Pubkey], +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*mint, false), + AccountMeta::new_readonly(*authority, multisig_signers.is_empty()), + ]; + + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } + + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::ConfidentialTransferFeeExtension, + ConfidentialTransferFeeInstruction::DisableHarvestToMint, + &(), + )) +} diff --git a/token/program-2022/src/extension/confidential_transfer_fee/mod.rs b/token/program-2022/src/extension/confidential_transfer_fee/mod.rs index a1b3a1170c9..df6204dfa88 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/mod.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/mod.rs @@ -2,11 +2,11 @@ use { crate::{ error::TokenError, extension::{Extension, ExtensionType}, - pod::*, }, bytemuck::{Pod, Zeroable}, solana_program::entrypoint::ProgramResult, solana_zk_token_sdk::zk_token_elgamal::pod::{ElGamalCiphertext, ElGamalPubkey, FeeEncryption}, + spl_pod::{optional_keys::OptionalNonZeroPubkey, primitives::PodBool}, }; /// Confidential transfer fee extension instructions @@ -15,6 +15,10 @@ pub mod instruction; /// Confidential transfer fee extension processor pub mod processor; +/// Confidential Transfer Fee extension account information needed for instructions +#[cfg(not(target_os = "solana"))] +pub mod account_info; + /// ElGamal ciphertext containing a transfer fee pub type EncryptedFee = FeeEncryption; /// ElGamal ciphertext containing a withheld fee in an account @@ -34,6 +38,9 @@ pub struct ConfidentialTransferFeeConfig { /// fee parameters, the withheld fee amounts can reveal information about transfer amounts. pub withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey, + /// If `false`, the harvest of withheld tokens to mint is rejected. + pub harvest_to_mint_enabled: PodBool, + /// Withheld confidential transfer fee tokens that have been moved to the mint for withdrawal. pub withheld_amount: EncryptedWithheldAmount, } diff --git a/token/program-2022/src/extension/confidential_transfer_fee/processor.rs b/token/program-2022/src/extension/confidential_transfer_fee/processor.rs index 8d2a0dfc12f..e1ee5099371 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/processor.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/processor.rs @@ -1,11 +1,21 @@ use { crate::{ - check_program_account, + check_program_account, check_zk_token_proof_program_account, error::TokenError, extension::{ + confidential_transfer::{ + instruction::{ + CiphertextCiphertextEqualityProofContext, + CiphertextCiphertextEqualityProofData, ProofContextState, ProofInstruction, + ProofType, + }, + ConfidentialTransferAccount, DecryptableBalance, + }, confidential_transfer_fee::{ instruction::{ - ConfidentialTransferFeeInstruction, InitializeConfidentialTransferFeeConfigData, + ConfidentialTransferFeeInstruction, + InitializeConfidentialTransferFeeConfigData, + WithdrawWithheldTokensFromAccountsData, WithdrawWithheldTokensFromMintData, }, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, EncryptedWithheldAmount, @@ -14,7 +24,8 @@ use { BaseStateWithExtensions, StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, - pod::OptionalNonZeroPubkey, + processor::Processor, + proof::decode_proof_instruction_context, solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, state::{Account, Mint}, }, @@ -25,31 +36,15 @@ use { msg, program_error::ProgramError, pubkey::Pubkey, + sysvar::instructions::get_instruction_relative, }, + spl_pod::{bytemuck::pod_from_bytes, optional_keys::OptionalNonZeroPubkey}, }; // Remove feature once zk ops syscalls are enabled on all networks #[cfg(feature = "zk-ops")] use solana_zk_token_sdk::zk_token_elgamal::ops as syscall; -#[cfg(feature = "proof-program")] -use { - crate::{ - extension::{ - confidential_transfer::{ - instruction::{ProofInstruction, WithdrawWithheldTokensData}, - processor::decode_proof_instruction, - ConfidentialTransferAccount, ConfidentialTransferMint, - }, - confidential_transfer_fee::instruction::{ - WithdrawWithheldTokensFromAccountsData, WithdrawWithheldTokensFromMintData, - }, - }, - processor::Processor, - }, - solana_program::sysvar::instructions::get_instruction_relative, -}; - /// Processes an [InitializeConfidentialTransferFeeConfig] instruction. fn process_initialize_confidential_transfer_fee_config( accounts: &[AccountInfo], @@ -65,22 +60,31 @@ fn process_initialize_confidential_transfer_fee_config( extension.authority = *authority; extension.withdraw_withheld_authority_elgamal_pubkey = *withdraw_withheld_authority_elgamal_pubkey; + extension.harvest_to_mint_enabled = true.into(); extension.withheld_amount = EncryptedWithheldAmount::zeroed(); Ok(()) } /// Processes a [WithdrawWithheldTokensFromMint] instruction. -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] fn process_withdraw_withheld_tokens_from_mint( program_id: &Pubkey, accounts: &[AccountInfo], + new_decryptable_available_balance: &DecryptableBalance, proof_instruction_offset: i64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_account_info = next_account_info(account_info_iter)?; let destination_account_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; + + // zero-knowledge proof certifies that the exact withheld amount is credited to the destination + // account. + let proof_context = verify_ciphertext_ciphertext_equality_proof( + next_account_info(account_info_iter)?, + proof_instruction_offset, + )?; + let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -121,47 +125,42 @@ fn process_withdraw_withheld_tokens_from_mint( if destination_account.base.is_frozen() { return Err(TokenError::AccountFrozen.into()); } - let mut destination_confidential_transfer_account = + let destination_confidential_transfer_account = destination_account.get_extension_mut::()?; destination_confidential_transfer_account.valid_as_destination()?; - // Zero-knowledge proof certifies that the exact withheld amount is credited to the destination - // account. - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyWithdrawWithheldTokens, - &zkp_instruction, - )?; + // The funds are moved from the mint to a destination account. Here, the `source` equates to + // the withdraw withheld authority associated in the mint. + // Check that the withdraw authority ElGamal public key associated with the mint is // consistent with what was actually used to generate the zkp. - if proof_data.withdraw_withheld_authority_pubkey + if proof_context.source_pubkey != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Check that the ElGamal public key associated with the destination account is consistent // with what was actually used to generate the zkp. - if proof_data.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey { + if proof_context.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey + { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Check that the withheld amount ciphertext is consistent with the ciphertext data that was // actually used to generate the zkp. - if proof_data.withdraw_withheld_authority_ciphertext - != confidential_transfer_fee_config.withheld_amount - { + if proof_context.source_ciphertext != confidential_transfer_fee_config.withheld_amount { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } // The proof data contains the mint withheld amount encrypted under the destination ElGamal pubkey. - // Add this amount to the destination pending balance. - destination_confidential_transfer_account.pending_balance_lo = syscall::add( - &destination_confidential_transfer_account.pending_balance_lo, - &proof_data.destination_ciphertext, + // Add this amount to the available balance. + destination_confidential_transfer_account.available_balance = syscall::add( + &destination_confidential_transfer_account.available_balance, + &proof_context.destination_ciphertext, ) .ok_or(ProgramError::InvalidInstructionData)?; - destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + destination_confidential_transfer_account.decryptable_available_balance = + *new_decryptable_available_balance; // Fee is now withdrawn, so zero out the mint withheld amount. confidential_transfer_fee_config.withheld_amount = EncryptedWithheldAmount::zeroed(); @@ -169,18 +168,58 @@ fn process_withdraw_withheld_tokens_from_mint( Ok(()) } +/// Verify zero-knowledge proof needed for a [WithdrawWithheldTokensFromMint] instruction or a +/// `[WithdrawWithheldTokensFromAccounts]` and return the corresponding proof context. +fn verify_ciphertext_ciphertext_equality_proof( + account_info: &AccountInfo<'_>, + proof_instruction_offset: i64, +) -> Result { + if proof_instruction_offset == 0 { + // interpret `account_info` as a context state account + check_zk_token_proof_program_account(account_info.owner)?; + let context_state_account_data = account_info.data.borrow(); + let context_state = pod_from_bytes::< + ProofContextState, + >(&context_state_account_data)?; + + if context_state.proof_type != ProofType::CiphertextCiphertextEquality.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(context_state.proof_context) + } else { + // interpret `account_info` as a sysvar + let zkp_instruction = get_instruction_relative(proof_instruction_offset, account_info)?; + Ok(*decode_proof_instruction_context::< + CiphertextCiphertextEqualityProofData, + CiphertextCiphertextEqualityProofContext, + >( + ProofInstruction::VerifyCiphertextCiphertextEquality, + &zkp_instruction, + )?) + } +} + /// Processes a [WithdrawWithheldTokensFromAccounts] instruction. -#[cfg(all(feature = "zk-ops", feature = "proof-program"))] +#[cfg(feature = "zk-ops")] fn process_withdraw_withheld_tokens_from_accounts( program_id: &Pubkey, accounts: &[AccountInfo], num_token_accounts: u8, + new_decryptable_available_balance: &DecryptableBalance, proof_instruction_offset: i64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_account_info = next_account_info(account_info_iter)?; let destination_account_info = next_account_info(account_info_iter)?; - let instructions_sysvar_info = next_account_info(account_info_iter)?; + + // zero-knowledge proof certifies that the exact aggregate withheld amount is credited to the + // destination account. + let proof_context = verify_ciphertext_ciphertext_equality_proof( + next_account_info(account_info_iter)?, + proof_instruction_offset, + )?; + let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); let account_infos = account_info_iter.as_slice(); @@ -247,47 +286,44 @@ fn process_withdraw_withheld_tokens_from_accounts( } } - let mut destination_confidential_transfer_account = + let destination_confidential_transfer_account = destination_account.get_extension_mut::()?; destination_confidential_transfer_account.valid_as_destination()?; - // Zero-knowledge proof certifies that the exact aggregate withheld amount is credited to the - // source account. - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, instructions_sysvar_info)?; - let proof_data = decode_proof_instruction::( - ProofInstruction::VerifyWithdrawWithheldTokens, - &zkp_instruction, - )?; + // The funds are moved from the accounts to a destination account. Here, the `source` equates + // to the withdraw withheld authority associated in the mint. + // Checks that the withdraw authority ElGamal public key associated with the mint is // consistent with what was actually used to generate the zkp. let confidential_transfer_fee_config = mint.get_extension_mut::()?; - if proof_data.withdraw_withheld_authority_pubkey + if proof_context.source_pubkey != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Checks that the ElGamal public key associated with the destination account is consistent // with what was actually used to generate the zkp. - if proof_data.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey { + if proof_context.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey + { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Checks that the withheld amount ciphertext is consistent with the ciphertext data that was // actually used to generate the zkp. - if proof_data.withdraw_withheld_authority_ciphertext != aggregate_withheld_amount { + if proof_context.source_ciphertext != aggregate_withheld_amount { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } // The proof data contains the mint withheld amount encrypted under the destination ElGamal pubkey. - // This amount is added to the destination pending balance. - destination_confidential_transfer_account.pending_balance_lo = syscall::add( - &destination_confidential_transfer_account.pending_balance_lo, - &proof_data.destination_ciphertext, + // This amount is added to the destination available balance. + destination_confidential_transfer_account.available_balance = syscall::add( + &destination_confidential_transfer_account.available_balance, + &proof_context.destination_ciphertext, ) .ok_or(ProgramError::InvalidInstructionData)?; - destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + destination_confidential_transfer_account.decryptable_available_balance = + *new_decryptable_available_balance; Ok(()) } @@ -328,6 +364,13 @@ fn process_harvest_withheld_tokens_to_mint(accounts: &[AccountInfo]) -> ProgramR let confidential_transfer_fee_mint = mint.get_extension_mut::()?; + let harvest_to_mint_enabled: bool = confidential_transfer_fee_mint + .harvest_to_mint_enabled + .into(); + if !harvest_to_mint_enabled { + return Err(TokenError::HarvestToMintDisabled.into()); + } + for token_account_info in token_account_infos { match harvest_from_account(mint_account_info.key, token_account_info) { Ok(withheld_amount) => { @@ -347,6 +390,66 @@ fn process_harvest_withheld_tokens_to_mint(accounts: &[AccountInfo]) -> ProgramR Ok(()) } +/// Process a [EnableHarvestToMint] instruction. +fn process_enable_harvest_to_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + let authority_info_data_len = authority_info.data_len(); + + check_program_account(mint_info.owner)?; + let mint_data = &mut mint_info.data.borrow_mut(); + let mut mint = StateWithExtensionsMut::::unpack(mint_data)?; + let confidential_transfer_fee_mint = + mint.get_extension_mut::()?; + + let maybe_confidential_transfer_fee_authority: Option = + confidential_transfer_fee_mint.authority.into(); + let confidential_transfer_fee_authority = + maybe_confidential_transfer_fee_authority.ok_or(TokenError::NoAuthorityExists)?; + + Processor::validate_owner( + program_id, + &confidential_transfer_fee_authority, + authority_info, + authority_info_data_len, + account_info_iter.as_slice(), + )?; + + confidential_transfer_fee_mint.harvest_to_mint_enabled = true.into(); + Ok(()) +} + +/// Process a [DisableHarvestToMint] instruction. +fn process_disable_harvest_to_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + let authority_info_data_len = authority_info.data_len(); + + check_program_account(mint_info.owner)?; + let mint_data = &mut mint_info.data.borrow_mut(); + let mut mint = StateWithExtensionsMut::::unpack(mint_data)?; + let confidential_transfer_fee_mint = + mint.get_extension_mut::()?; + + let maybe_confidential_transfer_fee_authority: Option = + confidential_transfer_fee_mint.authority.into(); + let confidential_transfer_fee_authority = + maybe_confidential_transfer_fee_authority.ok_or(TokenError::NoAuthorityExists)?; + + Processor::validate_owner( + program_id, + &confidential_transfer_fee_authority, + authority_info, + authority_info_data_len, + account_info_iter.as_slice(), + )?; + + confidential_transfer_fee_mint.harvest_to_mint_enabled = false.into(); + Ok(()) +} + #[allow(dead_code)] pub(crate) fn process_instruction( program_id: &Pubkey, @@ -357,7 +460,7 @@ pub(crate) fn process_instruction( match decode_instruction_type(input)? { ConfidentialTransferFeeInstruction::InitializeConfidentialTransferFeeConfig => { - msg!("ConfidentialTransferInstruction::InitializeConfidentialTransferFeeConfig"); + msg!("ConfidentialTransferFeeInstruction::InitializeConfidentialTransferFeeConfig"); let data = decode_instruction_data::(input)?; process_initialize_confidential_transfer_fee_config( @@ -367,41 +470,43 @@ pub(crate) fn process_instruction( ) } ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromMint => { - msg!("ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint"); - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] + msg!("ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromMint"); + #[cfg(feature = "zk-ops")] { let data = decode_instruction_data::(input)?; - return process_withdraw_withheld_tokens_from_mint( + process_withdraw_withheld_tokens_from_mint( program_id, accounts, + &data.new_decryptable_available_balance, data.proof_instruction_offset as i64, - ); + ) } - #[cfg(not(all(feature = "zk-ops", feature = "proof_program")))] + #[cfg(not(feature = "zk-ops"))] { Err(ProgramError::InvalidInstructionData) } } ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromAccounts => { - msg!("ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts"); - #[cfg(all(feature = "zk-ops", feature = "proof-program"))] + msg!("ConfidentialTransferFeeInstruction::WithdrawWithheldTokensFromAccounts"); + #[cfg(feature = "zk-ops")] { let data = decode_instruction_data::(input)?; - return process_withdraw_withheld_tokens_from_accounts( + process_withdraw_withheld_tokens_from_accounts( program_id, accounts, data.num_token_accounts, + &data.new_decryptable_available_balance, data.proof_instruction_offset as i64, - ); + ) } - #[cfg(not(all(feature = "zk-ops", feature = "proof_program")))] + #[cfg(not(feature = "zk-ops"))] { Err(ProgramError::InvalidInstructionData) } } ConfidentialTransferFeeInstruction::HarvestWithheldTokensToMint => { - msg!("ConfidentialTransferInstruction::HarvestWithheldTokensToMint"); + msg!("ConfidentialTransferFeeInstruction::HarvestWithheldTokensToMint"); #[cfg(feature = "zk-ops")] { process_harvest_withheld_tokens_to_mint(accounts) @@ -411,5 +516,13 @@ pub(crate) fn process_instruction( Err(ProgramError::InvalidInstructionData) } } + ConfidentialTransferFeeInstruction::EnableHarvestToMint => { + msg!("ConfidentialTransferFeeInstruction::EnableHarvestToMint"); + process_enable_harvest_to_mint(program_id, accounts) + } + ConfidentialTransferFeeInstruction::DisableHarvestToMint => { + msg!("ConfidentialTransferFeeInstruction::DisableHarvestToMint"); + process_disable_harvest_to_mint(program_id, accounts) + } } } diff --git a/token/program-2022/src/extension/cpi_guard/instruction.rs b/token/program-2022/src/extension/cpi_guard/instruction.rs index 9570315eb9f..3293e683f82 100644 --- a/token/program-2022/src/extension/cpi_guard/instruction.rs +++ b/token/program-2022/src/extension/cpi_guard/instruction.rs @@ -11,7 +11,12 @@ use { }, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// CPI Guard extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum CpiGuardInstruction { diff --git a/token/program-2022/src/extension/cpi_guard/mod.rs b/token/program-2022/src/extension/cpi_guard/mod.rs index f805fbd7a6c..9c75d8f92aa 100644 --- a/token/program-2022/src/extension/cpi_guard/mod.rs +++ b/token/program-2022/src/extension/cpi_guard/mod.rs @@ -1,13 +1,16 @@ use { crate::{ extension::{BaseStateWithExtensions, Extension, ExtensionType, StateWithExtensionsMut}, - pod::PodBool, state::Account, }, bytemuck::{Pod, Zeroable}, solana_program::instruction::{get_stack_height, TRANSACTION_LEVEL_STACK_HEIGHT}, + spl_pod::primitives::PodBool, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// CPI Guard extension instructions pub mod instruction; @@ -16,6 +19,8 @@ pub mod processor; /// CPI Guard extension for Accounts #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct CpiGuard { /// Lock privileged token operations from happening via CPI diff --git a/token/program-2022/src/extension/default_account_state/instruction.rs b/token/program-2022/src/extension/default_account_state/instruction.rs index c872f41e89e..2bb6931364f 100644 --- a/token/program-2022/src/extension/default_account_state/instruction.rs +++ b/token/program-2022/src/extension/default_account_state/instruction.rs @@ -12,7 +12,12 @@ use { std::convert::TryFrom, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Default Account State extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum DefaultAccountStateInstruction { diff --git a/token/program-2022/src/extension/default_account_state/mod.rs b/token/program-2022/src/extension/default_account_state/mod.rs index 848ea4627d1..23d14b0677e 100644 --- a/token/program-2022/src/extension/default_account_state/mod.rs +++ b/token/program-2022/src/extension/default_account_state/mod.rs @@ -3,6 +3,9 @@ use { bytemuck::{Pod, Zeroable}, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Default Account state extension instructions pub mod instruction; @@ -11,6 +14,8 @@ pub mod processor; /// Default Account::state extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct DefaultAccountState { /// Default Account::state in which new Accounts should be initialized diff --git a/token/program-2022/src/extension/group_pointer/instruction.rs b/token/program-2022/src/extension/group_pointer/instruction.rs new file mode 100644 index 00000000000..331dc119eff --- /dev/null +++ b/token/program-2022/src/extension/group_pointer/instruction.rs @@ -0,0 +1,131 @@ +use { + crate::{ + check_program_account, + instruction::{encode_instruction, TokenInstruction}, + }, + bytemuck::{Pod, Zeroable}, + num_enum::{IntoPrimitive, TryFromPrimitive}, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, + std::convert::TryInto, +}; + +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + +/// Group pointer extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum GroupPointerInstruction { + /// Initialize a new mint with a group pointer + /// + /// Fails if the mint has already been initialized, so must be called before + /// `InitializeMint`. + /// + /// The mint must have exactly enough space allocated for the base mint (82 + /// bytes), plus 83 bytes of padding, 1 byte reserved for the account type, + /// then space required for this extension, plus any others. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The mint to initialize. + /// + /// Data expected by this instruction: + /// `crate::extension::group_pointer::instruction::InitializeInstructionData` + /// + Initialize, + /// Update the group pointer address. Only supported for mints that + /// include the `GroupPointer` extension. + /// + /// Accounts expected by this instruction: + /// + /// * Single authority + /// 0. `[writable]` The mint. + /// 1. `[signer]` The group pointer authority. + /// + /// * Multisignature authority + /// 0. `[writable]` The mint. + /// 1. `[]` The mint's group pointer authority. + /// 2. ..2+M `[signer]` M signer accounts. + /// + /// Data expected by this instruction: + /// `crate::extension::group_pointer::instruction::UpdateInstructionData` + /// + Update, +} + +/// Data expected by `Initialize` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct InitializeInstructionData { + /// The public key for the account that can update the group address + pub authority: OptionalNonZeroPubkey, + /// The account address that holds the group + pub group_address: OptionalNonZeroPubkey, +} + +/// Data expected by `Update` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct UpdateInstructionData { + /// The new account address that holds the group configurations + pub group_address: OptionalNonZeroPubkey, +} + +/// Create an `Initialize` instruction +pub fn initialize( + token_program_id: &Pubkey, + mint: &Pubkey, + authority: Option, + group_address: Option, +) -> Result { + check_program_account(token_program_id)?; + let accounts = vec![AccountMeta::new(*mint, false)]; + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::GroupPointerExtension, + GroupPointerInstruction::Initialize, + &InitializeInstructionData { + authority: authority.try_into()?, + group_address: group_address.try_into()?, + }, + )) +} + +/// Create an `Update` instruction +pub fn update( + token_program_id: &Pubkey, + mint: &Pubkey, + authority: &Pubkey, + signers: &[&Pubkey], + group_address: Option, +) -> Result { + check_program_account(token_program_id)?; + let mut accounts = vec![ + AccountMeta::new(*mint, false), + AccountMeta::new_readonly(*authority, signers.is_empty()), + ]; + for signer_pubkey in signers.iter() { + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); + } + Ok(encode_instruction( + token_program_id, + accounts, + TokenInstruction::GroupPointerExtension, + GroupPointerInstruction::Update, + &UpdateInstructionData { + group_address: group_address.try_into()?, + }, + )) +} diff --git a/token/program-2022/src/extension/group_pointer/mod.rs b/token/program-2022/src/extension/group_pointer/mod.rs new file mode 100644 index 00000000000..60f5aec3669 --- /dev/null +++ b/token/program-2022/src/extension/group_pointer/mod.rs @@ -0,0 +1,29 @@ +use { + crate::extension::{Extension, ExtensionType}, + bytemuck::{Pod, Zeroable}, + spl_pod::optional_keys::OptionalNonZeroPubkey, +}; + +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + +/// Instructions for the GroupPointer extension +pub mod instruction; +/// Instruction processor for the GroupPointer extension +pub mod processor; + +/// Group pointer extension data for mints. +#[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct GroupPointer { + /// Authority that can set the group address + pub authority: OptionalNonZeroPubkey, + /// Account address that holds the group + pub group_address: OptionalNonZeroPubkey, +} + +impl Extension for GroupPointer { + const TYPE: ExtensionType = ExtensionType::GroupPointer; +} diff --git a/token/program-2022/src/extension/group_pointer/processor.rs b/token/program-2022/src/extension/group_pointer/processor.rs new file mode 100644 index 00000000000..7c99afc0001 --- /dev/null +++ b/token/program-2022/src/extension/group_pointer/processor.rs @@ -0,0 +1,103 @@ +use { + crate::{ + check_program_account, + error::TokenError, + extension::{ + group_pointer::{ + instruction::{ + GroupPointerInstruction, InitializeInstructionData, UpdateInstructionData, + }, + GroupPointer, + }, + StateWithExtensionsMut, + }, + instruction::{decode_instruction_data, decode_instruction_type}, + processor::Processor, + state::Mint, + }, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + pubkey::Pubkey, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, +}; + +fn process_initialize( + _program_id: &Pubkey, + accounts: &[AccountInfo], + authority: &OptionalNonZeroPubkey, + group_address: &OptionalNonZeroPubkey, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_account_info = next_account_info(account_info_iter)?; + let mut mint_data = mint_account_info.data.borrow_mut(); + let mut mint = StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data)?; + + let extension = mint.init_extension::(true)?; + extension.authority = *authority; + + if Option::::from(*authority).is_none() + && Option::::from(*group_address).is_none() + { + msg!( + "The group pointer extension requires at least an authority or an address for \ + initialization, neither was provided" + ); + Err(TokenError::InvalidInstruction)?; + } + extension.group_address = *group_address; + Ok(()) +} + +fn process_update( + program_id: &Pubkey, + accounts: &[AccountInfo], + new_group_address: &OptionalNonZeroPubkey, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_account_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + let owner_info_data_len = owner_info.data_len(); + + let mut mint_data = mint_account_info.data.borrow_mut(); + let mut mint = StateWithExtensionsMut::::unpack(&mut mint_data)?; + let extension = mint.get_extension_mut::()?; + let authority = + Option::::from(extension.authority).ok_or(TokenError::NoAuthorityExists)?; + + Processor::validate_owner( + program_id, + &authority, + owner_info, + owner_info_data_len, + account_info_iter.as_slice(), + )?; + + extension.group_address = *new_group_address; + Ok(()) +} + +pub(crate) fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + input: &[u8], +) -> ProgramResult { + check_program_account(program_id)?; + match decode_instruction_type(input)? { + GroupPointerInstruction::Initialize => { + msg!("GroupPointerInstruction::Initialize"); + let InitializeInstructionData { + authority, + group_address, + } = decode_instruction_data(input)?; + process_initialize(program_id, accounts, authority, group_address) + } + GroupPointerInstruction::Update => { + msg!("GroupPointerInstruction::Update"); + let UpdateInstructionData { group_address } = decode_instruction_data(input)?; + process_update(program_id, accounts, group_address) + } + } +} diff --git a/token/program-2022/src/extension/immutable_owner.rs b/token/program-2022/src/extension/immutable_owner.rs index 32d03210b60..61aa3329d4e 100644 --- a/token/program-2022/src/extension/immutable_owner.rs +++ b/token/program-2022/src/extension/immutable_owner.rs @@ -3,7 +3,12 @@ use { bytemuck::{Pod, Zeroable}, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Indicates that the Account owner authority cannot be changed +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)] pub struct ImmutableOwner; diff --git a/token/program-2022/src/extension/interest_bearing_mint/instruction.rs b/token/program-2022/src/extension/interest_bearing_mint/instruction.rs index 5231bb959e0..7a61125fe84 100644 --- a/token/program-2022/src/extension/interest_bearing_mint/instruction.rs +++ b/token/program-2022/src/extension/interest_bearing_mint/instruction.rs @@ -3,7 +3,6 @@ use { check_program_account, extension::interest_bearing_mint::BasisPoints, instruction::{encode_instruction, TokenInstruction}, - pod::OptionalNonZeroPubkey, }, bytemuck::{Pod, Zeroable}, num_enum::{IntoPrimitive, TryFromPrimitive}, @@ -12,10 +11,16 @@ use { program_error::ProgramError, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, std::convert::TryInto, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Interesting-bearing mint extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum InterestBearingMintInstruction { @@ -57,6 +62,8 @@ pub enum InterestBearingMintInstruction { } /// Data expected by `InterestBearing::Initialize` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct InitializeInstructionData { diff --git a/token/program-2022/src/extension/interest_bearing_mint/mod.rs b/token/program-2022/src/extension/interest_bearing_mint/mod.rs index fd2c31f6078..26b8fb1df50 100644 --- a/token/program-2022/src/extension/interest_bearing_mint/mod.rs +++ b/token/program-2022/src/extension/interest_bearing_mint/mod.rs @@ -1,13 +1,17 @@ use { - crate::{ - extension::{Extension, ExtensionType}, - pod::{OptionalNonZeroPubkey, PodI16, PodI64}, - }, + crate::extension::{Extension, ExtensionType}, bytemuck::{Pod, Zeroable}, solana_program::program_error::ProgramError, + spl_pod::{ + optional_keys::OptionalNonZeroPubkey, + primitives::{PodI16, PodI64}, + }, std::convert::TryInto, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Interest-bearing mint extension instructions pub mod instruction; @@ -31,6 +35,8 @@ pub type UnixTimestamp = PodI64; /// To support changing the rate, the config also maintains state for the previous /// rate. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct InterestBearingConfig { /// Authority that can set the interest rate and authority diff --git a/token/program-2022/src/extension/interest_bearing_mint/processor.rs b/token/program-2022/src/extension/interest_bearing_mint/processor.rs index 3c05084ad84..2ef0411a813 100644 --- a/token/program-2022/src/extension/interest_bearing_mint/processor.rs +++ b/token/program-2022/src/extension/interest_bearing_mint/processor.rs @@ -10,7 +10,6 @@ use { StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, - pod::OptionalNonZeroPubkey, processor::Processor, state::Mint, }, @@ -22,6 +21,7 @@ use { pubkey::Pubkey, sysvar::Sysvar, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; fn process_initialize( diff --git a/token/program-2022/src/extension/memo_transfer/instruction.rs b/token/program-2022/src/extension/memo_transfer/instruction.rs index f299167d27f..a6af5fde0d3 100644 --- a/token/program-2022/src/extension/memo_transfer/instruction.rs +++ b/token/program-2022/src/extension/memo_transfer/instruction.rs @@ -11,7 +11,12 @@ use { }, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Required Memo Transfers extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum RequiredMemoTransfersInstruction { diff --git a/token/program-2022/src/extension/memo_transfer/mod.rs b/token/program-2022/src/extension/memo_transfer/mod.rs index 11be19f6aae..0c61ca23994 100644 --- a/token/program-2022/src/extension/memo_transfer/mod.rs +++ b/token/program-2022/src/extension/memo_transfer/mod.rs @@ -2,15 +2,18 @@ use { crate::{ error::TokenError, extension::{BaseStateWithExtensions, Extension, ExtensionType, StateWithExtensionsMut}, - pod::PodBool, state::Account, }, bytemuck::{Pod, Zeroable}, solana_program::{ instruction::get_processed_sibling_instruction, program_error::ProgramError, pubkey::Pubkey, }, + spl_pod::primitives::PodBool, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Memo Transfer extension instructions pub mod instruction; @@ -19,6 +22,8 @@ pub mod processor; /// Memo Transfer extension for Accounts #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct MemoTransfer { /// Require transfers into this account to be accompanied by a memo diff --git a/token/program-2022/src/extension/metadata_pointer/instruction.rs b/token/program-2022/src/extension/metadata_pointer/instruction.rs index e379bfa3af2..841052226d0 100644 --- a/token/program-2022/src/extension/metadata_pointer/instruction.rs +++ b/token/program-2022/src/extension/metadata_pointer/instruction.rs @@ -2,7 +2,6 @@ use { crate::{ check_program_account, instruction::{encode_instruction, TokenInstruction}, - pod::OptionalNonZeroPubkey, }, bytemuck::{Pod, Zeroable}, num_enum::{IntoPrimitive, TryFromPrimitive}, @@ -11,10 +10,16 @@ use { program_error::ProgramError, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, std::convert::TryInto, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Metadata pointer extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum MetadataPointerInstruction { @@ -56,6 +61,8 @@ pub enum MetadataPointerInstruction { } /// Data expected by `Initialize` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct InitializeInstructionData { @@ -66,6 +73,8 @@ pub struct InitializeInstructionData { } /// Data expected by `Update` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct UpdateInstructionData { diff --git a/token/program-2022/src/extension/metadata_pointer/mod.rs b/token/program-2022/src/extension/metadata_pointer/mod.rs index 747cbe441b3..2f41bd75642 100644 --- a/token/program-2022/src/extension/metadata_pointer/mod.rs +++ b/token/program-2022/src/extension/metadata_pointer/mod.rs @@ -1,11 +1,12 @@ use { - crate::{ - extension::{Extension, ExtensionType}, - pod::OptionalNonZeroPubkey, - }, + crate::extension::{Extension, ExtensionType}, bytemuck::{Pod, Zeroable}, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Instructions for the MetadataPointer extension pub mod instruction; /// Instruction processor for the MetadataPointer extension @@ -13,6 +14,8 @@ pub mod processor; /// Metadata pointer extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct MetadataPointer { /// Authority that can set the metadata address diff --git a/token/program-2022/src/extension/metadata_pointer/processor.rs b/token/program-2022/src/extension/metadata_pointer/processor.rs index a0632520d57..d9911ad00d1 100644 --- a/token/program-2022/src/extension/metadata_pointer/processor.rs +++ b/token/program-2022/src/extension/metadata_pointer/processor.rs @@ -12,7 +12,6 @@ use { StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, - pod::OptionalNonZeroPubkey, processor::Processor, state::Mint, }, @@ -22,6 +21,7 @@ use { msg, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; fn process_initialize( @@ -42,7 +42,7 @@ fn process_initialize( && Option::::from(*metadata_address).is_none() { msg!("The metadata pointer extension requires at least an authority or an address for initialization, neither was provided"); - return Err(TokenError::InvalidInstruction)?; + Err(TokenError::InvalidInstruction)?; } extension.metadata_address = *metadata_address; Ok(()) diff --git a/token/program-2022/src/extension/mint_close_authority.rs b/token/program-2022/src/extension/mint_close_authority.rs index beb76a96d7e..f595d8dc100 100644 --- a/token/program-2022/src/extension/mint_close_authority.rs +++ b/token/program-2022/src/extension/mint_close_authority.rs @@ -1,13 +1,16 @@ use { - crate::{ - extension::{Extension, ExtensionType}, - pod::*, - }, + crate::extension::{Extension, ExtensionType}, bytemuck::{Pod, Zeroable}, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Close authority extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct MintCloseAuthority { /// Optional authority to close the mint diff --git a/token/program-2022/src/extension/mod.rs b/token/program-2022/src/extension/mod.rs index 5e792974784..46a43a4562f 100644 --- a/token/program-2022/src/extension/mod.rs +++ b/token/program-2022/src/extension/mod.rs @@ -10,6 +10,7 @@ use { }, cpi_guard::CpiGuard, default_account_state::DefaultAccountState, + group_pointer::GroupPointer, immutable_owner::ImmutableOwner, interest_bearing_mint::InterestBearingConfig, memo_transfer::MemoTransfer, @@ -20,15 +21,20 @@ use { transfer_fee::{TransferFeeAmount, TransferFeeConfig}, transfer_hook::{TransferHook, TransferHookAccount}, }, - pod::*, state::{Account, Mint, Multisig}, }, bytemuck::{Pod, Zeroable}, num_enum::{IntoPrimitive, TryFromPrimitive}, solana_program::{ + account_info::AccountInfo, program_error::ProgramError, program_pack::{IsInitialized, Pack}, }, + spl_pod::{ + bytemuck::{pod_from_bytes, pod_from_bytes_mut, pod_get_packed_len}, + primitives::PodU16, + }, + spl_type_length_value::variable_len_pack::VariableLenPack, std::{ cmp::Ordering, convert::{TryFrom, TryInto}, @@ -47,6 +53,8 @@ pub mod confidential_transfer_fee; pub mod cpi_guard; /// Default Account State extension pub mod default_account_state; +/// Group Pointer extension +pub mod group_pointer; /// Immutable Owner extension pub mod immutable_owner; /// Interest-Bearing Mint extension @@ -63,6 +71,8 @@ pub mod non_transferable; pub mod permanent_delegate; /// Utility to reallocate token accounts pub mod reallocate; +/// Token-metadata extension +pub mod token_metadata; /// Transfer Fee extension pub mod transfer_fee; /// Transfer Hook extension @@ -181,8 +191,12 @@ fn get_tlv_data_info(tlv_data: &[u8]) -> Result { while start_index < tlv_data.len() { let tlv_indices = get_tlv_indices(start_index); if tlv_data.len() < tlv_indices.length_start { - // not enough bytes to store the type, malformed - return Err(ProgramError::InvalidAccountData); + // There aren't enough bytes to store the next type, which means we + // got to the end. The last byte could be used during a realloc! + return Ok(TlvDataInfo { + extension_types, + used_len: tlv_indices.type_start, + }); } let extension_type = ExtensionType::try_from(&tlv_data[tlv_indices.type_start..tlv_indices.length_start])?; @@ -267,6 +281,9 @@ fn check_account_type(account_type: AccountType) -> Result<(), Pro /// that. We do a special case checking for a Multisig length, because those /// aren't extensible under any circumstances. const BASE_ACCOUNT_LENGTH: usize = Account::LEN; +/// Helper that tacks on the AccountType length, which gives the minimum for any +/// account with extensions +const BASE_ACCOUNT_AND_TYPE_LENGTH: usize = BASE_ACCOUNT_LENGTH + size_of::(); fn type_and_tlv_indices( rest_input: &[u8], @@ -323,16 +340,16 @@ fn get_extension_bytes_mut( return Err(ProgramError::InvalidAccountData); } let TlvIndices { - type_start, + type_start: _, length_start, value_start, } = get_extension_indices::(tlv_data, false)?; - - if tlv_data[type_start..].len() < add_type_and_length_to_len(V::TYPE.try_get_type_len()?) { - return Err(ProgramError::InvalidAccountData); - } + // get_extension_indices has checked that tlv_data is long enough to include these indices let length = pod_from_bytes::(&tlv_data[length_start..value_start])?; let value_end = value_start.saturating_add(usize::from(*length)); + if tlv_data.len() < value_end { + return Err(ProgramError::InvalidAccountData); + } Ok(&mut tlv_data[value_start..value_end]) } @@ -351,6 +368,14 @@ pub trait BaseStateWithExtensions { pod_from_bytes::(self.get_extension_bytes::()?) } + /// Unpacks a portion of the TLV data as the desired variable-length type + fn get_variable_len_extension( + &self, + ) -> Result { + let data = get_extension_bytes::(self.get_tlv_data())?; + V::unpack_from_slice(data) + } + /// Iterates through the TLV entries, returning only the types fn get_extension_types(&self) -> Result, ProgramError> { get_tlv_data_info(self.get_tlv_data()).map(|x| x.extension_types) @@ -360,6 +385,51 @@ pub trait BaseStateWithExtensions { fn get_first_extension_type(&self) -> Result, ProgramError> { get_first_extension_type(self.get_tlv_data()) } + + /// Get the total number of bytes used by TLV entries and the base type + fn try_get_account_len(&self) -> Result { + let tlv_info = get_tlv_data_info(self.get_tlv_data())?; + if tlv_info.extension_types.is_empty() { + Ok(S::LEN) + } else { + let total_len = tlv_info + .used_len + .saturating_add(BASE_ACCOUNT_AND_TYPE_LENGTH); + Ok(adjust_len_for_multisig(total_len)) + } + } + + /// Calculate the new expected size if the state allocates the given number + /// of bytes for the given extension type. + /// + /// Provides the correct answer regardless if the extension is already present + /// in the TLV data. + fn try_get_new_account_len( + &self, + new_extension: &V, + ) -> Result { + // get the new length used by the extension + let new_extension_len = add_type_and_length_to_len(new_extension.get_packed_len()?); + let tlv_info = get_tlv_data_info(self.get_tlv_data())?; + // If we're adding an extension, then we must have at least BASE_ACCOUNT_LENGTH + // and account type + let current_len = tlv_info + .used_len + .saturating_add(BASE_ACCOUNT_AND_TYPE_LENGTH); + let new_len = if tlv_info.extension_types.is_empty() { + current_len.saturating_add(new_extension_len) + } else { + // get the current length used by the extension + let current_extension_len = self + .get_extension_bytes::() + .map(|x| add_type_and_length_to_len(x.len())) + .unwrap_or(0); + current_len + .saturating_sub(current_extension_len) + .saturating_add(new_extension_len) + }; + Ok(adjust_len_for_multisig(new_len)) + } } /// Encapsulates owned immutable base state data (mint or account) with possible extensions @@ -532,6 +602,18 @@ impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> { pod_from_bytes_mut::(self.get_extension_bytes_mut::()?) } + /// Packs a variable-length extension into its appropriate data segment. Fails + /// if space hasn't already been allocated for the given extension + pub fn pack_variable_len_extension( + &mut self, + extension: &V, + ) -> Result<(), ProgramError> { + let data = self.get_extension_bytes_mut::()?; + // NOTE: Do *not* use `pack`, since the length check will cause + // reallocations to smaller sizes to fail + extension.pack_into_slice(data) + } + /// Packs base state data into the base data portion pub fn pack_base(&mut self) { S::pack_into_slice(&self.base, self.base_data); @@ -546,12 +628,25 @@ impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> { overwrite: bool, ) -> Result<&mut V, ProgramError> { let length = pod_get_packed_len::(); - let buffer = self.alloc_internal::(length, overwrite)?; + let buffer = self.alloc::(length, overwrite)?; let extension_ref = pod_from_bytes_mut::(buffer)?; *extension_ref = V::default(); Ok(extension_ref) } + /// Reallocate and overwite the TLV entry for the given variable-length + /// extension. + /// + /// Returns an error if the extension is not present, or if there is not enough + /// space in the buffer. + pub fn realloc_variable_len_extension( + &mut self, + new_extension: &V, + ) -> Result<(), ProgramError> { + let data = self.realloc::(new_extension.get_packed_len()?)?; + new_extension.pack_into_slice(data) + } + /// Reallocate the TLV entry for the given extension to the given number of bytes. /// /// If the new length is smaller, it will compact the rest of the buffer and zero out @@ -560,7 +655,7 @@ impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> { /// /// Returns an error if the extension is not present, or if this is not enough /// space in the buffer. - pub fn realloc( + fn realloc( &mut self, length: usize, ) -> Result<&mut [u8], ProgramError> { @@ -608,19 +703,21 @@ impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> { Ok(&mut self.tlv_data[value_start..new_value_end]) } - /// Allocate the given number of bytes for the given unsized extension + /// Allocate the given number of bytes for the given variable-length extension + /// and write its contents into the TLV buffer. /// /// This can only be used for variable-sized types, such as `String` or `Vec`. /// `Pod` types must use `init_extension` - pub fn alloc( + pub fn init_variable_len_extension( &mut self, - length: usize, + extension: &V, overwrite: bool, - ) -> Result<&mut [u8], ProgramError> { - self.alloc_internal::(length, overwrite) + ) -> Result<(), ProgramError> { + let data = self.alloc::(extension.get_packed_len()?, overwrite)?; + extension.pack_into_slice(data) } - fn alloc_internal( + fn alloc( &mut self, length: usize, overwrite: bool, @@ -767,6 +864,7 @@ impl Default for AccountType { /// accounts. #[repr(u16)] #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] pub enum ExtensionType { /// Used as padding if the account size would otherwise be 355, same as a multisig @@ -807,9 +905,14 @@ pub enum ExtensionType { ConfidentialTransferFeeAmount, /// Mint contains a pointer to another account (or the same account) that holds metadata MetadataPointer, - /// Test unsized mint extension + /// Mint contains token-metadata + TokenMetadata, + /// Test variable-length mint extension + /// Mint contains a pointer to another account (or the same account) that holds group + /// configurations + GroupPointer, #[cfg(test)] - UnsizedMintTest = u16::MAX - 2, + VariableLenMintTest = u16::MAX - 2, /// Padding extension used to make an account exactly Multisig::LEN, used for testing #[cfg(test)] AccountPaddingTest, @@ -834,19 +937,20 @@ impl From for [u8; 2] { impl ExtensionType { /// Returns true if the given extension type is sized /// - /// Most extension types should be sized, so any unsized extension types should - /// be added here by hand + /// Most extension types should be sized, so any variable-length extension + /// types should be added here by hand const fn sized(&self) -> bool { match self { + ExtensionType::TokenMetadata => false, #[cfg(test)] - ExtensionType::UnsizedMintTest => false, + ExtensionType::VariableLenMintTest => false, _ => true, } } /// Get the data length of the type associated with the enum /// - /// Fails if the extension type is unsized + /// Fails if the extension type has a variable length fn try_get_type_len(&self) -> Result { if !self.sized() { return Err(ProgramError::InvalidArgument); @@ -879,25 +983,27 @@ impl ExtensionType { pod_get_packed_len::() } ExtensionType::MetadataPointer => pod_get_packed_len::(), + ExtensionType::TokenMetadata => unreachable!(), + ExtensionType::GroupPointer => pod_get_packed_len::(), #[cfg(test)] ExtensionType::AccountPaddingTest => pod_get_packed_len::(), #[cfg(test)] ExtensionType::MintPaddingTest => pod_get_packed_len::(), #[cfg(test)] - ExtensionType::UnsizedMintTest => unreachable!(), + ExtensionType::VariableLenMintTest => unreachable!(), }) } /// Get the TLV length for an ExtensionType /// - /// Fails if the extension type is unsized + /// Fails if the extension type has a variable length fn try_get_tlv_len(&self) -> Result { Ok(add_type_and_length_to_len(self.try_get_type_len()?)) } /// Get the TLV length for a set of ExtensionTypes /// - /// Fails if any of the extension types is unsized + /// Fails if any of the extension types has a variable length fn try_get_total_tlv_len(extension_types: &[Self]) -> Result { // dedupe extensions let mut extensions = vec![]; @@ -911,17 +1017,15 @@ impl ExtensionType { /// Get the required account data length for the given ExtensionTypes /// - /// Fails if any of the extension types is unsized - pub fn try_get_account_len( + /// Fails if any of the extension types has a variable length + pub fn try_calculate_account_len( extension_types: &[Self], ) -> Result { if extension_types.is_empty() { Ok(S::LEN) } else { let extension_size = Self::try_get_total_tlv_len(extension_types)?; - let total_len = extension_size - .saturating_add(BASE_ACCOUNT_LENGTH) - .saturating_add(size_of::()); + let total_len = extension_size.saturating_add(BASE_ACCOUNT_AND_TYPE_LENGTH); Ok(adjust_len_for_multisig(total_len)) } } @@ -939,7 +1043,9 @@ impl ExtensionType { | ExtensionType::PermanentDelegate | ExtensionType::TransferHook | ExtensionType::ConfidentialTransferFeeConfig - | ExtensionType::MetadataPointer => AccountType::Mint, + | ExtensionType::MetadataPointer + | ExtensionType::TokenMetadata + | ExtensionType::GroupPointer => AccountType::Mint, ExtensionType::ImmutableOwner | ExtensionType::TransferFeeAmount | ExtensionType::ConfidentialTransferAccount @@ -949,7 +1055,7 @@ impl ExtensionType { | ExtensionType::CpiGuard | ExtensionType::ConfidentialTransferFeeAmount => AccountType::Account, #[cfg(test)] - ExtensionType::UnsizedMintTest => AccountType::Mint, + ExtensionType::VariableLenMintTest => AccountType::Mint, #[cfg(test)] ExtensionType::AccountPaddingTest => AccountType::Account, #[cfg(test)] @@ -1033,11 +1139,6 @@ pub trait Extension { const TYPE: ExtensionType; } -/// Trait to be implemented by any unsized extension. -/// -/// Prevents calling the raw `alloc` function for sized types. -pub trait UnsizedExtension: Extension {} - /// Padding a mint account to be exactly Multisig::LEN. /// We need to pad 185 bytes, since Multisig::LEN = 355, Account::LEN = 165, /// size_of AccountType = 1, size_of ExtensionType = 2, size_of Length = 2. @@ -1077,22 +1178,120 @@ impl Extension for AccountPaddingTest { const TYPE: ExtensionType = ExtensionType::AccountPaddingTest; } +/// Packs a variable-length extension into a TLV space +/// +/// This function reallocates the account as needed to accommodate for the +/// change in space, then reallocates in the TLV buffer, and finally writes the +/// bytes. +/// +/// NOTE: Unlike the `reallocate` instruction, this function will reduce the +/// size of an account if it has too many bytes allocated for the given value. +pub fn alloc_and_serialize( + account_info: &AccountInfo, + new_extension: &V, + overwrite: bool, +) -> Result<(), ProgramError> { + let previous_account_len = account_info.try_data_len()?; + let (new_account_len, extension_already_exists) = { + let data = account_info.try_borrow_data()?; + let state = StateWithExtensions::::unpack(&data)?; + let new_account_len = state.try_get_new_account_len(new_extension)?; + let extension_already_exists = state.get_extension_bytes::().is_ok(); + (new_account_len, extension_already_exists) + }; + + if extension_already_exists && !overwrite { + return Err(TokenError::ExtensionAlreadyInitialized.into()); + } + + if previous_account_len < new_account_len { + // account size increased, so realloc the account, then the TLV entry, then write data + account_info.realloc(new_account_len, false)?; + let mut buffer = account_info.try_borrow_mut_data()?; + if extension_already_exists { + let mut state = StateWithExtensionsMut::::unpack(&mut buffer)?; + state.realloc_variable_len_extension(new_extension)?; + } else { + if previous_account_len <= BASE_ACCOUNT_LENGTH { + set_account_type::(*buffer)?; + } + // now alloc in the TLV buffer and write the data + let mut state = StateWithExtensionsMut::::unpack(&mut buffer)?; + state.init_variable_len_extension(new_extension, false)?; + } + } else { + // do it backwards otherwise, write the state, realloc TLV, then the account + let mut buffer = account_info.try_borrow_mut_data()?; + let mut state = StateWithExtensionsMut::::unpack(&mut buffer)?; + if extension_already_exists { + state.realloc_variable_len_extension(new_extension)?; + } else { + // this situation can happen if we have an overallocated buffer + state.init_variable_len_extension(new_extension, false)?; + } + + let removed_bytes = previous_account_len + .checked_sub(new_account_len) + .ok_or(ProgramError::AccountDataTooSmall)?; + if removed_bytes > 0 { + // this is probably fine, but be safe and avoid invalidating references + drop(buffer); + account_info.realloc(new_account_len, false)?; + } + } + Ok(()) +} + #[cfg(test)] mod test { use { super::*, crate::state::test::{TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE}, - solana_program::pubkey::Pubkey, + solana_program::{ + account_info::{Account as GetAccount, IntoAccountInfo}, + clock::Epoch, + entrypoint::MAX_PERMITTED_DATA_INCREASE, + pubkey::Pubkey, + }, + spl_pod::{ + bytemuck::pod_bytes_of, optional_keys::OptionalNonZeroPubkey, primitives::PodU64, + }, transfer_fee::test::test_transfer_fee_config, }; - /// Test unsized struct + /// Test variable-length struct #[derive(Clone, Debug, PartialEq)] - struct UnsizedMintTest; - impl Extension for UnsizedMintTest { - const TYPE: ExtensionType = ExtensionType::UnsizedMintTest; + struct VariableLenMintTest { + data: Vec, + } + impl Extension for VariableLenMintTest { + const TYPE: ExtensionType = ExtensionType::VariableLenMintTest; + } + impl VariableLenPack for VariableLenMintTest { + fn pack_into_slice(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + let data_start = size_of::(); + let end = data_start + self.data.len(); + if dst.len() < end { + Err(ProgramError::InvalidAccountData) + } else { + dst[..data_start].copy_from_slice(&self.data.len().to_le_bytes()); + dst[data_start..end].copy_from_slice(&self.data); + Ok(()) + } + } + fn unpack_from_slice(src: &[u8]) -> Result { + let data_start = size_of::(); + let length = u64::from_le_bytes(src[..data_start].try_into().unwrap()) as usize; + if src[data_start..data_start + length].len() != length { + return Err(ProgramError::InvalidAccountData); + } + let data = Vec::from(&src[data_start..data_start + length]); + Ok(Self { data }) + } + fn get_packed_len(&self) -> Result { + Ok(size_of::().saturating_add(self.data.len())) + } } - impl UnsizedExtension for UnsizedMintTest {} const MINT_WITH_EXTENSION: &[u8] = &[ 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -1244,7 +1443,7 @@ mod test { #[test] fn mint_with_extension_pack_unpack() { - let mint_size = ExtensionType::try_get_account_len::(&[ + let mint_size = ExtensionType::try_calculate_account_len::(&[ ExtensionType::MintCloseAuthority, ExtensionType::TransferFeeConfig, ]) @@ -1330,7 +1529,7 @@ mod test { state.pack_base(); // check unpacking - let mut unpacked_extension = state.get_extension_mut::().unwrap(); + let unpacked_extension = state.get_extension_mut::().unwrap(); assert_eq!(*unpacked_extension, MintCloseAuthority { close_authority }); // update extension @@ -1407,7 +1606,7 @@ mod test { #[test] fn mint_extension_any_order() { - let mint_size = ExtensionType::try_get_account_len::(&[ + let mint_size = ExtensionType::try_calculate_account_len::(&[ ExtensionType::MintCloseAuthority, ExtensionType::TransferFeeConfig, ]) @@ -1499,7 +1698,8 @@ mod test { Err(ProgramError::InvalidAccountData), ); let mint_size = - ExtensionType::try_get_account_len::(&[ExtensionType::MintPaddingTest]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintPaddingTest]) + .unwrap(); assert_eq!(mint_size, Multisig::LEN + size_of::()); let mut buffer = vec![0; mint_size]; @@ -1533,9 +1733,10 @@ mod test { #[test] fn account_with_extension_pack_unpack() { - let account_size = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeAmount]) - .unwrap(); + let account_size = ExtensionType::try_calculate_account_len::(&[ + ExtensionType::TransferFeeAmount, + ]) + .unwrap(); let mut buffer = vec![0; account_size]; // fail unpack @@ -1597,7 +1798,7 @@ mod test { state.pack_base(); // check unpacking - let mut unpacked_extension = state.get_extension_mut::().unwrap(); + let unpacked_extension = state.get_extension_mut::().unwrap(); assert_eq!(*unpacked_extension, TransferFeeAmount { withheld_amount }); // update extension @@ -1634,9 +1835,10 @@ mod test { StateWithExtensionsMut::::unpack_uninitialized(&mut buffer), Err(ProgramError::InvalidAccountData), ); - let account_size = - ExtensionType::try_get_account_len::(&[ExtensionType::AccountPaddingTest]) - .unwrap(); + let account_size = ExtensionType::try_calculate_account_len::(&[ + ExtensionType::AccountPaddingTest, + ]) + .unwrap(); assert_eq!(account_size, Multisig::LEN + size_of::()); let mut buffer = vec![0; account_size]; @@ -1674,7 +1876,7 @@ mod test { // account with buffer big enough for AccountType and Extension let mut buffer = TEST_ACCOUNT_SLICE.to_vec(); let needed_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) .unwrap() - buffer.len(); buffer.append(&mut vec![0; needed_len]); @@ -1718,7 +1920,7 @@ mod test { // mint with buffer big enough for AccountType and Extension let mut buffer = TEST_MINT_SLICE.to_vec(); let needed_len = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) .unwrap() - buffer.len(); buffer.append(&mut vec![0; needed_len]); @@ -1829,7 +2031,7 @@ mod test { #[test] fn mint_without_extensions() { - let space = ExtensionType::try_get_account_len::(&[]).unwrap(); + let space = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let mut buffer = vec![0; space]; assert_eq!( StateWithExtensionsMut::::unpack_uninitialized(&mut buffer), @@ -1854,7 +2056,8 @@ mod test { #[test] fn test_init_nonzero_default() { let mint_size = - ExtensionType::try_get_account_len::(&[ExtensionType::MintPaddingTest]).unwrap(); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintPaddingTest]) + .unwrap(); let mut buffer = vec![0; mint_size]; let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); state.base = TEST_MINT; @@ -1869,7 +2072,7 @@ mod test { #[test] fn test_init_buffer_too_small() { let mint_size = - ExtensionType::try_get_account_len::(&[ExtensionType::MintCloseAuthority]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::MintCloseAuthority]) .unwrap(); let mut buffer = vec![0; mint_size - 1]; let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); @@ -1895,19 +2098,16 @@ mod test { assert_eq!(state.get_extension_types().unwrap(), vec![]); - // malformed since there aren't two bytes for the type + // OK, there aren't two bytes for the type, but that's fine let mut buffer = vec![0; BASE_ACCOUNT_LENGTH + 2]; let state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); - assert_eq!( - state.get_extension_types().unwrap_err(), - ProgramError::InvalidAccountData - ); + assert_eq!(state.get_extension_types().unwrap(), []); } #[test] fn test_extension_with_no_data() { let account_size = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) .unwrap(); let mut buffer = vec![0; account_size]; let mut state = @@ -1939,9 +2139,9 @@ mod test { #[test] fn fail_account_len_with_metadata() { assert_eq!( - ExtensionType::try_get_account_len::(&[ + ExtensionType::try_calculate_account_len::(&[ ExtensionType::MintCloseAuthority, - ExtensionType::UnsizedMintTest, + ExtensionType::VariableLenMintTest, ExtensionType::TransferFeeConfig, ]) .unwrap_err(), @@ -1951,28 +2151,33 @@ mod test { #[test] fn alloc() { - let alloc_size = 1; + let variable_len = VariableLenMintTest { data: vec![1] }; + let alloc_size = variable_len.get_packed_len().unwrap(); let account_size = BASE_ACCOUNT_LENGTH + size_of::() + add_type_and_length_to_len(alloc_size); let mut buffer = vec![0; account_size]; let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); - let _ = state.alloc::(alloc_size, false).unwrap(); + state + .init_variable_len_extension(&variable_len, false) + .unwrap(); // can't double alloc assert_eq!( state - .alloc::(alloc_size, false) + .init_variable_len_extension(&variable_len, false) .unwrap_err(), TokenError::ExtensionAlreadyInitialized.into() ); // unless overwrite is set - state.alloc::(alloc_size, true).unwrap(); + state + .init_variable_len_extension(&variable_len, true) + .unwrap(); // can't change the size during overwrite though assert_eq!( state - .alloc::(alloc_size - 1, true) + .init_variable_len_extension(&VariableLenMintTest { data: vec![] }, true) .unwrap_err(), TokenError::InvalidLengthForAlloc.into() ); @@ -1980,7 +2185,7 @@ mod test { // try to write too far, fail earlier assert_eq!( state - .alloc::(alloc_size + 1, true) + .init_variable_len_extension(&VariableLenMintTest { data: vec![1, 2] }, true) .unwrap_err(), ProgramError::InvalidAccountData ); @@ -1988,45 +2193,345 @@ mod test { #[test] fn realloc() { - const ALLOC_SIZE: usize = 5; - const BIG_SIZE: usize = 10; - const SMALL_SIZE: usize = 2; + let small_variable_len = VariableLenMintTest { + data: vec![1, 2, 3], + }; + let base_variable_len = VariableLenMintTest { + data: vec![1, 2, 3, 4], + }; + let big_variable_len = VariableLenMintTest { + data: vec![1, 2, 3, 4, 5], + }; + let too_big_variable_len = VariableLenMintTest { + data: vec![1, 2, 3, 4, 5, 6], + }; let account_size = - ExtensionType::try_get_account_len::(&[ExtensionType::MetadataPointer]).unwrap() - + add_type_and_length_to_len(BIG_SIZE); + ExtensionType::try_calculate_account_len::(&[ExtensionType::MetadataPointer]) + .unwrap() + + add_type_and_length_to_len(big_variable_len.get_packed_len().unwrap()); let mut buffer = vec![0; account_size]; let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); // alloc both types - let _ = state.alloc::(ALLOC_SIZE, false).unwrap(); + state + .init_variable_len_extension(&base_variable_len, false) + .unwrap(); let max_pubkey = OptionalNonZeroPubkey::try_from(Some(Pubkey::new_from_array([255; 32]))).unwrap(); let extension = state.init_extension::(false).unwrap(); extension.authority = max_pubkey; extension.metadata_address = max_pubkey; - // realloc first entry to larger, new bytes are all 0 - let data = state.realloc::(BIG_SIZE).unwrap(); - assert_eq!(data, [0; BIG_SIZE]); + // realloc first entry to larger + state + .realloc_variable_len_extension(&big_variable_len) + .unwrap(); + let extension = state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(extension, big_variable_len); let extension = state.get_extension::().unwrap(); assert_eq!(extension.authority, max_pubkey); assert_eq!(extension.metadata_address, max_pubkey); - // realloc to smaller, removed bytes are all 0 - let data = state.realloc::(SMALL_SIZE).unwrap(); - assert_eq!(data, [0; SMALL_SIZE]); + // realloc to smaller + state + .realloc_variable_len_extension(&small_variable_len) + .unwrap(); + let extension = state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(extension, small_variable_len); let extension = state.get_extension::().unwrap(); assert_eq!(extension.authority, max_pubkey); assert_eq!(extension.metadata_address, max_pubkey); - const DIFF: usize = BIG_SIZE - SMALL_SIZE; - assert_eq!(&buffer[account_size - DIFF..account_size], [0; DIFF]); + let diff = big_variable_len.get_packed_len().unwrap() + - small_variable_len.get_packed_len().unwrap(); + assert_eq!(&buffer[account_size - diff..account_size], vec![0; diff]); // unpack again since we dropped the last `state` let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); // realloc too much, fails assert_eq!( - state.realloc::(BIG_SIZE + 1).unwrap_err(), + state + .realloc_variable_len_extension(&too_big_variable_len) + .unwrap_err(), ProgramError::InvalidAccountData, ); } + + #[test] + fn account_len() { + let small_variable_len = VariableLenMintTest { + data: vec![20, 30, 40], + }; + let variable_len = VariableLenMintTest { + data: vec![20, 30, 40, 50], + }; + let big_variable_len = VariableLenMintTest { + data: vec![20, 30, 40, 50, 60], + }; + let value_len = variable_len.get_packed_len().unwrap(); + let account_size = + BASE_ACCOUNT_LENGTH + size_of::() + add_type_and_length_to_len(value_len); + let mut buffer = vec![0; account_size]; + let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); + + // With a new extension, new length must include padding, 1 byte for + // account type, 2 bytes for type, 2 for length + let current_len = state.try_get_account_len().unwrap(); + assert_eq!(current_len, Mint::LEN); + let new_len = state + .try_get_new_account_len::(&variable_len) + .unwrap(); + assert_eq!( + new_len, + BASE_ACCOUNT_AND_TYPE_LENGTH.saturating_add(add_type_and_length_to_len(value_len)) + ); + + state + .init_variable_len_extension::(&variable_len, false) + .unwrap(); + let current_len = state.try_get_account_len().unwrap(); + assert_eq!(current_len, new_len); + + // Reduce the extension size + let new_len = state + .try_get_new_account_len::(&small_variable_len) + .unwrap(); + assert_eq!(current_len.checked_sub(new_len).unwrap(), 1); + + // Increase the extension size + let new_len = state + .try_get_new_account_len::(&big_variable_len) + .unwrap(); + assert_eq!(new_len.checked_sub(current_len).unwrap(), 1); + + // Maintain the extension size + let new_len = state + .try_get_new_account_len::(&variable_len) + .unwrap(); + assert_eq!(new_len, current_len); + } + + /// Test helper for mimicking the data layout an on-chain `AccountInfo`, + /// which permits "reallocs" as the Solana runtime does it + struct SolanaAccountData { + data: Vec, + lamports: u64, + owner: Pubkey, + } + impl SolanaAccountData { + /// Create a new fake solana account data. The underlying vector is + /// overallocated to mimic the runtime + fn new(account_data: &[u8]) -> Self { + let mut data = vec![]; + data.extend_from_slice(&(account_data.len() as u64).to_le_bytes()); + data.extend_from_slice(account_data); + data.extend_from_slice(&[0; MAX_PERMITTED_DATA_INCREASE]); + Self { + data, + lamports: 10, + owner: Pubkey::new_unique(), + } + } + + /// Data lops off the first 8 bytes, since those store the size of the + /// account for the Solana runtime + fn data(&self) -> &[u8] { + let start = size_of::(); + let len = self.len(); + &self.data[start..start + len] + } + + /// Gets the runtime length of the account data + fn len(&self) -> usize { + self.data + .get(..size_of::()) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .unwrap() as usize + } + } + impl GetAccount for SolanaAccountData { + fn get(&mut self) -> (&mut u64, &mut [u8], &Pubkey, bool, Epoch) { + // need to pull out the data here to avoid a double-mutable borrow + let start = size_of::(); + let len = self.len(); + ( + &mut self.lamports, + &mut self.data[start..start + len], + &self.owner, + false, + Epoch::default(), + ) + } + } + + #[test] + fn alloc_new_tlv_in_account_info_from_base_size() { + let variable_len = VariableLenMintTest { data: vec![20, 99] }; + let value_len = variable_len.get_packed_len().unwrap(); + let base_account_size = Mint::LEN; + let mut buffer = vec![0; base_account_size]; + let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); + state.base = TEST_MINT; + state.pack_base(); + + let mut data = SolanaAccountData::new(&buffer); + let key = Pubkey::new_unique(); + let account_info = (&key, &mut data).into_account_info(); + + alloc_and_serialize::(&account_info, &variable_len, false).unwrap(); + let new_account_len = BASE_ACCOUNT_AND_TYPE_LENGTH + add_type_and_length_to_len(value_len); + assert_eq!(data.len(), new_account_len); + let state = StateWithExtensions::::unpack(data.data()).unwrap(); + assert_eq!( + state + .get_variable_len_extension::() + .unwrap(), + variable_len + ); + + // alloc again succeeds with "overwrite" + let account_info = (&key, &mut data).into_account_info(); + alloc_and_serialize::(&account_info, &variable_len, true).unwrap(); + + // alloc again fails without "overwrite" + let account_info = (&key, &mut data).into_account_info(); + assert_eq!( + alloc_and_serialize::(&account_info, &variable_len, false).unwrap_err(), + TokenError::ExtensionAlreadyInitialized.into() + ); + } + + #[test] + fn alloc_new_tlv_in_account_info_from_extended_size() { + let variable_len = VariableLenMintTest { data: vec![42, 6] }; + let value_len = variable_len.get_packed_len().unwrap(); + let account_size = + ExtensionType::try_calculate_account_len::(&[ExtensionType::MetadataPointer]) + .unwrap() + + add_type_and_length_to_len(value_len); + let mut buffer = vec![0; account_size]; + let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); + state.base = TEST_MINT; + state.pack_base(); + state.init_account_type().unwrap(); + + let test_key = + OptionalNonZeroPubkey::try_from(Some(Pubkey::new_from_array([20; 32]))).unwrap(); + let extension = state.init_extension::(false).unwrap(); + extension.authority = test_key; + extension.metadata_address = test_key; + + let mut data = SolanaAccountData::new(&buffer); + let key = Pubkey::new_unique(); + let account_info = (&key, &mut data).into_account_info(); + + alloc_and_serialize::(&account_info, &variable_len, false).unwrap(); + let new_account_len = BASE_ACCOUNT_AND_TYPE_LENGTH + + add_type_and_length_to_len(value_len) + + add_type_and_length_to_len(size_of::()); + assert_eq!(data.len(), new_account_len); + let state = StateWithExtensions::::unpack(data.data()).unwrap(); + assert_eq!( + state + .get_variable_len_extension::() + .unwrap(), + variable_len + ); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, test_key); + assert_eq!(extension.metadata_address, test_key); + + // alloc again succeeds with "overwrite" + let account_info = (&key, &mut data).into_account_info(); + alloc_and_serialize::(&account_info, &variable_len, true).unwrap(); + + // alloc again fails without "overwrite" + let account_info = (&key, &mut data).into_account_info(); + assert_eq!( + alloc_and_serialize::(&account_info, &variable_len, false).unwrap_err(), + TokenError::ExtensionAlreadyInitialized.into() + ); + } + + #[test] + fn realloc_tlv_in_account_info() { + let variable_len = VariableLenMintTest { + data: vec![1, 2, 3, 4, 5], + }; + let alloc_size = variable_len.get_packed_len().unwrap(); + let account_size = + ExtensionType::try_calculate_account_len::(&[ExtensionType::MetadataPointer]) + .unwrap() + + add_type_and_length_to_len(alloc_size); + let mut buffer = vec![0; account_size]; + let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut buffer).unwrap(); + state.base = TEST_MINT; + state.pack_base(); + state.init_account_type().unwrap(); + + // alloc both types + state + .init_variable_len_extension(&variable_len, false) + .unwrap(); + let max_pubkey = + OptionalNonZeroPubkey::try_from(Some(Pubkey::new_from_array([255; 32]))).unwrap(); + let extension = state.init_extension::(false).unwrap(); + extension.authority = max_pubkey; + extension.metadata_address = max_pubkey; + + // reallocate to smaller, make sure existing extension is fine + let mut data = SolanaAccountData::new(&buffer); + let key = Pubkey::new_unique(); + let account_info = (&key, &mut data).into_account_info(); + let variable_len = VariableLenMintTest { data: vec![1, 2] }; + alloc_and_serialize::(&account_info, &variable_len, true).unwrap(); + + let state = StateWithExtensions::::unpack(data.data()).unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, max_pubkey); + assert_eq!(extension.metadata_address, max_pubkey); + let extension = state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(extension, variable_len); + assert_eq!(data.len(), state.try_get_account_len().unwrap()); + + // reallocate to larger + let account_info = (&key, &mut data).into_account_info(); + let variable_len = VariableLenMintTest { + data: vec![1, 2, 3, 4, 5, 6, 7], + }; + alloc_and_serialize::(&account_info, &variable_len, true).unwrap(); + + let state = StateWithExtensions::::unpack(data.data()).unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, max_pubkey); + assert_eq!(extension.metadata_address, max_pubkey); + let extension = state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(extension, variable_len); + assert_eq!(data.len(), state.try_get_account_len().unwrap()); + + // reallocate to same + let account_info = (&key, &mut data).into_account_info(); + let variable_len = VariableLenMintTest { + data: vec![7, 6, 5, 4, 3, 2, 1], + }; + alloc_and_serialize::(&account_info, &variable_len, true).unwrap(); + + let state = StateWithExtensions::::unpack(data.data()).unwrap(); + let extension = state.get_extension::().unwrap(); + assert_eq!(extension.authority, max_pubkey); + assert_eq!(extension.metadata_address, max_pubkey); + let extension = state + .get_variable_len_extension::() + .unwrap(); + assert_eq!(extension, variable_len); + assert_eq!(data.len(), state.try_get_account_len().unwrap()); + } } diff --git a/token/program-2022/src/extension/non_transferable.rs b/token/program-2022/src/extension/non_transferable.rs index fea457d5ad9..a6321161f61 100644 --- a/token/program-2022/src/extension/non_transferable.rs +++ b/token/program-2022/src/extension/non_transferable.rs @@ -3,12 +3,19 @@ use { bytemuck::{Pod, Zeroable}, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Indicates that the tokens from this mint can't be transfered +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)] pub struct NonTransferable; /// Indicates that the tokens from this account belong to a non-transferable mint +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)] pub struct NonTransferableAccount; diff --git a/token/program-2022/src/extension/permanent_delegate.rs b/token/program-2022/src/extension/permanent_delegate.rs index bfd7bcc10c0..f70e9516be7 100644 --- a/token/program-2022/src/extension/permanent_delegate.rs +++ b/token/program-2022/src/extension/permanent_delegate.rs @@ -1,14 +1,17 @@ use { - crate::{ - extension::{BaseState, BaseStateWithExtensions, Extension, ExtensionType}, - pod::*, - }, + crate::extension::{BaseState, BaseStateWithExtensions, Extension, ExtensionType}, bytemuck::{Pod, Zeroable}, solana_program::pubkey::Pubkey, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Permanent delegate extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct PermanentDelegate { /// Optional permanent delegate for transferring or burning tokens diff --git a/token/program-2022/src/extension/reallocate.rs b/token/program-2022/src/extension/reallocate.rs index d4ea7328319..df2a8f12482 100644 --- a/token/program-2022/src/extension/reallocate.rs +++ b/token/program-2022/src/extension/reallocate.rs @@ -55,10 +55,10 @@ pub fn process_reallocate( { return Err(TokenError::InvalidState.into()); } - // ExtensionType::try_get_account_len() dedupes types, so just a dumb concatenation is fine here + // ExtensionType::try_calculate_account_len() dedupes types, so just a dumb concatenation is fine here current_extension_types.extend_from_slice(&new_extension_types); let needed_account_len = - ExtensionType::try_get_account_len::(¤t_extension_types)?; + ExtensionType::try_calculate_account_len::(¤t_extension_types)?; // if account is already large enough, return early if token_account_info.data_len() >= needed_account_len { diff --git a/token/program-2022/src/extension/token_metadata/mod.rs b/token/program-2022/src/extension/token_metadata/mod.rs new file mode 100644 index 00000000000..cc96af20b5a --- /dev/null +++ b/token/program-2022/src/extension/token_metadata/mod.rs @@ -0,0 +1,11 @@ +use { + crate::extension::{Extension, ExtensionType}, + spl_token_metadata_interface::state::TokenMetadata, +}; + +/// Instruction processor for the TokenMetadata extension +pub mod processor; + +impl Extension for TokenMetadata { + const TYPE: ExtensionType = ExtensionType::TokenMetadata; +} diff --git a/token/program-2022/src/extension/token_metadata/processor.rs b/token/program-2022/src/extension/token_metadata/processor.rs new file mode 100644 index 00000000000..88f58d8a9ec --- /dev/null +++ b/token/program-2022/src/extension/token_metadata/processor.rs @@ -0,0 +1,234 @@ +//! Token-metadata processor + +use { + crate::{ + check_program_account, + error::TokenError, + extension::{ + alloc_and_serialize, metadata_pointer::MetadataPointer, BaseStateWithExtensions, + StateWithExtensions, + }, + state::Mint, + }, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + program::set_return_data, + program_error::ProgramError, + program_option::COption, + pubkey::Pubkey, + }, + spl_pod::optional_keys::OptionalNonZeroPubkey, + spl_token_metadata_interface::{ + error::TokenMetadataError, + instruction::{ + Emit, Initialize, RemoveKey, TokenMetadataInstruction, UpdateAuthority, UpdateField, + }, + state::TokenMetadata, + }, +}; + +fn check_update_authority( + update_authority_info: &AccountInfo, + expected_update_authority: &OptionalNonZeroPubkey, +) -> Result<(), ProgramError> { + if !update_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + let update_authority = Option::::from(*expected_update_authority) + .ok_or(TokenMetadataError::ImmutableMetadata)?; + if update_authority != *update_authority_info.key { + return Err(TokenMetadataError::IncorrectUpdateAuthority.into()); + } + Ok(()) +} + +/// Processes a [Initialize](enum.TokenMetadataInstruction.html) instruction. +pub fn process_initialize( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: Initialize, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let metadata_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let mint_authority_info = next_account_info(account_info_iter)?; + + // check that the mint and metadata accounts are the same, since the metadata + // extension should only describe itself + if metadata_info.key != mint_info.key { + msg!("Metadata for a mint must be initialized in the mint itself."); + return Err(TokenError::MintMismatch.into()); + } + + // scope the mint authority check, since the mint is in the same account! + { + // This check isn't really needed since we'll be writing into the account, + // but auditors like it + check_program_account(mint_info.owner)?; + let mint_data = mint_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + if !mint_authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if mint.base.mint_authority.as_ref() != COption::Some(mint_authority_info.key) { + return Err(TokenMetadataError::IncorrectMintAuthority.into()); + } + + if mint.get_extension::().is_err() { + msg!("A mint with metadata must have the metadata-pointer extension initialized"); + return Err(TokenError::InvalidExtensionCombination.into()); + } + } + + // Create the token metadata + let update_authority = OptionalNonZeroPubkey::try_from(Some(*update_authority_info.key))?; + let token_metadata = TokenMetadata { + name: data.name, + symbol: data.symbol, + uri: data.uri, + update_authority, + mint: *mint_info.key, + ..Default::default() + }; + + // allocate a TLV entry for the space and write it in, assumes that there's + // enough SOL for the new rent-exemption + alloc_and_serialize::(metadata_info, &token_metadata, false)?; + + Ok(()) +} + +/// Processes an [UpdateField](enum.TokenMetadataInstruction.html) instruction. +pub fn process_update_field( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: UpdateField, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let metadata_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + + // deserialize the metadata, but scope the data borrow since we'll probably + // realloc the account + let mut token_metadata = { + let buffer = metadata_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&buffer)?; + mint.get_variable_len_extension::()? + }; + + check_update_authority(update_authority_info, &token_metadata.update_authority)?; + + // Update the field + token_metadata.update(data.field, data.value); + + // Update / realloc the account + alloc_and_serialize::(metadata_info, &token_metadata, true)?; + + Ok(()) +} + +/// Processes a [RemoveKey](enum.TokenMetadataInstruction.html) instruction. +pub fn process_remove_key( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: RemoveKey, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let metadata_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + + // deserialize the metadata, but scope the data borrow since we'll probably + // realloc the account + let mut token_metadata = { + let buffer = metadata_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&buffer)?; + mint.get_variable_len_extension::()? + }; + + check_update_authority(update_authority_info, &token_metadata.update_authority)?; + if !token_metadata.remove_key(&data.key) && !data.idempotent { + return Err(TokenMetadataError::KeyNotFound.into()); + } + alloc_and_serialize::(metadata_info, &token_metadata, true)?; + Ok(()) +} + +/// Processes a [UpdateAuthority](enum.TokenMetadataInstruction.html) instruction. +pub fn process_update_authority( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: UpdateAuthority, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let metadata_info = next_account_info(account_info_iter)?; + let update_authority_info = next_account_info(account_info_iter)?; + + // deserialize the metadata, but scope the data borrow since we'll write + // to the account later + let mut token_metadata = { + let buffer = metadata_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&buffer)?; + mint.get_variable_len_extension::()? + }; + + check_update_authority(update_authority_info, &token_metadata.update_authority)?; + token_metadata.update_authority = data.new_authority; + // Update the account, no realloc needed! + alloc_and_serialize::(metadata_info, &token_metadata, true)?; + + Ok(()) +} + +/// Processes an [Emit](enum.TokenMetadataInstruction.html) instruction. +pub fn process_emit(program_id: &Pubkey, accounts: &[AccountInfo], data: Emit) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let metadata_info = next_account_info(account_info_iter)?; + + if metadata_info.owner != program_id { + return Err(ProgramError::IllegalOwner); + } + + let buffer = metadata_info.try_borrow_data()?; + let state = StateWithExtensions::::unpack(&buffer)?; + let metadata_bytes = state.get_extension_bytes::()?; + + if let Some(range) = TokenMetadata::get_slice(metadata_bytes, data.start, data.end) { + set_return_data(range); + } + Ok(()) +} + +/// Processes an [Instruction](enum.Instruction.html). +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction: TokenMetadataInstruction, +) -> ProgramResult { + match instruction { + TokenMetadataInstruction::Initialize(data) => { + msg!("TokenMetadataInstruction: Initialize"); + process_initialize(program_id, accounts, data) + } + TokenMetadataInstruction::UpdateField(data) => { + msg!("TokenMetadataInstruction: UpdateField"); + process_update_field(program_id, accounts, data) + } + TokenMetadataInstruction::RemoveKey(data) => { + msg!("TokenMetadataInstruction: RemoveKey"); + process_remove_key(program_id, accounts, data) + } + TokenMetadataInstruction::UpdateAuthority(data) => { + msg!("TokenMetadataInstruction: UpdateAuthority"); + process_update_authority(program_id, accounts, data) + } + TokenMetadataInstruction::Emit(data) => { + msg!("TokenMetadataInstruction: Emit"); + process_emit(program_id, accounts, data) + } + } +} diff --git a/token/program-2022/src/extension/transfer_fee/instruction.rs b/token/program-2022/src/extension/transfer_fee/instruction.rs index f6ae38dc5d7..c28de0941be 100644 --- a/token/program-2022/src/extension/transfer_fee/instruction.rs +++ b/token/program-2022/src/extension/transfer_fee/instruction.rs @@ -17,6 +17,10 @@ use { /// Transfer Fee extension instructions #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "serde-traits", + serde(rename_all = "camelCase", rename_all_fields = "camelCase") +)] #[derive(Clone, Copy, Debug, PartialEq)] #[repr(u8)] pub enum TransferFeeInstruction { diff --git a/token/program-2022/src/extension/transfer_fee/mod.rs b/token/program-2022/src/extension/transfer_fee/mod.rs index 10b89f49968..1ea46ac92ce 100644 --- a/token/program-2022/src/extension/transfer_fee/mod.rs +++ b/token/program-2022/src/extension/transfer_fee/mod.rs @@ -2,16 +2,22 @@ use { crate::{ error::TokenError, extension::{Extension, ExtensionType}, - pod::*, }, bytemuck::{Pod, Zeroable}, solana_program::{clock::Epoch, entrypoint::ProgramResult}, + spl_pod::{ + optional_keys::OptionalNonZeroPubkey, + primitives::{PodU16, PodU64}, + }, std::{ cmp, convert::{TryFrom, TryInto}, }, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Transfer fee extension instructions pub mod instruction; @@ -24,6 +30,8 @@ const ONE_IN_BASIS_POINTS: u128 = MAX_FEE_BASIS_POINTS as u128; /// Transfer fee information #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct TransferFee { /// First epoch where the transfer fee takes effect @@ -107,6 +115,8 @@ impl TransferFee { /// Transfer fee extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct TransferFeeConfig { /// Optional authority to set the fee @@ -145,6 +155,8 @@ impl Extension for TransferFeeConfig { /// Transfer fee extension data for accounts. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct TransferFeeAmount { /// Amount withheld during transfers, to be harvested to the mint diff --git a/token/program-2022/src/extension/transfer_hook/instruction.rs b/token/program-2022/src/extension/transfer_hook/instruction.rs index 902270bdf80..710879c6208 100644 --- a/token/program-2022/src/extension/transfer_hook/instruction.rs +++ b/token/program-2022/src/extension/transfer_hook/instruction.rs @@ -2,7 +2,6 @@ use { crate::{ check_program_account, instruction::{encode_instruction, TokenInstruction}, - pod::OptionalNonZeroPubkey, }, bytemuck::{Pod, Zeroable}, num_enum::{IntoPrimitive, TryFromPrimitive}, @@ -11,10 +10,16 @@ use { program_error::ProgramError, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, std::convert::TryInto, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Transfer hook extension instructions +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum TransferHookInstruction { @@ -56,6 +61,8 @@ pub enum TransferHookInstruction { } /// Data expected by `Initialize` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct InitializeInstructionData { @@ -66,6 +73,8 @@ pub struct InitializeInstructionData { } /// Data expected by `Update` +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct UpdateInstructionData { diff --git a/token/program-2022/src/extension/transfer_hook/mod.rs b/token/program-2022/src/extension/transfer_hook/mod.rs index cf1414cc23d..a8f489ac5d8 100644 --- a/token/program-2022/src/extension/transfer_hook/mod.rs +++ b/token/program-2022/src/extension/transfer_hook/mod.rs @@ -3,13 +3,16 @@ use { extension::{ BaseState, BaseStateWithExtensions, Extension, ExtensionType, StateWithExtensionsMut, }, - pod::{OptionalNonZeroPubkey, PodBool}, state::Account, }, bytemuck::{Pod, Zeroable}, solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}, + spl_pod::{optional_keys::OptionalNonZeroPubkey, primitives::PodBool}, }; +#[cfg(feature = "serde-traits")] +use serde::{Deserialize, Serialize}; + /// Instructions for the TransferHook extension pub mod instruction; /// Instruction processor for the TransferHook extension @@ -17,6 +20,8 @@ pub mod processor; /// Transfer hook extension data for mints. #[repr(C)] +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct TransferHook { /// Authority that can set the transfer hook program id @@ -26,6 +31,8 @@ pub struct TransferHook { } /// Indicates that the tokens from this account belong to a mint with a transfer hook +#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)] pub struct TransferHookAccount { diff --git a/token/program-2022/src/extension/transfer_hook/processor.rs b/token/program-2022/src/extension/transfer_hook/processor.rs index c93dfc39187..41c41bcd736 100644 --- a/token/program-2022/src/extension/transfer_hook/processor.rs +++ b/token/program-2022/src/extension/transfer_hook/processor.rs @@ -12,7 +12,6 @@ use { StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, - pod::OptionalNonZeroPubkey, processor::Processor, state::Mint, }, @@ -23,6 +22,7 @@ use { program_error::ProgramError, pubkey::Pubkey, }, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; fn process_initialize( @@ -45,7 +45,7 @@ fn process_initialize( } } else if Option::::from(*authority).is_none() { msg!("The transfer hook extension requires at least an authority or a program id for initialization, neither was provided"); - return Err(TokenError::InvalidInstruction)?; + Err(TokenError::InvalidInstruction)?; } extension.program_id = *transfer_hook_program_id; Ok(()) diff --git a/token/program-2022/src/instruction.rs b/token/program-2022/src/instruction.rs index ddd56fc8447..95dd690de18 100644 --- a/token/program-2022/src/instruction.rs +++ b/token/program-2022/src/instruction.rs @@ -7,7 +7,6 @@ use { check_program_account, check_spl_token_program_account, error::TokenError, extension::{transfer_fee::instruction::TransferFeeInstruction, ExtensionType}, - pod::{pod_from_bytes, pod_get_packed_len}, }, bytemuck::Pod, solana_program::{ @@ -17,6 +16,7 @@ use { pubkey::{Pubkey, PUBKEY_BYTES}, system_program, sysvar, }, + spl_pod::bytemuck::{pod_from_bytes, pod_get_packed_len}, std::{ convert::{TryFrom, TryInto}, mem::size_of, @@ -42,6 +42,10 @@ const U64_BYTES: usize = 8; /// Instructions supported by the token program. #[repr(C)] #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "serde-traits", + serde(rename_all_fields = "camelCase", rename_all = "camelCase") +)] #[derive(Clone, Debug, PartialEq)] pub enum TokenInstruction<'a> { /// Initializes a new mint and optionally deposits all the newly minted @@ -420,6 +424,7 @@ pub enum TokenInstruction<'a> { /// 2. `[]` Rent sysvar InitializeAccount2 { /// The new account's owner/multisignature. + #[cfg_attr(feature = "serde-traits", serde(with = "As::"))] owner: Pubkey, }, /// Given a wrapped / native token account (a token account containing SOL) @@ -440,6 +445,7 @@ pub enum TokenInstruction<'a> { /// 1. `[]` The mint this account will be associated with. InitializeAccount3 { /// The new account's owner/multisignature. + #[cfg_attr(feature = "serde-traits", serde(with = "As::"))] owner: Pubkey, }, /// Like InitializeMultisig, but does not require the Rent sysvar to be provided @@ -635,6 +641,7 @@ pub enum TokenInstruction<'a> { /// InitializePermanentDelegate { /// Authority that may sign for `Transfer`s and `Burn`s on any account + #[cfg_attr(feature = "serde-traits", serde(with = "As::"))] delegate: Pubkey, }, /// The common instruction prefix for transfer hook extension instructions. @@ -663,6 +670,12 @@ pub enum TokenInstruction<'a> { /// for further details about the extended instructions that share this instruction /// prefix MetadataPointerExtension, + /// The common instruction prefix for group pointer extension instructions. + /// + /// See `extension::group_pointer::instruction::GroupPointerInstruction` + /// for further details about the extended instructions that share this instruction + /// prefix + GroupPointerExtension, } impl<'a> TokenInstruction<'a> { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -802,6 +815,7 @@ impl<'a> TokenInstruction<'a> { 37 => Self::ConfidentialTransferFeeExtension, 38 => Self::WithdrawExcessLamports, 39 => Self::MetadataPointerExtension, + 40 => Self::GroupPointerExtension, _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -967,6 +981,9 @@ impl<'a> TokenInstruction<'a> { &Self::MetadataPointerExtension => { buf.push(39); } + &Self::GroupPointerExtension => { + buf.push(40); + } }; buf } @@ -1030,6 +1047,7 @@ impl<'a> TokenInstruction<'a> { /// Specifies the authority type for SetAuthority instructions #[repr(u8)] #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] #[derive(Clone, Debug, PartialEq)] pub enum AuthorityType { /// Authority to mint new tokens @@ -1059,6 +1077,8 @@ pub enum AuthorityType { ConfidentialTransferFeeConfig, /// Authority to set the metadata address MetadataPointer, + /// Authority to set the group address + GroupPointer, } impl AuthorityType { @@ -1077,6 +1097,7 @@ impl AuthorityType { AuthorityType::TransferHookProgramId => 10, AuthorityType::ConfidentialTransferFeeConfig => 11, AuthorityType::MetadataPointer => 12, + AuthorityType::GroupPointer => 13, } } @@ -1095,6 +1116,7 @@ impl AuthorityType { 10 => Ok(AuthorityType::TransferHookProgramId), 11 => Ok(AuthorityType::ConfidentialTransferFeeConfig), 12 => Ok(AuthorityType::MetadataPointer), + 13 => Ok(AuthorityType::GroupPointer), _ => Err(TokenError::InvalidInstruction.into()), } } diff --git a/token/program-2022/src/lib.rs b/token/program-2022/src/lib.rs index 8d2848189d7..13077557224 100644 --- a/token/program-2022/src/lib.rs +++ b/token/program-2022/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] @@ -11,7 +11,6 @@ pub mod instruction; pub mod native_mint; pub mod offchain; pub mod onchain; -pub mod pod; pub mod processor; pub mod proof; #[cfg(feature = "serde-traits")] @@ -28,6 +27,7 @@ use solana_program::{ program_error::ProgramError, program_memory::sol_memcmp, pubkey::{Pubkey, PUBKEY_BYTES}, + system_program, }; pub use solana_zk_token_sdk; @@ -109,6 +109,22 @@ pub fn check_spl_token_program_account(spl_token_program_id: &Pubkey) -> Program Ok(()) } +/// Checks that the supplied program ID is correct for the ZK Token proof program +pub fn check_zk_token_proof_program_account(zk_token_proof_program_id: &Pubkey) -> ProgramResult { + if zk_token_proof_program_id != &solana_zk_token_sdk::zk_token_proof_program::id() { + return Err(ProgramError::IncorrectProgramId); + } + Ok(()) +} + +/// Checks if the spplied program ID is that of the system program +pub fn check_system_program_account(system_program_id: &Pubkey) -> ProgramResult { + if system_program_id != &system_program::id() { + return Err(ProgramError::IncorrectProgramId); + } + Ok(()) +} + /// Checks two pubkeys for equality in a computationally cheap way using /// `sol_memcmp` pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool { diff --git a/token/program-2022/src/offchain.rs b/token/program-2022/src/offchain.rs index 3b2802322db..94ca6c5590a 100644 --- a/token/program-2022/src/offchain.rs +++ b/token/program-2022/src/offchain.rs @@ -6,8 +6,8 @@ use { extension::{transfer_hook, StateWithExtensions}, state::Mint, }, - solana_program::{instruction::AccountMeta, program_error::ProgramError, pubkey::Pubkey}, - spl_transfer_hook_interface::offchain::get_extra_account_metas, + solana_program::{instruction::Instruction, program_error::ProgramError, pubkey::Pubkey}, + spl_transfer_hook_interface::offchain::resolve_extra_account_metas, std::future::Future, }; @@ -32,23 +32,23 @@ use { /// &mint, /// ).await?; /// ``` -pub async fn get_extra_transfer_account_metas( - account_metas: &mut Vec, - get_account_data_fn: F, +pub async fn resolve_extra_transfer_account_metas( + instruction: &mut Instruction, + fetch_account_data_fn: F, mint_address: &Pubkey, ) -> Result<(), AccountFetchError> where F: Fn(Pubkey) -> Fut, Fut: Future, { - let mint_data = get_account_data_fn(*mint_address) + let mint_data = fetch_account_data_fn(*mint_address) .await? .ok_or(ProgramError::InvalidAccountData)?; let mint = StateWithExtensions::::unpack(&mint_data)?; if let Some(program_id) = transfer_hook::get_program_id(&mint) { - get_extra_account_metas( - account_metas, - get_account_data_fn, + resolve_extra_account_metas( + instruction, + fetch_account_data_fn, mint_address, &program_id, ) diff --git a/token/program-2022/src/pod.rs b/token/program-2022/src/pod.rs deleted file mode 100644 index 88691b1a562..00000000000 --- a/token/program-2022/src/pod.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! Solana program utilities for Plain Old Data types -use { - bytemuck::{Pod, Zeroable}, - solana_program::{program_error::ProgramError, program_option::COption, pubkey::Pubkey}, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, - std::convert::TryFrom, -}; - -/// A Pubkey that encodes `None` as all `0`, meant to be usable as a Pod type, -/// similar to all NonZero* number types from the bytemuck library. -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct OptionalNonZeroPubkey(Pubkey); -impl TryFrom> for OptionalNonZeroPubkey { - type Error = ProgramError; - fn try_from(p: Option) -> Result { - match p { - None => Ok(Self(Pubkey::default())), - Some(pubkey) => { - if pubkey == Pubkey::default() { - Err(ProgramError::InvalidArgument) - } else { - Ok(Self(pubkey)) - } - } - } - } -} -impl TryFrom> for OptionalNonZeroPubkey { - type Error = ProgramError; - fn try_from(p: COption) -> Result { - match p { - COption::None => Ok(Self(Pubkey::default())), - COption::Some(pubkey) => { - if pubkey == Pubkey::default() { - Err(ProgramError::InvalidArgument) - } else { - Ok(Self(pubkey)) - } - } - } - } -} -impl From for Option { - fn from(p: OptionalNonZeroPubkey) -> Self { - if p.0 == Pubkey::default() { - None - } else { - Some(p.0) - } - } -} -impl From for COption { - fn from(p: OptionalNonZeroPubkey) -> Self { - if p.0 == Pubkey::default() { - COption::None - } else { - COption::Some(p.0) - } - } -} - -/// An ElGamalPubkey that encodes `None` as all `0`, meant to be usable as a Pod type. -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct OptionalNonZeroElGamalPubkey(ElGamalPubkey); -impl OptionalNonZeroElGamalPubkey { - /// Checks equality between an OptionalNonZeroElGamalPubkey and an ElGamalPubkey when - /// interpreted as bytes. - pub fn equals(&self, other: &ElGamalPubkey) -> bool { - &self.0 == other - } -} -impl TryFrom> for OptionalNonZeroElGamalPubkey { - type Error = ProgramError; - fn try_from(p: Option) -> Result { - match p { - None => Ok(Self(ElGamalPubkey::default())), - Some(elgamal_pubkey) => { - if elgamal_pubkey == ElGamalPubkey::default() { - Err(ProgramError::InvalidArgument) - } else { - Ok(Self(elgamal_pubkey)) - } - } - } - } -} -impl From for Option { - fn from(p: OptionalNonZeroElGamalPubkey) -> Self { - if p.0 == ElGamalPubkey::default() { - None - } else { - Some(p.0) - } - } -} - -/// The standard `bool` is not a `Pod`, define a replacement that is -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodBool(u8); -impl From for PodBool { - fn from(b: bool) -> Self { - Self(if b { 1 } else { 0 }) - } -} -impl From<&PodBool> for bool { - fn from(b: &PodBool) -> Self { - b.0 != 0 - } -} - -impl From for bool { - fn from(b: PodBool) -> Self { - b.0 != 0 - } -} - -/// Simple macro for implementing conversion functions between Pod* ints and standard ints. -/// -/// The standard int types can cause alignment issues when placed in a `Pod`, -/// so these replacements are usable in all `Pod`s. -macro_rules! impl_int_conversion { - ($P:ty, $I:ty) => { - impl From<$I> for $P { - fn from(n: $I) -> Self { - Self(n.to_le_bytes()) - } - } - impl From<$P> for $I { - fn from(pod: $P) -> Self { - Self::from_le_bytes(pod.0) - } - } - }; -} - -/// `u16` type that can be used in `Pod`s -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU16([u8; 2]); -impl_int_conversion!(PodU16, u16); - -/// `i16` type that can be used in `Pod`s -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodI16([u8; 2]); -impl_int_conversion!(PodI16, i16); - -/// `u64` type that can be used in `Pod`s -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU64([u8; 8]); -impl_int_conversion!(PodU64, u64); - -/// `i64` type that can be used in `Pod`s -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodI64([u8; 8]); -impl_int_conversion!(PodI64, i64); - -/// On-chain size of a `Pod` type -pub const fn pod_get_packed_len() -> usize { - std::mem::size_of::() -} - -/// Convert a `Pod` into a slice (zero copy) -pub fn pod_bytes_of(t: &T) -> &[u8] { - bytemuck::bytes_of(t) -} - -/// Convert a slice into a `Pod` (zero copy) -pub fn pod_from_bytes(bytes: &[u8]) -> Result<&T, ProgramError> { - bytemuck::try_from_bytes(bytes).map_err(|_| ProgramError::InvalidArgument) -} - -/// Maybe convert a slice into a `Pod` (zero copy) -/// -/// Returns `None` if the slice is empty, but `Err` if all other lengths but `get_packed_len()` -/// This function exists primarily because `Option` is not a `Pod`. -pub fn pod_maybe_from_bytes(bytes: &[u8]) -> Result, ProgramError> { - if bytes.is_empty() { - Ok(None) - } else { - bytemuck::try_from_bytes(bytes) - .map(Some) - .map_err(|_| ProgramError::InvalidArgument) - } -} - -/// Convert a slice into a mutable `Pod` (zero copy) -pub fn pod_from_bytes_mut(bytes: &mut [u8]) -> Result<&mut T, ProgramError> { - bytemuck::try_from_bytes_mut(bytes).map_err(|_| ProgramError::InvalidArgument) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_pod_bool() { - assert!(pod_from_bytes::(&[]).is_err()); - assert!(pod_from_bytes::(&[0, 0]).is_err()); - - for i in 0..=u8::MAX { - assert_eq!(i != 0, bool::from(pod_from_bytes::(&[i]).unwrap())); - } - } - - #[test] - fn test_pod_u64() { - assert!(pod_from_bytes::(&[]).is_err()); - assert_eq!( - 1u64, - u64::from(*pod_from_bytes::(&[1, 0, 0, 0, 0, 0, 0, 0]).unwrap()) - ); - } - - #[test] - fn test_pod_option() { - assert_eq!( - Some(Pubkey::new_from_array([1; 32])), - Option::::from(*pod_from_bytes::(&[1; 32]).unwrap()) - ); - assert_eq!( - None, - Option::::from(*pod_from_bytes::(&[0; 32]).unwrap()) - ); - assert!(pod_from_bytes::(&[]).is_err()); - assert!(pod_from_bytes::(&[0; 1]).is_err()); - assert!(pod_from_bytes::(&[1; 1]).is_err()); - } -} diff --git a/token/program-2022/src/processor.rs b/token/program-2022/src/processor.rs index 5e2588c6e9f..4bcfb09b10c 100644 --- a/token/program-2022/src/processor.rs +++ b/token/program-2022/src/processor.rs @@ -11,6 +11,7 @@ use { }, cpi_guard::{self, in_cpi, CpiGuard}, default_account_state::{self, DefaultAccountState}, + group_pointer::{self, GroupPointer}, immutable_owner::ImmutableOwner, interest_bearing_mint::{self, InterestBearingConfig}, memo_transfer::{self, check_previous_sibling_instruction_is_memo, memo_required}, @@ -18,10 +19,11 @@ use { mint_close_authority::MintCloseAuthority, non_transferable::{NonTransferable, NonTransferableAccount}, permanent_delegate::{get_permanent_delegate, PermanentDelegate}, - reallocate, + reallocate, token_metadata, transfer_fee::{self, TransferFeeAmount, TransferFeeConfig}, transfer_hook::{self, TransferHook, TransferHookAccount}, - BaseStateWithExtensions, ExtensionType, StateWithExtensions, StateWithExtensionsMut, + AccountType, BaseStateWithExtensions, ExtensionType, StateWithExtensions, + StateWithExtensionsMut, }, instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, native_mint, @@ -40,6 +42,7 @@ use { system_instruction, system_program, sysvar::{rent::Rent, Sysvar}, }, + spl_token_metadata_interface::instruction::TokenMetadataInstruction, std::convert::{TryFrom, TryInto}, }; @@ -69,7 +72,7 @@ impl Processor { let mut mint = StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data)?; let extension_types = mint.get_extension_types()?; - if ExtensionType::try_get_account_len::(&extension_types)? != mint_data_len { + if ExtensionType::try_calculate_account_len::(&extension_types)? != mint_data_len { return Err(ProgramError::InvalidAccountData); } ExtensionType::check_for_invalid_mint_extension_combinations(&extension_types)?; @@ -154,7 +157,7 @@ impl Processor { } let required_extensions = Self::get_required_account_extensions_from_unpacked_mint(mint_info.owner, &mint)?; - if ExtensionType::try_get_account_len::(&required_extensions)? + if ExtensionType::try_calculate_account_len::(&required_extensions)? > new_account_info_data_len { return Err(ProgramError::InvalidAccountData); @@ -844,6 +847,19 @@ impl Processor { )?; extension.authority = new_authority.try_into()?; } + AuthorityType::GroupPointer => { + let extension = mint.get_extension_mut::()?; + let maybe_authority: Option = extension.authority.into(); + let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?; + Self::validate_owner( + program_id, + &authority, + authority_info, + authority_info_data_len, + account_info_iter.as_slice(), + )?; + extension.authority = new_authority.try_into()?; + } _ => { return Err(TokenError::AuthorityTypeNotSupported.into()); } @@ -1244,15 +1260,22 @@ impl Processor { accounts: &[AccountInfo], new_extension_types: Vec, ) -> ProgramResult { + if new_extension_types + .iter() + .any(|&t| t.get_account_type() != AccountType::Account) + { + return Err(TokenError::ExtensionTypeMismatch.into()); + } + let account_info_iter = &mut accounts.iter(); let mint_account_info = next_account_info(account_info_iter)?; let mut account_extensions = Self::get_required_account_extensions(mint_account_info)?; - // ExtensionType::try_get_account_len() dedupes types, so just a dumb concatenation is fine + // ExtensionType::try_calculate_account_len() dedupes types, so just a dumb concatenation is fine // here account_extensions.extend_from_slice(&new_extension_types); - let account_len = ExtensionType::try_get_account_len::(&account_extensions)?; + let account_len = ExtensionType::try_calculate_account_len::(&account_extensions)?; set_return_data(&account_len.to_le_bytes()); Ok(()) @@ -1457,188 +1480,209 @@ impl Processor { /// Processes an [Instruction](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { - let instruction = TokenInstruction::unpack(input)?; - - match instruction { - TokenInstruction::InitializeMint { - decimals, - mint_authority, - freeze_authority, - } => { - msg!("Instruction: InitializeMint"); - Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority) - } - TokenInstruction::InitializeMint2 { - decimals, - mint_authority, - freeze_authority, - } => { - msg!("Instruction: InitializeMint2"); - Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority) - } - TokenInstruction::InitializeAccount => { - msg!("Instruction: InitializeAccount"); - Self::process_initialize_account(accounts) - } - TokenInstruction::InitializeAccount2 { owner } => { - msg!("Instruction: InitializeAccount2"); - Self::process_initialize_account2(accounts, owner) - } - TokenInstruction::InitializeAccount3 { owner } => { - msg!("Instruction: InitializeAccount3"); - Self::process_initialize_account3(accounts, owner) - } - TokenInstruction::InitializeMultisig { m } => { - msg!("Instruction: InitializeMultisig"); - Self::process_initialize_multisig(accounts, m) - } - TokenInstruction::InitializeMultisig2 { m } => { - msg!("Instruction: InitializeMultisig2"); - Self::process_initialize_multisig2(accounts, m) - } - #[allow(deprecated)] - TokenInstruction::Transfer { amount } => { - msg!("Instruction: Transfer"); - Self::process_transfer(program_id, accounts, amount, None, None) - } - TokenInstruction::Approve { amount } => { - msg!("Instruction: Approve"); - Self::process_approve(program_id, accounts, amount, None) - } - TokenInstruction::Revoke => { - msg!("Instruction: Revoke"); - Self::process_revoke(program_id, accounts) - } - TokenInstruction::SetAuthority { - authority_type, - new_authority, - } => { - msg!("Instruction: SetAuthority"); - Self::process_set_authority(program_id, accounts, authority_type, new_authority) - } - TokenInstruction::MintTo { amount } => { - msg!("Instruction: MintTo"); - Self::process_mint_to(program_id, accounts, amount, None) - } - TokenInstruction::Burn { amount } => { - msg!("Instruction: Burn"); - Self::process_burn(program_id, accounts, amount, None) - } - TokenInstruction::CloseAccount => { - msg!("Instruction: CloseAccount"); - Self::process_close_account(program_id, accounts) - } - TokenInstruction::FreezeAccount => { - msg!("Instruction: FreezeAccount"); - Self::process_toggle_freeze_account(program_id, accounts, true) - } - TokenInstruction::ThawAccount => { - msg!("Instruction: ThawAccount"); - Self::process_toggle_freeze_account(program_id, accounts, false) - } - TokenInstruction::TransferChecked { amount, decimals } => { - msg!("Instruction: TransferChecked"); - Self::process_transfer(program_id, accounts, amount, Some(decimals), None) - } - TokenInstruction::ApproveChecked { amount, decimals } => { - msg!("Instruction: ApproveChecked"); - Self::process_approve(program_id, accounts, amount, Some(decimals)) - } - TokenInstruction::MintToChecked { amount, decimals } => { - msg!("Instruction: MintToChecked"); - Self::process_mint_to(program_id, accounts, amount, Some(decimals)) - } - TokenInstruction::BurnChecked { amount, decimals } => { - msg!("Instruction: BurnChecked"); - Self::process_burn(program_id, accounts, amount, Some(decimals)) - } - TokenInstruction::SyncNative => { - msg!("Instruction: SyncNative"); - Self::process_sync_native(accounts) - } - TokenInstruction::GetAccountDataSize { extension_types } => { - msg!("Instruction: GetAccountDataSize"); - Self::process_get_account_data_size(accounts, extension_types) - } - TokenInstruction::InitializeMintCloseAuthority { close_authority } => { - msg!("Instruction: InitializeMintCloseAuthority"); - Self::process_initialize_mint_close_authority(accounts, close_authority) - } - TokenInstruction::TransferFeeExtension(instruction) => { - transfer_fee::processor::process_instruction(program_id, accounts, instruction) - } - TokenInstruction::ConfidentialTransferExtension => { - confidential_transfer::processor::process_instruction( - program_id, - accounts, - &input[1..], - ) - } - TokenInstruction::DefaultAccountStateExtension => { - default_account_state::processor::process_instruction( - program_id, - accounts, - &input[1..], - ) - } - TokenInstruction::InitializeImmutableOwner => { - msg!("Instruction: InitializeImmutableOwner"); - Self::process_initialize_immutable_owner(accounts) - } - TokenInstruction::AmountToUiAmount { amount } => { - msg!("Instruction: AmountToUiAmount"); - Self::process_amount_to_ui_amount(accounts, amount) - } - TokenInstruction::UiAmountToAmount { ui_amount } => { - msg!("Instruction: UiAmountToAmount"); - Self::process_ui_amount_to_amount(accounts, ui_amount) - } - TokenInstruction::Reallocate { extension_types } => { - msg!("Instruction: Reallocate"); - reallocate::process_reallocate(program_id, accounts, extension_types) - } - TokenInstruction::MemoTransferExtension => { - memo_transfer::processor::process_instruction(program_id, accounts, &input[1..]) - } - TokenInstruction::CreateNativeMint => { - msg!("Instruction: CreateNativeMint"); - Self::process_create_native_mint(accounts) - } - TokenInstruction::InitializeNonTransferableMint => { - msg!("Instruction: InitializeNonTransferableMint"); - Self::process_initialize_non_transferable_mint(accounts) - } - TokenInstruction::InterestBearingMintExtension => { - interest_bearing_mint::processor::process_instruction( - program_id, - accounts, - &input[1..], - ) - } - TokenInstruction::CpiGuardExtension => { - cpi_guard::processor::process_instruction(program_id, accounts, &input[1..]) - } - TokenInstruction::InitializePermanentDelegate { delegate } => { - msg!("Instruction: InitializePermanentDelegate"); - Self::process_initialize_permanent_delegate(accounts, delegate) - } - TokenInstruction::TransferHookExtension => { - transfer_hook::processor::process_instruction(program_id, accounts, &input[1..]) - } - TokenInstruction::ConfidentialTransferFeeExtension => { - confidential_transfer_fee::processor::process_instruction( - program_id, - accounts, - &input[1..], - ) - } - TokenInstruction::WithdrawExcessLamports => { - msg!("Instruction: WithdrawExcessLamports"); - Self::process_withdraw_excess_lamports(program_id, accounts) - } - TokenInstruction::MetadataPointerExtension => { - metadata_pointer::processor::process_instruction(program_id, accounts, &input[1..]) + if let Ok(instruction) = TokenInstruction::unpack(input) { + match instruction { + TokenInstruction::InitializeMint { + decimals, + mint_authority, + freeze_authority, + } => { + msg!("Instruction: InitializeMint"); + Self::process_initialize_mint( + accounts, + decimals, + mint_authority, + freeze_authority, + ) + } + TokenInstruction::InitializeMint2 { + decimals, + mint_authority, + freeze_authority, + } => { + msg!("Instruction: InitializeMint2"); + Self::process_initialize_mint2( + accounts, + decimals, + mint_authority, + freeze_authority, + ) + } + TokenInstruction::InitializeAccount => { + msg!("Instruction: InitializeAccount"); + Self::process_initialize_account(accounts) + } + TokenInstruction::InitializeAccount2 { owner } => { + msg!("Instruction: InitializeAccount2"); + Self::process_initialize_account2(accounts, owner) + } + TokenInstruction::InitializeAccount3 { owner } => { + msg!("Instruction: InitializeAccount3"); + Self::process_initialize_account3(accounts, owner) + } + TokenInstruction::InitializeMultisig { m } => { + msg!("Instruction: InitializeMultisig"); + Self::process_initialize_multisig(accounts, m) + } + TokenInstruction::InitializeMultisig2 { m } => { + msg!("Instruction: InitializeMultisig2"); + Self::process_initialize_multisig2(accounts, m) + } + #[allow(deprecated)] + TokenInstruction::Transfer { amount } => { + msg!("Instruction: Transfer"); + Self::process_transfer(program_id, accounts, amount, None, None) + } + TokenInstruction::Approve { amount } => { + msg!("Instruction: Approve"); + Self::process_approve(program_id, accounts, amount, None) + } + TokenInstruction::Revoke => { + msg!("Instruction: Revoke"); + Self::process_revoke(program_id, accounts) + } + TokenInstruction::SetAuthority { + authority_type, + new_authority, + } => { + msg!("Instruction: SetAuthority"); + Self::process_set_authority(program_id, accounts, authority_type, new_authority) + } + TokenInstruction::MintTo { amount } => { + msg!("Instruction: MintTo"); + Self::process_mint_to(program_id, accounts, amount, None) + } + TokenInstruction::Burn { amount } => { + msg!("Instruction: Burn"); + Self::process_burn(program_id, accounts, amount, None) + } + TokenInstruction::CloseAccount => { + msg!("Instruction: CloseAccount"); + Self::process_close_account(program_id, accounts) + } + TokenInstruction::FreezeAccount => { + msg!("Instruction: FreezeAccount"); + Self::process_toggle_freeze_account(program_id, accounts, true) + } + TokenInstruction::ThawAccount => { + msg!("Instruction: ThawAccount"); + Self::process_toggle_freeze_account(program_id, accounts, false) + } + TokenInstruction::TransferChecked { amount, decimals } => { + msg!("Instruction: TransferChecked"); + Self::process_transfer(program_id, accounts, amount, Some(decimals), None) + } + TokenInstruction::ApproveChecked { amount, decimals } => { + msg!("Instruction: ApproveChecked"); + Self::process_approve(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::MintToChecked { amount, decimals } => { + msg!("Instruction: MintToChecked"); + Self::process_mint_to(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::BurnChecked { amount, decimals } => { + msg!("Instruction: BurnChecked"); + Self::process_burn(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::SyncNative => { + msg!("Instruction: SyncNative"); + Self::process_sync_native(accounts) + } + TokenInstruction::GetAccountDataSize { extension_types } => { + msg!("Instruction: GetAccountDataSize"); + Self::process_get_account_data_size(accounts, extension_types) + } + TokenInstruction::InitializeMintCloseAuthority { close_authority } => { + msg!("Instruction: InitializeMintCloseAuthority"); + Self::process_initialize_mint_close_authority(accounts, close_authority) + } + TokenInstruction::TransferFeeExtension(instruction) => { + transfer_fee::processor::process_instruction(program_id, accounts, instruction) + } + TokenInstruction::ConfidentialTransferExtension => { + confidential_transfer::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } + TokenInstruction::DefaultAccountStateExtension => { + default_account_state::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } + TokenInstruction::InitializeImmutableOwner => { + msg!("Instruction: InitializeImmutableOwner"); + Self::process_initialize_immutable_owner(accounts) + } + TokenInstruction::AmountToUiAmount { amount } => { + msg!("Instruction: AmountToUiAmount"); + Self::process_amount_to_ui_amount(accounts, amount) + } + TokenInstruction::UiAmountToAmount { ui_amount } => { + msg!("Instruction: UiAmountToAmount"); + Self::process_ui_amount_to_amount(accounts, ui_amount) + } + TokenInstruction::Reallocate { extension_types } => { + msg!("Instruction: Reallocate"); + reallocate::process_reallocate(program_id, accounts, extension_types) + } + TokenInstruction::MemoTransferExtension => { + memo_transfer::processor::process_instruction(program_id, accounts, &input[1..]) + } + TokenInstruction::CreateNativeMint => { + msg!("Instruction: CreateNativeMint"); + Self::process_create_native_mint(accounts) + } + TokenInstruction::InitializeNonTransferableMint => { + msg!("Instruction: InitializeNonTransferableMint"); + Self::process_initialize_non_transferable_mint(accounts) + } + TokenInstruction::InterestBearingMintExtension => { + interest_bearing_mint::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } + TokenInstruction::CpiGuardExtension => { + cpi_guard::processor::process_instruction(program_id, accounts, &input[1..]) + } + TokenInstruction::InitializePermanentDelegate { delegate } => { + msg!("Instruction: InitializePermanentDelegate"); + Self::process_initialize_permanent_delegate(accounts, delegate) + } + TokenInstruction::TransferHookExtension => { + transfer_hook::processor::process_instruction(program_id, accounts, &input[1..]) + } + TokenInstruction::ConfidentialTransferFeeExtension => { + confidential_transfer_fee::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } + TokenInstruction::WithdrawExcessLamports => { + msg!("Instruction: WithdrawExcessLamports"); + Self::process_withdraw_excess_lamports(program_id, accounts) + } + TokenInstruction::MetadataPointerExtension => { + metadata_pointer::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } + TokenInstruction::GroupPointerExtension => { + group_pointer::processor::process_instruction(program_id, accounts, &input[1..]) + } } + } else if let Ok(instruction) = TokenMetadataInstruction::unpack(input) { + token_metadata::processor::process_instruction(program_id, accounts, instruction) + } else { + Err(TokenError::InvalidInstruction.into()) } } @@ -1868,9 +1912,11 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(3)")] - fn test_error_unwrap() { - Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); + fn test_error_as_custom() { + assert_eq!( + return_token_error_as_program_error(), + ProgramError::Custom(3) + ); } #[test] @@ -4503,7 +4549,7 @@ mod tests { let account_key = Pubkey::new_unique(); let account_len = - ExtensionType::try_get_account_len::(&[ExtensionType::ImmutableOwner]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::ImmutableOwner]) .unwrap(); let mut account_account = SolanaAccount::new( Rent::default().minimum_balance(account_len), @@ -7384,7 +7430,7 @@ mod tests { .unwrap(); set_expected_data( - ExtensionType::try_get_account_len::(&[]) + ExtensionType::try_calculate_account_len::(&[]) .unwrap() .to_le_bytes() .to_vec(), @@ -7396,10 +7442,12 @@ mod tests { .unwrap(); set_expected_data( - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeAmount]) - .unwrap() - .to_le_bytes() - .to_vec(), + ExtensionType::try_calculate_account_len::(&[ + ExtensionType::TransferFeeAmount, + ]) + .unwrap() + .to_le_bytes() + .to_vec(), ); do_process_instruction( get_account_data_size( @@ -7418,7 +7466,7 @@ mod tests { // Native mint let mut mint_account = native_mint(); set_expected_data( - ExtensionType::try_get_account_len::(&[]) + ExtensionType::try_calculate_account_len::(&[]) .unwrap() .to_le_bytes() .to_vec(), @@ -7431,7 +7479,7 @@ mod tests { // Extended mint let mint_len = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeConfig]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferFeeConfig]) .unwrap(); let mut extended_mint_account = SolanaAccount::new( Rent::default().minimum_balance(mint_len), @@ -7452,10 +7500,12 @@ mod tests { .unwrap(); set_expected_data( - ExtensionType::try_get_account_len::(&[ExtensionType::TransferFeeAmount]) - .unwrap() - .to_le_bytes() - .to_vec(), + ExtensionType::try_calculate_account_len::(&[ + ExtensionType::TransferFeeAmount, + ]) + .unwrap() + .to_le_bytes() + .to_vec(), ); do_process_instruction( get_account_data_size(&program_id, &mint_key, &[]).unwrap(), @@ -7524,6 +7574,31 @@ mod tests { ), Err(ProgramError::IncorrectProgramId) ); + + // Invalid Extension Type for mint and uninitialized account + assert_eq!( + do_process_instruction( + get_account_data_size(&program_id, &mint_key, &[ExtensionType::Uninitialized]) + .unwrap(), + vec![&mut mint_account], + ), + Err(TokenError::ExtensionTypeMismatch.into()) + ); + assert_eq!( + do_process_instruction( + get_account_data_size( + &program_id, + &mint_key, + &[ + ExtensionType::MemoTransfer, + ExtensionType::MintCloseAuthority + ] + ) + .unwrap(), + vec![&mut mint_account], + ), + Err(TokenError::ExtensionTypeMismatch.into()) + ); } #[test] diff --git a/token/program-2022/src/proof.rs b/token/program-2022/src/proof.rs index 486690bf749..ac72c5020ec 100644 --- a/token/program-2022/src/proof.rs +++ b/token/program-2022/src/proof.rs @@ -2,11 +2,12 @@ use { bytemuck::Pod, - solana_program::{instruction::Instruction, msg, program_error::ProgramError}, + solana_program::{instruction::Instruction, msg, program_error::ProgramError, pubkey::Pubkey}, solana_zk_token_sdk::{ instruction::ZkProofData, zk_token_proof_instruction::ProofInstruction, zk_token_proof_program, }, + std::num::NonZeroI8, }; /// Decodes the proof context data associated with a zero-knowledge proof instruction. @@ -25,3 +26,22 @@ pub fn decode_proof_instruction_context, U: Pod>( .map(ZkProofData::context_data) .ok_or(ProgramError::InvalidInstructionData) } + +/// A proof location type meant to be used for arguments to instruction constructors. +#[derive(Clone, Copy)] +pub enum ProofLocation<'a, T> { + /// The proof is included in the same transaction of a corresponding token-2022 instruction. + InstructionOffset(NonZeroI8, &'a T), + /// The proof is pre-verified into a context state account. + ContextStateAccount(&'a Pubkey), +} + +/// Instruction options for when using split context state accounts +#[derive(Clone, Copy)] +pub struct SplitContextStateAccountsConfig { + /// If true, execute no op when an associated split proof context state account is not + /// initialized. Otherwise, fail on an uninitialized context state account. + pub no_op_on_uninitialized_split_context_state: bool, + /// Close associated context states after a complete execution of the transfer instruction. + pub close_split_context_state_on_execution: bool, +} diff --git a/token/program-2022/src/serialization.rs b/token/program-2022/src/serialization.rs index 9b2d269748c..850ec1cb8c1 100644 --- a/token/program-2022/src/serialization.rs +++ b/token/program-2022/src/serialization.rs @@ -1,4 +1,25 @@ -//! serialization module +//! serialization module - contains helpers for serde types from other crates, deserialization visitors + +use { + base64::{prelude::BASE64_STANDARD, Engine}, + serde::de::Error, +}; + +/// helper function to convert base64 encoded string into a bytes array +fn base64_to_bytes(v: &str) -> Result<[u8; N], E> { + let bytes = BASE64_STANDARD.decode(v).map_err(Error::custom)?; + + if bytes.len() != N { + return Err(Error::custom(format!( + "Length of base64 decoded bytes is not {}", + N + ))); + } + + let mut array = [0; N]; + array.copy_from_slice(&bytes[0..N]); + Ok(array) +} /// helper function to ser/deser COption wrapped values pub mod coption_fromstr { @@ -76,3 +97,148 @@ pub mod coption_fromstr { }) } } + +/// helper to ser/deser AeCiphertext values +pub mod aeciphertext_fromstr { + use { + serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }, + solana_zk_token_sdk::zk_token_elgamal::pod::AeCiphertext, + std::fmt, + }; + + const AE_CIPHERTEXT_LEN: usize = 36; + + /// serialize AeCiphertext values supporting Display trait + pub fn serialize(x: &AeCiphertext, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&x.to_string()) + } + + struct AeCiphertextVisitor; + + impl<'de> Visitor<'de> for AeCiphertextVisitor { + type Value = AeCiphertext; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a FromStr type") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let array = super::base64_to_bytes::(v)?; + Ok(AeCiphertext(array)) + } + } + + /// deserialize AeCiphertext values from str + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + d.deserialize_str(AeCiphertextVisitor) + } +} + +/// helper to ser/deser pod::ElGamalPubkey values +pub mod elgamalpubkey_fromstr { + use { + serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }, + solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + std::fmt, + }; + + const ELGAMAL_PUBKEY_LEN: usize = 32; + + /// serialize ElGamalPubkey values supporting Display trait + pub fn serialize(x: &ElGamalPubkey, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&x.to_string()) + } + + struct ElGamalPubkeyVisitor; + + impl<'de> Visitor<'de> for ElGamalPubkeyVisitor { + type Value = ElGamalPubkey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a FromStr type") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let array = super::base64_to_bytes::(v)?; + Ok(ElGamalPubkey(array)) + } + } + + /// deserialize ElGamalPubkey values from str + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + d.deserialize_str(ElGamalPubkeyVisitor) + } +} + +/// helper to ser/deser pod::DecryptHandle values +pub mod decrypthandle_fromstr { + use { + base64::{prelude::BASE64_STANDARD, Engine}, + serde::{ + de::{Error, Visitor}, + Deserializer, Serializer, + }, + solana_zk_token_sdk::zk_token_elgamal::pod::DecryptHandle, + std::fmt, + }; + + const DECRYPT_HANDLE_LEN: usize = 32; + + /// Serialize a decrypt handle as a base64 string + pub fn serialize(x: &DecryptHandle, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&BASE64_STANDARD.encode(x.0)) + } + + struct DecryptHandleVisitor; + + impl<'de> Visitor<'de> for DecryptHandleVisitor { + type Value = DecryptHandle; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a FromStr type") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + let array = super::base64_to_bytes::(v)?; + Ok(DecryptHandle(array)) + } + } + + /// Deserialize a DecryptHandle from a base64 string + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + d.deserialize_str(DecryptHandleVisitor) + } +} diff --git a/token/program-2022/tests/serialization.rs b/token/program-2022/tests/serialization.rs index 46051e9ac5c..da192afc97e 100644 --- a/token/program-2022/tests/serialization.rs +++ b/token/program-2022/tests/serialization.rs @@ -1,12 +1,19 @@ #![cfg(feature = "serde-traits")] use { - solana_program::program_option::COption, solana_sdk::pubkey::Pubkey, - spl_token_2022::instruction, std::str::FromStr, + solana_program::program_option::COption, + solana_sdk::pubkey::Pubkey, + spl_pod::optional_keys::{OptionalNonZeroElGamalPubkey, OptionalNonZeroPubkey}, + spl_token_2022::{ + extension::confidential_transfer, + instruction, + solana_zk_token_sdk::zk_token_elgamal::pod::{AeCiphertext, ElGamalPubkey}, + }, + std::str::FromStr, }; #[test] -fn token_program_serde() { +fn serde_instruction_coption_pubkey() { let inst = instruction::TokenInstruction::InitializeMint2 { decimals: 0, mint_authority: Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap(), @@ -16,13 +23,13 @@ fn token_program_serde() { }; let serialized = serde_json::to_string(&inst).unwrap(); - assert_eq!(&serialized, "{\"InitializeMint2\":{\"decimals\":0,\"mint_authority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\",\"freeze_authority\":\"8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh\"}}"); + assert_eq!(&serialized, "{\"initializeMint2\":{\"decimals\":0,\"mintAuthority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\",\"freezeAuthority\":\"8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh\"}}"); serde_json::from_str::(&serialized).unwrap(); } #[test] -fn token_program_serde_with_none() { +fn serde_instruction_coption_pubkey_with_none() { let inst = instruction::TokenInstruction::InitializeMintCloseAuthority { close_authority: COption::None, }; @@ -30,8 +37,131 @@ fn token_program_serde_with_none() { let serialized = serde_json::to_string(&inst).unwrap(); assert_eq!( &serialized, - "{\"InitializeMintCloseAuthority\":{\"close_authority\":null}}" + "{\"initializeMintCloseAuthority\":{\"closeAuthority\":null}}" ); serde_json::from_str::(&serialized).unwrap(); } + +#[test] +fn serde_instruction_optional_nonzero_pubkeys_podbool() { + // tests serde of ix containing OptionalNonZeroPubkey, PodBool and OptionalNonZeroElGamalPubkey + let authority_option: Option = + Some(Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap()); + let authority: OptionalNonZeroPubkey = authority_option.try_into().unwrap(); + + let elgamal_pubkey_pod_option: Option = Some(ElGamalPubkey([ + 162, 23, 108, 36, 130, 143, 18, 219, 196, 134, 242, 145, 179, 49, 229, 193, 74, 64, 3, 158, + 68, 235, 124, 88, 247, 144, 164, 254, 228, 12, 173, 85, + ])); + let auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey = + elgamal_pubkey_pod_option.try_into().unwrap(); + + let inst = confidential_transfer::instruction::InitializeMintData { + authority, + auto_approve_new_accounts: false.into(), + auditor_elgamal_pubkey, + }; + + let serialized = serde_json::to_string(&inst).unwrap(); + let serialized_expected = &format!("{{\"authority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\",\"autoApproveNewAccounts\":false,\"auditorElgamalPubkey\":\"ohdsJIKPEtvEhvKRszHlwUpAA55E63xY95Ck/uQMrVU=\"}}"); + assert_eq!(&serialized, serialized_expected); + + let deserialized = + serde_json::from_str::(&serialized) + .unwrap(); + assert_eq!(inst, deserialized); +} + +#[test] +fn serde_instruction_optional_nonzero_pubkeys_podbool_with_none() { + // tests serde of ix containing OptionalNonZeroPubkey, PodBool and OptionalNonZeroElGamalPubkey + // with null values + let authority: OptionalNonZeroPubkey = None.try_into().unwrap(); + + let auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey = + OptionalNonZeroElGamalPubkey::default(); + + let inst = confidential_transfer::instruction::InitializeMintData { + authority, + auto_approve_new_accounts: false.into(), + auditor_elgamal_pubkey, + }; + + let serialized = serde_json::to_string(&inst).unwrap(); + let serialized_expected = &format!( + "{{\"authority\":null,\"autoApproveNewAccounts\":false,\"auditorElgamalPubkey\":null}}" + ); + assert_eq!(&serialized, serialized_expected); + + let deserialized = + serde_json::from_str::( + &serialized_expected, + ) + .unwrap(); + assert_eq!(inst, deserialized); +} + +#[test] +fn serde_instruction_decryptable_balance_podu64() { + let decryptable_zero_balance = AeCiphertext([ + 56, 22, 102, 48, 112, 106, 58, 25, 25, 244, 194, 217, 73, 137, 73, 38, 24, 26, 36, 25, 235, + 234, 68, 181, 11, 82, 170, 163, 89, 205, 113, 160, 55, 16, 35, 151, + ]); + + let inst = confidential_transfer::instruction::ConfigureAccountInstructionData { + decryptable_zero_balance, + maximum_pending_balance_credit_counter: 1099.into(), + proof_instruction_offset: 100, + }; + + let serialized = serde_json::to_string(&inst).unwrap(); + let serialized_expected = &format!("{{\"decryptableZeroBalance\":\"OBZmMHBqOhkZ9MLZSYlJJhgaJBnr6kS1C1Kqo1nNcaA3ECOX\",\"maximumPendingBalanceCreditCounter\":1099,\"proofInstructionOffset\":100}}"); + assert_eq!(&serialized, serialized_expected); + + let deserialized = serde_json::from_str::< + confidential_transfer::instruction::ConfigureAccountInstructionData, + >(&serialized_expected) + .unwrap(); + assert_eq!(inst, deserialized); +} + +#[test] +fn serde_instruction_elgamal_pubkey() { + use spl_token_2022::extension::confidential_transfer_fee::instruction::InitializeConfidentialTransferFeeConfigData; + + let withdraw_withheld_authority_elgamal_pubkey = ElGamalPubkey([ + 162, 23, 108, 36, 130, 143, 18, 219, 196, 134, 242, 145, 179, 49, 229, 193, 74, 64, 3, 158, + 68, 235, 124, 88, 247, 144, 164, 254, 228, 12, 173, 85, + ]); + + let inst = InitializeConfidentialTransferFeeConfigData { + authority: OptionalNonZeroPubkey::default(), + withdraw_withheld_authority_elgamal_pubkey, + }; + + let serialized = serde_json::to_string(&inst).unwrap(); + let serialized_expected = "{\"authority\":null,\"withdrawWithheldAuthorityElgamalPubkey\":\"ohdsJIKPEtvEhvKRszHlwUpAA55E63xY95Ck/uQMrVU=\"}"; + assert_eq!(&serialized, serialized_expected); + + let deserialized = + serde_json::from_str::(&serialized_expected) + .unwrap(); + assert_eq!(inst, deserialized); +} + +#[test] +fn serde_instruction_basis_points() { + use spl_token_2022::extension::interest_bearing_mint::instruction::InitializeInstructionData; + + let inst = InitializeInstructionData { + rate_authority: OptionalNonZeroPubkey::default(), + rate: 127.into(), + }; + + let serialized = serde_json::to_string(&inst).unwrap(); + let serialized_expected = "{\"rateAuthority\":null,\"rate\":127}"; + assert_eq!(&serialized, serialized_expected); + + serde_json::from_str::(&serialized_expected).unwrap(); +} diff --git a/token/program/Cargo.toml b/token/program/Cargo.toml index ea22d013c3b..17c1b4d7b40 100644 --- a/token/program/Cargo.toml +++ b/token/program/Cargo.toml @@ -14,19 +14,19 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.13.1" +bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" -solana-program = "1.16.1" +num_enum = "0.7.1" +solana-program = "1.17.2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.2" +proptest = "1.3" serial_test = "2.0.0" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token/program/src/lib.rs b/token/program/src/lib.rs index e449506d49e..1eb3855df90 100644 --- a/token/program/src/lib.rs +++ b/token/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/token/program/src/processor.rs b/token/program/src/processor.rs index 7056f2e707e..6c9b0f4fef0 100644 --- a/token/program/src/processor.rs +++ b/token/program/src/processor.rs @@ -1147,9 +1147,11 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(3)")] - fn test_error_unwrap() { - Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); + fn test_error_as_custom() { + assert_eq!( + return_token_error_as_program_error(), + ProgramError::Custom(3) + ); } #[test] diff --git a/token/transfer-hook-example/src/state.rs b/token/transfer-hook-example/src/state.rs deleted file mode 100644 index 1bac50ea1a4..00000000000 --- a/token/transfer-hook-example/src/state.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! State helpers for working with the example program - -use { - solana_program::{instruction::AccountMeta, program_error::ProgramError}, - spl_tlv_account_resolution::state::ExtraAccountMetas, - spl_transfer_hook_interface::instruction::ExecuteInstruction, -}; - -/// Generate example data to be used directly in an account for testing -pub fn example_data(account_metas: &[AccountMeta]) -> Result, ProgramError> { - let account_size = ExtraAccountMetas::size_of(account_metas.len())?; - let mut data = vec![0; account_size]; - ExtraAccountMetas::init_with_account_metas::(&mut data, account_metas)?; - Ok(data) -} diff --git a/token/transfer-hook-interface/Cargo.toml b/token/transfer-hook-interface/Cargo.toml deleted file mode 100644 index 593d4e013e9..00000000000 --- a/token/transfer-hook-interface/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "spl-transfer-hook-interface" -version = "0.1.0" -description = "Solana Program Library Transfer Hook Interface" -authors = ["Solana Labs Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2021" - -[dependencies] -arrayref = "0.3.7" -bytemuck = { version = "1.13.1", features = ["derive"] } -num-derive = "0.4" -num-traits = "0.2" -num_enum = "0.6.1" -solana-program = "1.16.1" -spl-discriminator = { version = "0.1.0" , path = "../../libraries/discriminator" } -spl-tlv-account-resolution = { version = "0.2.0" , path = "../../libraries/tlv-account-resolution" } -spl-type-length-value = { version = "0.2.0" , path = "../../libraries/type-length-value" } -thiserror = "1.0" - -[lib] -crate-type = ["cdylib", "lib"] - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] diff --git a/token/transfer-hook-interface/README.md b/token/transfer-hook-interface/README.md deleted file mode 100644 index 6c9887157a7..00000000000 --- a/token/transfer-hook-interface/README.md +++ /dev/null @@ -1,106 +0,0 @@ -## Transfer-Hook Interface - -### Example program - -Here is an example program that only implements the required "execute" instruction, -assuming that the proper account data is already written to the appropriate -program-derived address defined by the interface. - -```rust -use { - solana_program::{entrypoint::ProgramResult, program_error::ProgramError}, - spl_tlv_account_resolution::state::ExtraAccountMetas, - spl_transfer_hook_interface::instruction::{ExecuteInstruction, TransferHookInstruction}, - spl_type_length_value::state::TlvStateBorrowed, -}; -pub fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - input: &[u8], -) -> ProgramResult { - let instruction = TransferHookInstruction::unpack(input)?; - let _amount = match instruction { - TransferHookInstruction::Execute { amount } => amount, - _ => return Err(ProgramError::InvalidInstructionData), - }; - let account_info_iter = &mut accounts.iter(); - - // Pull out the accounts in order, none are validated in this test program - let _source_account_info = next_account_info(account_info_iter)?; - let mint_info = next_account_info(account_info_iter)?; - let _destination_account_info = next_account_info(account_info_iter)?; - let _authority_info = next_account_info(account_info_iter)?; - let extra_account_metas_info = next_account_info(account_info_iter)?; - - // Only check that the correct pda and account are provided - let expected_validation_address = get_extra_account_metas_address(mint_info.key, program_id); - if expected_validation_address != *extra_account_metas_info.key { - return Err(ProgramError::InvalidSeeds); - } - - // Get the extra account metas from the account data - let data = extra_account_metas_info.try_borrow_data()?; - let state = TlvStateBorrowed::unpack(&data).unwrap(); - let extra_account_metas = - ExtraAccountMetas::unpack_with_tlv_state::(&state)?; - - // If incorrect number of accounts is provided, error - let extra_account_infos = account_info_iter.as_slice(); - let account_metas = extra_account_metas.data(); - if extra_account_infos.len() != account_metas.len() { - return Err(ProgramError::InvalidInstructionData); - } - - // Let's require that they're provided in the correct order - for (i, account_info) in extra_account_infos.iter().enumerate() { - if &account_metas[i] != account_info { - return Err(ProgramError::InvalidInstructionData); - } - } - - Ok(()) -} -``` - -### Motivation - -Token creators may need more control over transfers of their token. The most -prominent use case revolves around NFT royalties. Whenever a token is moved, -the creator should be entitled to royalties, but due to the design of the current -token program, it's impossible to stop a transfer at the protocol level. - -Current solutions typically resort to perpetually freezing tokens, which requires -a whole proxy layer to interact with the token. Wallets and marketplaces need -to be aware of the proxy layer in order to properly use the token. - -Worse still, different royalty systems have different proxy layers for using -their token. All in all, these systems harm composability and make development -harder. - -### Solution - -To give more flexibility to token creators and improve the situation for everyone, -`spl-transfer-hook-interface` introduces the concept of an interface integrated -with `spl-token-2022`. A token creator must develop and deploy a program that -implements the interface and then configure their token mint to use their program. - -During transfer, token-2022 calls into the program with the accounts specified -at a well-defined program-derived address for that mint and program id. This -call happens after all other transfer logic, so the accounts reflect the *end* -state of the transfer. - -A developer must implement the `Execute` instruction, and the -`InitializeExtraAccountMetas` instruction to write the required additional account -pubkeys into the program-derived address defined by the mint and program id. - -Side note: it's technically not required to implement `InitializeExtraAccountMetas` -at that instruction descriminator. Your program may implement multiple interfaces, -so any other instruction in your program can create the account at the program-derived -address! - -This library provides offchain and onchain helpers for resolving the additional -accounts required. See -[invoke.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface/src/invoke.rs) -for usage on-chain, and -[offchain.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface/src/offchain.rs) -for fetching the additional required account metas. diff --git a/token/transfer-hook-interface/src/error.rs b/token/transfer-hook-interface/src/error.rs deleted file mode 100644 index c387b8b15b3..00000000000 --- a/token/transfer-hook-interface/src/error.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Error types - -use { - num_derive::FromPrimitive, - solana_program::{ - decode_error::DecodeError, - msg, - program_error::{PrintProgramError, ProgramError}, - }, - thiserror::Error, -}; - -/// Errors that may be returned by the interface. -#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] -pub enum TransferHookError { - /// Incorrect account provided - #[error("Incorrect account provided")] - IncorrectAccount, - /// Mint has no mint authority - #[error("Mint has no mint authority")] - MintHasNoMintAuthority, - /// Incorrect mint authority has signed the instruction - #[error("Incorrect mint authority has signed the instruction")] - IncorrectMintAuthority, - /// Program called outside of a token transfer - #[error("Program called outside of a token transfer")] - ProgramCalledOutsideOfTransfer, -} -impl From for ProgramError { - fn from(e: TransferHookError) -> Self { - ProgramError::Custom(e as u32) - } -} -impl DecodeError for TransferHookError { - fn type_of() -> &'static str { - "TransferHookError" - } -} - -impl PrintProgramError for TransferHookError { - fn print(&self) - where - E: 'static - + std::error::Error - + DecodeError - + PrintProgramError - + num_traits::FromPrimitive, - { - match self { - Self::IncorrectAccount => msg!("Incorrect account provided"), - Self::MintHasNoMintAuthority => msg!("Mint has no mint authority"), - Self::IncorrectMintAuthority => { - msg!("Incorrect mint authority has signed the instruction") - } - Self::ProgramCalledOutsideOfTransfer => { - msg!("Program called outside of a token transfer") - } - } - } -} diff --git a/token/transfer-hook/cli/Cargo.toml b/token/transfer-hook/cli/Cargo.toml new file mode 100644 index 00000000000..7bb6efd4fd5 --- /dev/null +++ b/token/transfer-hook/cli/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["Solana Labs Maintainers "] +description = "SPL-Token Command-line Utility" +edition = "2021" +homepage = "https://spl.solana.com/token" +license = "Apache-2.0" +name = "spl-transfer-hook-cli" +repository = "https://github.com/solana-labs/solana-program-library" +version = "0.1.0" + +[dependencies] +clap = { version = "3", features = ["cargo"] } +futures-util = "0.3.19" +solana-clap-v3-utils = "=1.17.2" +solana-cli-config = "=1.17.2" +solana-client = "=1.17.2" +solana-logger = "=1.17.2" +solana-remote-wallet = "=1.17.2" +solana-sdk = "=1.17.2" +spl-transfer-hook-interface = { version = "0.3", path = "../interface" } +spl-tlv-account-resolution = { version = "0.4" , path = "../../../libraries/tlv-account-resolution" } +strum = "0.25" +strum_macros = "0.25" +tokio = { version = "1", features = ["full"] } + +[dev-dependencies] +solana-test-validator = "=1.17.2" +spl-token-2022 = { version = "0.9", path = "../../program-2022", features = ["no-entrypoint"] } +spl-token-client = { version = "0.8", path = "../../client" } + +[[bin]] +name = "spl-transfer-hook" +path = "src/main.rs" diff --git a/token/transfer-hook/cli/src/main.rs b/token/transfer-hook/cli/src/main.rs new file mode 100644 index 00000000000..63d11f8903f --- /dev/null +++ b/token/transfer-hook/cli/src/main.rs @@ -0,0 +1,407 @@ +use { + clap::{crate_description, crate_name, crate_version, Arg, Command}, + solana_clap_v3_utils::{ + input_parsers::{parse_url_or_moniker, pubkey_of_signer}, + input_validators::{is_valid_pubkey, is_valid_signer, normalize_to_url_if_moniker}, + keypair::DefaultSigner, + }, + solana_client::nonblocking::rpc_client::RpcClient, + solana_remote_wallet::remote_wallet::RemoteWalletManager, + solana_sdk::{ + commitment_config::CommitmentConfig, + instruction::AccountMeta, + pubkey::Pubkey, + signature::{Signature, Signer}, + system_instruction, system_program, + transaction::Transaction, + }, + spl_tlv_account_resolution::state::ExtraAccountMetaList, + spl_transfer_hook_interface::{ + get_extra_account_metas_address, instruction::initialize_extra_account_meta_list, + }, + std::{fmt, process::exit, rc::Rc, str::FromStr}, + strum_macros::{EnumString, IntoStaticStr}, +}; + +#[derive(Debug, Clone, Copy, PartialEq, EnumString, IntoStaticStr)] +#[strum(serialize_all = "kebab-case")] +pub enum AccountMetaRole { + Readonly, + Writable, + ReadonlySigner, + WritableSigner, +} +impl fmt::Display for AccountMetaRole { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} +fn parse_transfer_hook_account(arg: &str) -> Result { + match arg.split(':').collect::>().as_slice() { + [address, role] => { + let address = Pubkey::from_str(address).map_err(|e| format!("{e}"))?; + let meta = match AccountMetaRole::from_str(role).map_err(|e| format!("{e}"))? { + AccountMetaRole::Readonly => AccountMeta::new_readonly(address, false), + AccountMetaRole::Writable => AccountMeta::new(address, false), + AccountMetaRole::ReadonlySigner => AccountMeta::new_readonly(address, true), + AccountMetaRole::WritableSigner => AccountMeta::new(address, true), + }; + Ok(meta) + } + _ => Err("Transfer hook account must be present as
:".to_string()), + } +} + +fn clap_is_valid_pubkey(arg: &str) -> Result<(), String> { + is_valid_pubkey(arg) +} + +struct Config { + commitment_config: CommitmentConfig, + default_signer: Box, + json_rpc_url: String, + verbose: bool, +} + +async fn process_create_extra_account_metas( + rpc_client: &RpcClient, + program_id: &Pubkey, + token: &Pubkey, + transfer_hook_accounts: Vec, + mint_authority: &dyn Signer, + payer: &dyn Signer, +) -> Result> { + let extra_account_metas_address = get_extra_account_metas_address(token, program_id); + let extra_account_metas = transfer_hook_accounts + .into_iter() + .map(|v| v.into()) + .collect::>(); + + let length = extra_account_metas.len(); + let account_size = ExtraAccountMetaList::size_of(length)?; + let required_lamports = rpc_client + .get_minimum_balance_for_rent_exemption(account_size) + .await + .map_err(|err| format!("error: unable to fetch rent-exemption: {err}"))?; + let extra_account_metas_account = rpc_client.get_account(&extra_account_metas_address).await; + if let Ok(account) = &extra_account_metas_account { + if account.owner != system_program::id() { + return Err(format!("error: extra account metas for mint {token} and program {program_id} already exists").into()); + } + } + let current_lamports = extra_account_metas_account.map(|a| a.lamports).unwrap_or(0); + let transfer_lamports = required_lamports.saturating_sub(current_lamports); + + let mut ixs = vec![]; + if transfer_lamports > 0 { + ixs.push(system_instruction::transfer( + &payer.pubkey(), + &extra_account_metas_address, + transfer_lamports, + )); + } + ixs.push(initialize_extra_account_meta_list( + program_id, + &extra_account_metas_address, + token, + &mint_authority.pubkey(), + &extra_account_metas, + )); + + let mut transaction = Transaction::new_with_payer(&ixs, Some(&payer.pubkey())); + let blockhash = rpc_client + .get_latest_blockhash() + .await + .map_err(|err| format!("error: unable to get latest blockhash: {err}"))?; + let mut signers = vec![payer]; + if payer.pubkey() != mint_authority.pubkey() { + signers.push(mint_authority); + } + transaction + .try_sign(&signers, blockhash) + .map_err(|err| format!("error: failed to sign transaction: {err}"))?; + + rpc_client + .send_and_confirm_transaction_with_spinner(&transaction) + .await + .map_err(|err| format!("error: send transaction: {err}").into()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let app_matches = Command::new(crate_name!()) + .about(crate_description!()) + .version(crate_version!()) + .subcommand_required(true) + .arg_required_else_help(true) + .arg({ + let arg = Arg::new("config_file") + .short('C') + .long("config") + .value_name("PATH") + .takes_value(true) + .global(true) + .help("Configuration file to use"); + if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE { + arg.default_value(config_file) + } else { + arg + } + }) + .arg( + Arg::new("fee_payer") + .long("fee-payer") + .value_name("KEYPAIR") + .validator(|s| is_valid_signer(s)) + .takes_value(true) + .global(true) + .help("Filepath or URL to a keypair to pay transaction fee [default: client keypair]"), + ) + .arg( + Arg::new("verbose") + .long("verbose") + .short('v') + .takes_value(false) + .global(true) + .help("Show additional information"), + ) + .arg( + Arg::new("json_rpc_url") + .short('u') + .long("url") + .value_name("URL") + .takes_value(true) + .global(true) + .value_parser(parse_url_or_moniker) + .help("JSON RPC URL for the cluster [default: value from configuration file]"), + ) + .subcommand( + Command::new("create-extra-metas") + .about("Create the extra account metas account for a transfer hook program") + .arg( + Arg::with_name("program_id") + .validator(clap_is_valid_pubkey) + .value_name("TRANSFER_HOOK_PROGRAM") + .takes_value(true) + .index(1) + .required(true) + .help("The transfer hook program id"), + ) + .arg( + Arg::with_name("token") + .validator(clap_is_valid_pubkey) + .value_name("TOKEN_MINT_ADDRESS") + .takes_value(true) + .index(2) + .required(true) + .help("The token mint address for the transfer hook"), + ) + .arg( + Arg::with_name("transfer_hook_account") + .value_parser(parse_transfer_hook_account) + .value_name("PUBKEY:ROLE") + .takes_value(true) + .multiple(true) + .min_values(0) + .index(3) + .help("Additional pubkey(s) required for a transfer hook and their \ + role, in the format \":\". The role must be \ + \"readonly\", \"writable\". \"readonly-signer\", or \"writable-signer\".") + ) + .arg( + Arg::new("mint_authority") + .long("mint-authority") + .value_name("KEYPAIR") + .validator(|s| is_valid_signer(s)) + .takes_value(true) + .global(true) + .help("Filepath or URL to mint-authority keypair [default: client keypair]"), + ) + ).get_matches(); + + let (command, matches) = app_matches.subcommand().unwrap(); + let mut wallet_manager: Option> = None; + + let cli_config = if let Some(config_file) = matches.value_of("config_file") { + solana_cli_config::Config::load(config_file).unwrap_or_default() + } else { + solana_cli_config::Config::default() + }; + + let config = { + let default_signer = DefaultSigner::new( + "fee_payer", + matches + .value_of("fee_payer") + .map(|s| s.to_string()) + .unwrap_or_else(|| cli_config.keypair_path.clone()), + ); + + let json_rpc_url = normalize_to_url_if_moniker( + matches + .value_of("json_rpc_url") + .unwrap_or(&cli_config.json_rpc_url), + ); + + Config { + commitment_config: CommitmentConfig::confirmed(), + default_signer: default_signer + .signer_from_path(matches, &mut wallet_manager) + .unwrap_or_else(|err| { + eprintln!("error: {err}"); + exit(1); + }), + json_rpc_url, + verbose: matches.is_present("verbose"), + } + }; + solana_logger::setup_with_default("solana=info"); + + if config.verbose { + println!("JSON RPC URL: {}", config.json_rpc_url); + } + let rpc_client = + RpcClient::new_with_commitment(config.json_rpc_url.clone(), config.commitment_config); + + match (command, matches) { + ("create-extra-metas", arg_matches) => { + let program_id = pubkey_of_signer(arg_matches, "program_id", &mut wallet_manager) + .unwrap() + .unwrap(); + let token = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) + .unwrap() + .unwrap(); + let transfer_hook_accounts = arg_matches + .get_many::("transfer_hook_account") + .unwrap_or_default() + .cloned() + .collect(); + let mint_authority = DefaultSigner::new( + "mint_authority", + matches + .value_of("mint_authority") + .map(|s| s.to_string()) + .unwrap_or_else(|| cli_config.keypair_path.clone()), + ) + .signer_from_path(matches, &mut wallet_manager) + .unwrap_or_else(|err| { + eprintln!("error: {err}"); + exit(1); + }); + let signature = process_create_extra_account_metas( + &rpc_client, + &program_id, + &token, + transfer_hook_accounts, + mint_authority.as_ref(), + config.default_signer.as_ref(), + ) + .await + .unwrap_or_else(|err| { + eprintln!("error: send transaction: {err}"); + exit(1); + }); + println!("Signature: {signature}"); + } + _ => unreachable!(), + }; + + Ok(()) +} + +#[cfg(test)] +mod test { + use { + super::*, + solana_sdk::{bpf_loader_upgradeable, signer::keypair::Keypair}, + solana_test_validator::{TestValidator, TestValidatorGenesis, UpgradeableProgramInfo}, + spl_token_client::{ + client::{ + ProgramClient, ProgramRpcClient, ProgramRpcClientSendTransaction, SendTransaction, + SimulateTransaction, + }, + token::Token, + }, + std::{path::PathBuf, sync::Arc}, + }; + + async fn new_validator_for_test(program_id: Pubkey) -> (TestValidator, Keypair) { + solana_logger::setup(); + let mut test_validator_genesis = TestValidatorGenesis::default(); + test_validator_genesis.add_upgradeable_programs_with_path(&[UpgradeableProgramInfo { + program_id, + loader: bpf_loader_upgradeable::id(), + program_path: PathBuf::from("../../../target/deploy/spl_transfer_hook_example.so"), + upgrade_authority: Pubkey::new_unique(), + }]); + test_validator_genesis.start_async().await + } + + async fn setup_mint( + program_id: &Pubkey, + mint_authority: &Pubkey, + decimals: u8, + payer: Arc, + client: Arc>, + ) -> Token { + let mint_account = Keypair::new(); + let token = Token::new( + client, + program_id, + &mint_account.pubkey(), + Some(decimals), + payer, + ); + token + .create_mint(mint_authority, None, vec![], &[&mint_account]) + .await + .unwrap(); + token + } + + #[tokio::test] + async fn test_create() { + let program_id = Pubkey::new_unique(); + + let (test_validator, payer) = new_validator_for_test(program_id).await; + let payer: Arc = Arc::new(payer); + let rpc_client = Arc::new(test_validator.get_async_rpc_client()); + let client = Arc::new(ProgramRpcClient::new( + rpc_client.clone(), + ProgramRpcClientSendTransaction, + )); + + let mint_authority = Keypair::new(); + let decimals = 2; + + let token = setup_mint( + &spl_token_2022::id(), + &mint_authority.pubkey(), + decimals, + payer.clone(), + client.clone(), + ) + .await; + + let required_address = Pubkey::new_unique(); + let accounts = vec![AccountMeta::new_readonly(required_address, false)]; + process_create_extra_account_metas( + &rpc_client, + &program_id, + token.get_address(), + accounts, + &mint_authority, + payer.as_ref(), + ) + .await + .unwrap(); + + let extra_account_metas_address = + get_extra_account_metas_address(token.get_address(), &program_id); + let account = rpc_client + .get_account(&extra_account_metas_address) + .await + .unwrap(); + assert_eq!(account.owner, program_id); + } +} diff --git a/token/transfer-hook-example/Cargo.toml b/token/transfer-hook/example/Cargo.toml similarity index 51% rename from token/transfer-hook-example/Cargo.toml rename to token/transfer-hook/example/Cargo.toml index 2c3fb8f67f8..b8a48219254 100644 --- a/token/transfer-hook-example/Cargo.toml +++ b/token/transfer-hook/example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-transfer-hook-example" -version = "0.1.0" +version = "0.3.0" description = "Solana Program Library Transfer Hook Example Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -13,15 +13,15 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -solana-program = "1.16.1" -spl-tlv-account-resolution = { version = "0.2.0" , path = "../../libraries/tlv-account-resolution" } -spl-token-2022 = { version = "0.7", path = "../program-2022", features = ["no-entrypoint"] } -spl-transfer-hook-interface = { version = "0.1.0" , path = "../transfer-hook-interface" } -spl-type-length-value = { version = "0.2.0" , path = "../../libraries/type-length-value" } +solana-program = "1.17.2" +spl-tlv-account-resolution = { version = "0.4" , path = "../../../libraries/tlv-account-resolution" } +spl-token-2022 = { version = "0.9", path = "../../program-2022", features = ["no-entrypoint"] } +spl-transfer-hook-interface = { version = "0.3" , path = "../interface" } +spl-type-length-value = { version = "0.3" , path = "../../../libraries/type-length-value" } [dev-dependencies] -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/token/transfer-hook-example/README.md b/token/transfer-hook/example/README.md similarity index 96% rename from token/transfer-hook-example/README.md rename to token/transfer-hook/example/README.md index 1988b78d515..f3d2aef4a4b 100644 --- a/token/transfer-hook-example/README.md +++ b/token/transfer-hook/example/README.md @@ -4,7 +4,7 @@ Full example program and tests implementing the `spl-transfer-hook-interface`, to be used for testing a program that calls into the `spl-transfer-hook-interface`. See the -[SPL Transfer Hook Interface](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook-interface) +[SPL Transfer Hook Interface](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface) code for more information. ### Example usage of example diff --git a/token/transfer-hook-example/src/entrypoint.rs b/token/transfer-hook/example/src/entrypoint.rs similarity index 100% rename from token/transfer-hook-example/src/entrypoint.rs rename to token/transfer-hook/example/src/entrypoint.rs diff --git a/token/transfer-hook-example/src/lib.rs b/token/transfer-hook/example/src/lib.rs similarity index 54% rename from token/transfer-hook-example/src/lib.rs rename to token/transfer-hook/example/src/lib.rs index c7452cf2af3..9db06d9c66e 100644 --- a/token/transfer-hook-example/src/lib.rs +++ b/token/transfer-hook/example/src/lib.rs @@ -1,9 +1,9 @@ -//! Crate defining an example program for performing a hook on transfer, where the -//! token program calls into a separate program with additional accounts after -//! all other logic, to be sure that a transfer has accomplished all required -//! preconditions. +//! Crate defining an example program for performing a hook on transfer, where +//! the token program calls into a separate program with additional accounts +//! after all other logic, to be sure that a transfer has accomplished all +//! required preconditions. -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] @@ -13,5 +13,6 @@ pub mod state; #[cfg(not(feature = "no-entrypoint"))] mod entrypoint; -// Export current sdk types for downstream users building with a different sdk version +// Export current sdk types for downstream users building with a different sdk +// version pub use solana_program; diff --git a/token/transfer-hook-example/src/processor.rs b/token/transfer-hook/example/src/processor.rs similarity index 76% rename from token/transfer-hook-example/src/processor.rs rename to token/transfer-hook/example/src/processor.rs index b253f55b729..fccd0ba0bae 100644 --- a/token/transfer-hook-example/src/processor.rs +++ b/token/transfer-hook/example/src/processor.rs @@ -10,7 +10,7 @@ use { pubkey::Pubkey, system_instruction, }, - spl_tlv_account_resolution::state::ExtraAccountMetas, + spl_tlv_account_resolution::{account::ExtraAccountMeta, state::ExtraAccountMetaList}, spl_token_2022::{ extension::{ transfer_hook::TransferHookAccount, BaseStateWithExtensions, StateWithExtensions, @@ -23,7 +23,6 @@ use { get_extra_account_metas_address, get_extra_account_metas_address_and_bump_seed, instruction::{ExecuteInstruction, TransferHookInstruction}, }, - spl_type_length_value::state::TlvStateBorrowed, }; fn check_token_account_is_transferring(account_info: &AccountInfo) -> Result<(), ProgramError> { @@ -41,7 +40,7 @@ fn check_token_account_is_transferring(account_info: &AccountInfo) -> Result<(), pub fn process_execute( program_id: &Pubkey, accounts: &[AccountInfo], - _amount: u64, + amount: u64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -63,31 +62,22 @@ pub fn process_execute( } let data = extra_account_metas_info.try_borrow_data()?; - let state = TlvStateBorrowed::unpack(&data).unwrap(); - let extra_account_metas = - ExtraAccountMetas::unpack_with_tlv_state::(&state)?; - - // if incorrect number of are provided, error - let extra_account_infos = account_info_iter.as_slice(); - let account_metas = extra_account_metas.data(); - if extra_account_infos.len() != account_metas.len() { - return Err(TransferHookError::IncorrectAccount.into()); - } - // Let's assume that they're provided in the correct order - for (i, account_info) in extra_account_infos.iter().enumerate() { - if &account_metas[i] != account_info { - return Err(TransferHookError::IncorrectAccount.into()); - } - } + ExtraAccountMetaList::check_account_infos::( + accounts, + &TransferHookInstruction::Execute { amount }.pack(), + program_id, + &data, + )?; Ok(()) } -/// Processes a [InitializeExtraAccountMetas](enum.TransferHookInstruction.html) instruction. -pub fn process_initialize_extra_account_metas( +/// Processes a [InitializeExtraAccountMetaList](enum.TransferHookInstruction.html) instruction. +pub fn process_initialize_extra_account_meta_list( program_id: &Pubkey, accounts: &[AccountInfo], + extra_account_metas: &[ExtraAccountMeta], ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -122,9 +112,8 @@ pub fn process_initialize_extra_account_metas( // Create the account let bump_seed = [bump_seed]; let signer_seeds = collect_extra_account_metas_signer_seeds(mint_info.key, &bump_seed); - let extra_account_infos = account_info_iter.as_slice(); - let length = extra_account_infos.len(); - let account_size = ExtraAccountMetas::size_of(length)?; + let length = extra_account_metas.len(); + let account_size = ExtraAccountMetaList::size_of(length)?; invoke_signed( &system_instruction::allocate(extra_account_metas_info.key, account_size as u64), &[extra_account_metas_info.clone()], @@ -138,10 +127,7 @@ pub fn process_initialize_extra_account_metas( // Write the data let mut data = extra_account_metas_info.try_borrow_mut_data()?; - ExtraAccountMetas::init_with_account_infos::( - &mut data, - extra_account_infos, - )?; + ExtraAccountMetaList::init::(&mut data, extra_account_metas)?; Ok(()) } @@ -155,9 +141,11 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> P msg!("Instruction: Execute"); process_execute(program_id, accounts, amount) } - TransferHookInstruction::InitializeExtraAccountMetas => { - msg!("Instruction: InitializeExtraAccountMetas"); - process_initialize_extra_account_metas(program_id, accounts) + TransferHookInstruction::InitializeExtraAccountMetaList { + extra_account_metas, + } => { + msg!("Instruction: InitializeExtraAccountMetaList"); + process_initialize_extra_account_meta_list(program_id, accounts, &extra_account_metas) } } } diff --git a/token/transfer-hook/example/src/state.rs b/token/transfer-hook/example/src/state.rs new file mode 100644 index 00000000000..b2c962c5072 --- /dev/null +++ b/token/transfer-hook/example/src/state.rs @@ -0,0 +1,15 @@ +//! State helpers for working with the example program + +use { + solana_program::program_error::ProgramError, + spl_tlv_account_resolution::{account::ExtraAccountMeta, state::ExtraAccountMetaList}, + spl_transfer_hook_interface::instruction::ExecuteInstruction, +}; + +/// Generate example data to be used directly in an account for testing +pub fn example_data(account_metas: &[ExtraAccountMeta]) -> Result, ProgramError> { + let account_size = ExtraAccountMetaList::size_of(account_metas.len())?; + let mut data = vec![0; account_size]; + ExtraAccountMetaList::init::(&mut data, account_metas)?; + Ok(data) +} diff --git a/token/transfer-hook-example/tests/functional.rs b/token/transfer-hook/example/tests/functional.rs similarity index 66% rename from token/transfer-hook-example/tests/functional.rs rename to token/transfer-hook/example/tests/functional.rs index 5b42dd02de9..989b6f901ce 100644 --- a/token/transfer-hook-example/tests/functional.rs +++ b/token/transfer-hook/example/tests/functional.rs @@ -1,4 +1,5 @@ -// Mark this test as SBF-only due to current `ProgramTest` limitations when CPIing into the system program +// Mark this test as SBF-only due to current `ProgramTest` limitations when +// CPIing into the system program #![cfg(feature = "test-sbf")] use { @@ -8,6 +9,7 @@ use { account_info::AccountInfo, entrypoint::ProgramResult, instruction::{AccountMeta, InstructionError}, + program_error::ProgramError, program_option::COption, pubkey::Pubkey, signature::Signer, @@ -15,7 +17,10 @@ use { system_instruction, sysvar, transaction::{Transaction, TransactionError}, }, - spl_tlv_account_resolution::state::ExtraAccountMetas, + spl_tlv_account_resolution::{ + account::ExtraAccountMeta, error::AccountResolutionError, seeds::Seed, + state::ExtraAccountMetaList, + }, spl_token_2022::{ extension::{transfer_hook::TransferHookAccount, ExtensionType, StateWithExtensionsMut}, state::{Account, AccountState, Mint}, @@ -23,7 +28,7 @@ use { spl_transfer_hook_interface::{ error::TransferHookError, get_extra_account_metas_address, - instruction::{execute_with_extra_account_metas, initialize_extra_account_metas}, + instruction::{execute_with_extra_account_metas, initialize_extra_account_meta_list}, onchain, }, }; @@ -60,7 +65,7 @@ fn setup_token_accounts( ) { // add mint, source, and destination accounts by hand to always force // the "transferring" flag to true - let mint_size = ExtensionType::try_get_account_len::(&[]).unwrap(); + let mint_size = ExtensionType::try_calculate_account_len::(&[]).unwrap(); let mut mint_data = vec![0; mint_size]; let mut state = StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); let token_amount = 1_000_000_000_000; @@ -83,7 +88,7 @@ fn setup_token_accounts( ); let account_size = - ExtensionType::try_get_account_len::(&[ExtensionType::TransferHookAccount]) + ExtensionType::try_calculate_account_len::(&[ExtensionType::TransferHookAccount]) .unwrap(); let mut account_data = vec![0; account_size]; let mut state = @@ -137,6 +142,7 @@ async fn success_execute() { let source = Pubkey::new_unique(); let destination = Pubkey::new_unique(); let decimals = 2; + let amount = 0u64; setup_token_accounts( &mut program_test, @@ -150,30 +156,81 @@ async fn success_execute() { true, ); - let extra_account_metas = get_extra_account_metas_address(&mint_address, &program_id); + let extra_account_metas_address = get_extra_account_metas_address(&mint_address, &program_id); + + let writable_pubkey = Pubkey::new_unique(); + + let init_extra_account_metas = [ + ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(), + ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: b"seed-prefix".to_vec(), + }, + Seed::AccountKey { index: 0 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::InstructionData { + index: 8, // After instruction discriminator + length: 8, // `u64` (amount) + }, + Seed::AccountKey { index: 2 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(), + ]; + + let extra_pda_1 = Pubkey::find_program_address( + &[ + b"seed-prefix", // Literal prefix + source.as_ref(), // Account at index 0 + ], + &program_id, + ) + .0; + let extra_pda_2 = Pubkey::find_program_address( + &[ + &amount.to_le_bytes(), // Instruction data bytes 8 to 16 + destination.as_ref(), // Account at index 2 + ], + &program_id, + ) + .0; - let extra_account_pubkeys = [ + let extra_account_metas = [ AccountMeta::new_readonly(sysvar::instructions::id(), false), AccountMeta::new_readonly(mint_authority_pubkey, true), - AccountMeta::new(extra_account_metas, false), + AccountMeta::new(extra_pda_1, false), + AccountMeta::new(extra_pda_2, false), + AccountMeta::new(writable_pubkey, false), ]; + let mut context = program_test.start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let rent_lamports = - rent.minimum_balance(ExtraAccountMetas::size_of(extra_account_pubkeys.len()).unwrap()); + let rent_lamports = rent + .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap()); let transaction = Transaction::new_signed_with_payer( &[ system_instruction::transfer( &context.payer.pubkey(), - &extra_account_metas, + &extra_account_metas_address, rent_lamports, ), - initialize_extra_account_metas( + initialize_extra_account_meta_list( &program_id, - &extra_account_metas, + &extra_account_metas_address, &mint_address, &mint_authority_pubkey, - &extra_account_pubkeys, + &init_extra_account_metas, ), ], Some(&context.payer.pubkey()), @@ -196,9 +253,9 @@ async fn success_execute() { &mint_address, &destination, &wallet.pubkey(), - &extra_account_metas, - &extra_account_pubkeys[..2], - 0, + &extra_account_metas_address, + &extra_account_metas[..2], + amount, )], Some(&context.payer.pubkey()), &[&context.payer, &mint_authority], @@ -214,17 +271,19 @@ async fn success_execute() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(TransferHookError::IncorrectAccount as u32), + InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32), ) ); } // fail with wrong account { - let extra_account_pubkeys = [ + let extra_account_metas = [ AccountMeta::new_readonly(sysvar::instructions::id(), false), AccountMeta::new_readonly(mint_authority_pubkey, true), - AccountMeta::new(wallet.pubkey(), false), + AccountMeta::new(extra_pda_1, false), + AccountMeta::new(extra_pda_2, false), + AccountMeta::new(Pubkey::new_unique(), false), ]; let transaction = Transaction::new_signed_with_payer( &[execute_with_extra_account_metas( @@ -233,9 +292,56 @@ async fn success_execute() { &mint_address, &destination, &wallet.pubkey(), + &extra_account_metas_address, &extra_account_metas, - &extra_account_pubkeys, + amount, + )], + Some(&context.payer.pubkey()), + &[&context.payer, &mint_authority], + context.last_blockhash, + ); + let error = context + .banks_client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + error, + TransactionError::InstructionError( 0, + InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32), + ) + ); + } + + // fail with wrong PDA + let wrong_pda_2 = Pubkey::find_program_address( + &[ + &99u64.to_le_bytes(), // Wrong data + destination.as_ref(), + ], + &program_id, + ) + .0; + { + let extra_account_metas = [ + AccountMeta::new_readonly(sysvar::instructions::id(), false), + AccountMeta::new_readonly(mint_authority_pubkey, true), + AccountMeta::new(extra_pda_1, false), + AccountMeta::new(wrong_pda_2, false), + AccountMeta::new(writable_pubkey, false), + ]; + let transaction = Transaction::new_signed_with_payer( + &[execute_with_extra_account_metas( + &program_id, + &source, + &mint_address, + &destination, + &wallet.pubkey(), + &extra_account_metas_address, + &extra_account_metas, + amount, )], Some(&context.payer.pubkey()), &[&context.payer, &mint_authority], @@ -251,17 +357,19 @@ async fn success_execute() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(TransferHookError::IncorrectAccount as u32), + InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32), ) ); } // fail with not signer { - let extra_account_pubkeys = [ + let extra_account_metas = [ AccountMeta::new_readonly(sysvar::instructions::id(), false), AccountMeta::new_readonly(mint_authority_pubkey, false), - AccountMeta::new(extra_account_metas, false), + AccountMeta::new(extra_pda_1, false), + AccountMeta::new(extra_pda_2, false), + AccountMeta::new(writable_pubkey, false), ]; let transaction = Transaction::new_signed_with_payer( &[execute_with_extra_account_metas( @@ -270,9 +378,9 @@ async fn success_execute() { &mint_address, &destination, &wallet.pubkey(), + &extra_account_metas_address, &extra_account_metas, - &extra_account_pubkeys, - 0, + amount, )], Some(&context.payer.pubkey()), &[&context.payer], @@ -288,7 +396,7 @@ async fn success_execute() { error, TransactionError::InstructionError( 0, - InstructionError::Custom(TransferHookError::IncorrectAccount as u32), + InstructionError::Custom(AccountResolutionError::IncorrectAccount as u32), ) ); } @@ -302,9 +410,9 @@ async fn success_execute() { &mint_address, &destination, &wallet.pubkey(), + &extra_account_metas_address, &extra_account_metas, - &extra_account_pubkeys, - 0, + amount, )], Some(&context.payer.pubkey()), &[&context.payer, &mint_authority], @@ -348,7 +456,7 @@ async fn fail_incorrect_derivation() { let mut context = program_test.start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let rent_lamports = rent.minimum_balance(ExtraAccountMetas::size_of(0).unwrap()); + let rent_lamports = rent.minimum_balance(ExtraAccountMetaList::size_of(0).unwrap()); let transaction = Transaction::new_signed_with_payer( &[ @@ -357,7 +465,7 @@ async fn fail_incorrect_derivation() { &extra_account_metas, rent_lamports, ), - initialize_extra_account_metas( + initialize_extra_account_meta_list( &program_id, &extra_account_metas, &mint_address, @@ -385,16 +493,21 @@ async fn fail_incorrect_derivation() { pub fn process_instruction( _program_id: &Pubkey, accounts: &[AccountInfo], - _input: &[u8], + input: &[u8], ) -> ProgramResult { + let amount = input + .get(8..16) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(ProgramError::InvalidInstructionData)?; onchain::invoke_execute( accounts[0].key, accounts[1].clone(), accounts[2].clone(), accounts[3].clone(), accounts[4].clone(), - &accounts[4..], - 0, + &accounts[5..], + amount, ) } @@ -417,6 +530,7 @@ async fn success_on_chain_invoke() { let source = Pubkey::new_unique(); let destination = Pubkey::new_unique(); let decimals = 2; + let amount = 0u64; setup_token_accounts( &mut program_test, @@ -430,31 +544,81 @@ async fn success_on_chain_invoke() { true, ); - let extra_account_metas = get_extra_account_metas_address(&mint_address, &hook_program_id); - + let extra_account_metas_address = + get_extra_account_metas_address(&mint_address, &hook_program_id); let writable_pubkey = Pubkey::new_unique(); - let extra_account_pubkeys = [ + + let init_extra_account_metas = [ + ExtraAccountMeta::new_with_pubkey(&sysvar::instructions::id(), false, false).unwrap(), + ExtraAccountMeta::new_with_pubkey(&mint_authority_pubkey, true, false).unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::Literal { + bytes: b"seed-prefix".to_vec(), + }, + Seed::AccountKey { index: 0 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_seeds( + &[ + Seed::InstructionData { + index: 8, // After instruction discriminator + length: 8, // `u64` (amount) + }, + Seed::AccountKey { index: 2 }, + ], + false, + true, + ) + .unwrap(), + ExtraAccountMeta::new_with_pubkey(&writable_pubkey, false, true).unwrap(), + ]; + + let extra_pda_1 = Pubkey::find_program_address( + &[ + b"seed-prefix", // Literal prefix + source.as_ref(), // Account at index 0 + ], + &hook_program_id, + ) + .0; + let extra_pda_2 = Pubkey::find_program_address( + &[ + &amount.to_le_bytes(), // Instruction data bytes 8 to 16 + destination.as_ref(), // Account at index 2 + ], + &hook_program_id, + ) + .0; + + let extra_account_metas = [ AccountMeta::new_readonly(sysvar::instructions::id(), false), AccountMeta::new_readonly(mint_authority_pubkey, true), + AccountMeta::new(extra_pda_1, false), + AccountMeta::new(extra_pda_2, false), AccountMeta::new(writable_pubkey, false), ]; + let mut context = program_test.start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let rent_lamports = - rent.minimum_balance(ExtraAccountMetas::size_of(extra_account_pubkeys.len()).unwrap()); + let rent_lamports = rent + .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap()); let transaction = Transaction::new_signed_with_payer( &[ system_instruction::transfer( &context.payer.pubkey(), - &extra_account_metas, + &extra_account_metas_address, rent_lamports, ), - initialize_extra_account_metas( + initialize_extra_account_meta_list( &hook_program_id, - &extra_account_metas, + &extra_account_metas_address, &mint_address, &mint_authority_pubkey, - &extra_account_pubkeys, + &init_extra_account_metas, ), ], Some(&context.payer.pubkey()), @@ -475,9 +639,9 @@ async fn success_on_chain_invoke() { &mint_address, &destination, &wallet.pubkey(), + &extra_account_metas_address, &extra_account_metas, - &extra_account_pubkeys, - 0, + amount, ); test_instruction .accounts @@ -509,6 +673,7 @@ async fn fail_without_transferring_flag() { let source = Pubkey::new_unique(); let destination = Pubkey::new_unique(); let decimals = 2; + setup_token_accounts( &mut program_test, &token_program_id, @@ -521,25 +686,26 @@ async fn fail_without_transferring_flag() { false, ); - let extra_account_metas = get_extra_account_metas_address(&mint_address, &program_id); - let extra_account_pubkeys = []; + let extra_account_metas_address = get_extra_account_metas_address(&mint_address, &program_id); + let extra_account_metas = []; + let init_extra_account_metas = []; let mut context = program_test.start_with_context().await; let rent = context.banks_client.get_rent().await.unwrap(); - let rent_lamports = - rent.minimum_balance(ExtraAccountMetas::size_of(extra_account_pubkeys.len()).unwrap()); + let rent_lamports = rent + .minimum_balance(ExtraAccountMetaList::size_of(init_extra_account_metas.len()).unwrap()); let transaction = Transaction::new_signed_with_payer( &[ system_instruction::transfer( &context.payer.pubkey(), - &extra_account_metas, + &extra_account_metas_address, rent_lamports, ), - initialize_extra_account_metas( + initialize_extra_account_meta_list( &program_id, - &extra_account_metas, + &extra_account_metas_address, &mint_address, &mint_authority_pubkey, - &extra_account_pubkeys, + &init_extra_account_metas, ), ], Some(&context.payer.pubkey()), @@ -559,8 +725,8 @@ async fn fail_without_transferring_flag() { &mint_address, &destination, &wallet.pubkey(), + &extra_account_metas_address, &extra_account_metas, - &extra_account_pubkeys, 0, )], Some(&context.payer.pubkey()), diff --git a/token/transfer-hook/interface/Cargo.toml b/token/transfer-hook/interface/Cargo.toml new file mode 100644 index 00000000000..79644bcd4ee --- /dev/null +++ b/token/transfer-hook/interface/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "spl-transfer-hook-interface" +version = "0.3.0" +description = "Solana Program Library Transfer Hook Interface" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +arrayref = "0.3.7" +bytemuck = { version = "1.14.0", features = ["derive"] } +solana-program = "1.17.2" +spl-discriminator = { version = "0.1" , path = "../../../libraries/discriminator" } +spl-program-error = { version = "0.3" , path = "../../../libraries/program-error" } +spl-tlv-account-resolution = { version = "0.4" , path = "../../../libraries/tlv-account-resolution" } +spl-type-length-value = { version = "0.3" , path = "../../../libraries/type-length-value" } +spl-pod = { version = "0.1", path = "../../../libraries/pod" } + +[lib] +crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/token/transfer-hook/interface/README.md b/token/transfer-hook/interface/README.md new file mode 100644 index 00000000000..84dab20d08d --- /dev/null +++ b/token/transfer-hook/interface/README.md @@ -0,0 +1,149 @@ +## Transfer-Hook Interface + +### Example program + +Here is an example program that only implements the required "execute" instruction, +assuming that the proper account data is already written to the appropriate +program-derived address defined by the interface. + +```rust +use { + solana_program::{entrypoint::ProgramResult, program_error::ProgramError}, + spl_tlv_account_resolution::state::ExtraAccountMetaList, + spl_transfer_hook_interface::instruction::{ExecuteInstruction, TransferHookInstruction}, + spl_type_length_value::state::TlvStateBorrowed, +}; +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + input: &[u8], +) -> ProgramResult { + let instruction = TransferHookInstruction::unpack(input)?; + let _amount = match instruction { + TransferHookInstruction::Execute { amount } => amount, + _ => return Err(ProgramError::InvalidInstructionData), + }; + let account_info_iter = &mut accounts.iter(); + + // Pull out the accounts in order, none are validated in this test program + let _source_account_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let _destination_account_info = next_account_info(account_info_iter)?; + let _authority_info = next_account_info(account_info_iter)?; + let extra_account_metas_info = next_account_info(account_info_iter)?; + + // Only check that the correct pda and account are provided + let expected_validation_address = get_extra_account_metas_address(mint_info.key, program_id); + if expected_validation_address != *extra_account_metas_info.key { + return Err(ProgramError::InvalidSeeds); + } + + // Load the extra required accounts from the validation account + let data = extra_account_metas_info.try_borrow_data()?; + + // Check the provided accounts against the validation data + ExtraAccountMetaList::check_account_infos::( + accounts, + &TransferHookInstruction::Execute { amount }.pack(), + program_id, + &data, + )?; + + Ok(()) +} +``` + +### Motivation + +Token creators may need more control over how their token is transferred. The +most prominent use case revolves around NFT royalties. Whenever a token is moved, +the creator should be entitled to royalties, but due to the design of the current +token program, it's impossible to stop a transfer at the protocol level. + +Current solutions typically resort to perpetually freezing tokens, which requires +a whole proxy layer to interact with the token. Wallets and marketplaces need +to be aware of the proxy layer in order to properly use the token. + +Worse still, different royalty systems have different proxy layers for using +their token. All in all, these systems harm composability and make development +harder. + +### Solution + +To improve the situation, Token-2022 introduces the concept of the transfer-hook +interface and extension. A token creator must develop and deploy a program that +implements the interface and then configure their token mint to use their program. + +During transfer, Token-2022 calls into the program with the accounts specified +at a well-defined program-derived address for that mint and program id. This +call happens after all other transfer logic, so the accounts reflect the *end* +state of the transfer. + +### How to Use + +Developers must implement the `Execute` instruction, and optionally the +`InitializeExtraAccountMetaList` instruction to write the required additional account +pubkeys into the program-derived address defined by the mint and program id. + +Note: it's technically not required to implement `InitializeExtraAccountMetaList` +at that instruction descriminator. Your program may implement multiple interfaces, +so any other instruction in your program can create the account at the program-derived +address! + +When your program stores configurations for extra required accounts in the +well-defined program-derived address, it's possible to send an instruction - +such as `Execute` (transfer) - to your program with only accounts required +for the interface instruction, and all extra required accounts are +automatically resolved! + +### Account Resolution + +Implementers of the transfer-hook interface are encouraged to make use of the +[spl-tlv-account-resolution](https://github.com/solana-labs/solana-program-library/tree/master/libraries/tlv-account-resolution/README.md) +library to manage the additional required accounts for their transfer hook +program. + +TLV Account Resolution is capable of powering on-chain account resolution +when an instruction that requires extra accounts is invoked. +Read more about how account resolution works in the repository's +[README file](https://github.com/solana-labs/solana-program-library/tree/master/libraries/tlv-account-resolution/README.md). + +### An Example + +You have created a DAO to govern a community. Your DAO's authority is a +multisig account, and you want to ensure that any transfer of your token is +approved by the DAO. You also want to make sure that someone who intends to +transfer your token has the proper permissions to do so. + +Let's assume the DAO multisig has some **fixed address**. And let's assume that +in order to have the `can_transfer` permission, a user must have this +**dynamic program-derived address** associated with their wallet via the +following seeds: `"can_transfer" + `. + +Using the transfer-hook interface, you can store these configurations in the +well-defined program-derived address for your mint and program id. + +When a user attempts to transfer your token, they might provide to Token-2022: + +```rust +[source, mint, destination, owner/delegate] +``` + +Token-2022 will then call into your program, +**resolving the extra required accounts automatically** from your stored +configurations, to result in the following accounts being provided to your +program: + +```rust +[source, mint, destination, owner/delegate, dao_authority, can_transfer_pda] +``` + +### Utilities + +The `spl-transfer-hook-interface` library provides offchain and onchain helpers +for resolving the additional accounts required. See +[invoke.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface/src/invoke.rs) +for usage on-chain, and +[offchain.rs](https://github.com/solana-labs/solana-program-library/tree/master/token/transfer-hook/interface/src/offchain.rs) +for fetching the additional required account metas with any async off-chain client +like `BanksClient` or `RpcClient`. diff --git a/token/transfer-hook/interface/src/error.rs b/token/transfer-hook/interface/src/error.rs new file mode 100644 index 00000000000..aaf25e19627 --- /dev/null +++ b/token/transfer-hook/interface/src/error.rs @@ -0,0 +1,20 @@ +//! Error types + +use spl_program_error::*; + +/// Errors that may be returned by the interface. +#[spl_program_error(hash_error_code_start = 2_110_272_652)] +pub enum TransferHookError { + /// Incorrect account provided + #[error("Incorrect account provided")] + IncorrectAccount, + /// Mint has no mint authority + #[error("Mint has no mint authority")] + MintHasNoMintAuthority, + /// Incorrect mint authority has signed the instruction + #[error("Incorrect mint authority has signed the instruction")] + IncorrectMintAuthority, + /// Program called outside of a token transfer + #[error("Program called outside of a token transfer")] + ProgramCalledOutsideOfTransfer, +} diff --git a/token/transfer-hook-interface/src/instruction.rs b/token/transfer-hook/interface/src/instruction.rs similarity index 75% rename from token/transfer-hook-interface/src/instruction.rs rename to token/transfer-hook/interface/src/instruction.rs index 1e597c2a70d..a1fdcfea519 100644 --- a/token/transfer-hook-interface/src/instruction.rs +++ b/token/transfer-hook/interface/src/instruction.rs @@ -8,6 +8,8 @@ use { system_program, }, spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, + spl_pod::{bytemuck::pod_slice_to_bytes, slice::PodSlice}, + spl_tlv_account_resolution::account::ExtraAccountMeta, std::convert::TryInto, }; @@ -39,12 +41,14 @@ pub enum TransferHookInstruction { /// 1. `[]` Mint /// 2. `[s]` Mint authority /// 3. `[]` System program - /// 4..4+M `[]` `M` additional accounts, to be written to validation data /// - InitializeExtraAccountMetas, + InitializeExtraAccountMetaList { + /// List of `ExtraAccountMeta`s to write into the account + extra_account_metas: Vec, + }, } /// TLV instruction type only used to define the discriminator. The actual data -/// is entirely managed by `ExtraAccountMetas`, and it is the only data contained +/// is entirely managed by `ExtraAccountMetaList`, and it is the only data contained /// by this type. #[derive(SplDiscriminate)] #[discriminator_hash_input("spl-transfer-hook-interface:execute")] @@ -54,7 +58,7 @@ pub struct ExecuteInstruction; /// for the transfer hook #[derive(SplDiscriminate)] #[discriminator_hash_input("spl-transfer-hook-interface:initialize-extra-account-metas")] -pub struct InitializeExtraAccountMetasInstruction; +pub struct InitializeExtraAccountMetaListInstruction; impl TransferHookInstruction { /// Unpacks a byte buffer into a [TransferHookInstruction](enum.TransferHookInstruction.html). @@ -72,8 +76,12 @@ impl TransferHookInstruction { .ok_or(ProgramError::InvalidInstructionData)?; Self::Execute { amount } } - InitializeExtraAccountMetasInstruction::SPL_DISCRIMINATOR_SLICE => { - Self::InitializeExtraAccountMetas + InitializeExtraAccountMetaListInstruction::SPL_DISCRIMINATOR_SLICE => { + let pod_slice = PodSlice::::unpack(rest)?; + let extra_account_metas = pod_slice.data().to_vec(); + Self::InitializeExtraAccountMetaList { + extra_account_metas, + } } _ => return Err(ProgramError::InvalidInstructionData), }) @@ -87,10 +95,14 @@ impl TransferHookInstruction { buf.extend_from_slice(ExecuteInstruction::SPL_DISCRIMINATOR_SLICE); buf.extend_from_slice(&amount.to_le_bytes()); } - Self::InitializeExtraAccountMetas => { + Self::InitializeExtraAccountMetaList { + extra_account_metas, + } => { buf.extend_from_slice( - InitializeExtraAccountMetasInstruction::SPL_DISCRIMINATOR_SLICE, + InitializeExtraAccountMetaListInstruction::SPL_DISCRIMINATOR_SLICE, ); + buf.extend_from_slice(&(extra_account_metas.len() as u32).to_le_bytes()); + buf.extend_from_slice(pod_slice_to_bytes(extra_account_metas)); } }; buf @@ -149,23 +161,25 @@ pub fn execute( } } -/// Creates a `InitializeExtraAccountMetas` instruction. -pub fn initialize_extra_account_metas( +/// Creates a `InitializeExtraAccountMetaList` instruction. +pub fn initialize_extra_account_meta_list( program_id: &Pubkey, extra_account_metas_pubkey: &Pubkey, mint_pubkey: &Pubkey, authority_pubkey: &Pubkey, - additional_accounts: &[AccountMeta], + extra_account_metas: &[ExtraAccountMeta], ) -> Instruction { - let data = TransferHookInstruction::InitializeExtraAccountMetas.pack(); + let data = TransferHookInstruction::InitializeExtraAccountMetaList { + extra_account_metas: extra_account_metas.to_vec(), + } + .pack(); - let mut accounts = vec![ + let accounts = vec![ AccountMeta::new(*extra_account_metas_pubkey, false), AccountMeta::new_readonly(*mint_pubkey, false), AccountMeta::new_readonly(*authority_pubkey, true), AccountMeta::new_readonly(system_program::id(), false), ]; - accounts.extend_from_slice(additional_accounts); Instruction { program_id: *program_id, @@ -176,7 +190,7 @@ pub fn initialize_extra_account_metas( #[cfg(test)] mod test { - use {super::*, crate::NAMESPACE, solana_program::hash}; + use {super::*, crate::NAMESPACE, solana_program::hash, spl_pod::bytemuck::pod_from_bytes}; #[test] fn validate_packing() { @@ -197,7 +211,21 @@ mod test { #[test] fn initialize_validation_pubkeys_packing() { - let check = TransferHookInstruction::InitializeExtraAccountMetas; + let extra_meta_len_bytes = &[ + 1, 0, 0, 0, // `1u32` + ]; + let extra_meta_bytes = &[ + 0, // `AccountMeta` + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, // pubkey + 0, // is_signer + 0, // is_writable + ]; + let extra_account_metas = + vec![*pod_from_bytes::(extra_meta_bytes).unwrap()]; + let check = TransferHookInstruction::InitializeExtraAccountMetaList { + extra_account_metas, + }; let packed = check.pack(); // Please use INITIALIZE_EXTRA_ACCOUNT_METAS_DISCRIMINATOR in your program, // the following is just for test purposes @@ -206,6 +234,8 @@ mod test { let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH]; let mut expect = vec![]; expect.extend_from_slice(discriminator.as_ref()); + expect.extend_from_slice(extra_meta_len_bytes); + expect.extend_from_slice(extra_meta_bytes); assert_eq!(packed, expect); let unpacked = TransferHookInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); diff --git a/token/transfer-hook-interface/src/lib.rs b/token/transfer-hook/interface/src/lib.rs similarity index 97% rename from token/transfer-hook-interface/src/lib.rs rename to token/transfer-hook/interface/src/lib.rs index 4212fa963b2..20af3abe1ed 100644 --- a/token/transfer-hook-interface/src/lib.rs +++ b/token/transfer-hook/interface/src/lib.rs @@ -3,7 +3,7 @@ //! all other logic, to be sure that a transfer has accomplished all required //! preconditions. -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/token/transfer-hook-interface/src/offchain.rs b/token/transfer-hook/interface/src/offchain.rs similarity index 64% rename from token/transfer-hook-interface/src/offchain.rs rename to token/transfer-hook/interface/src/offchain.rs index c5fe239bf5e..60b7ea53593 100644 --- a/token/transfer-hook-interface/src/offchain.rs +++ b/token/transfer-hook/interface/src/offchain.rs @@ -1,18 +1,17 @@ //! Offchain helper for fetching required accounts to build instructions +pub use spl_tlv_account_resolution::state::{AccountDataResult, AccountFetchError}; use { crate::{get_extra_account_metas_address, instruction::ExecuteInstruction}, - solana_program::{instruction::AccountMeta, program_error::ProgramError, pubkey::Pubkey}, - spl_tlv_account_resolution::state::ExtraAccountMetas, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, + }, + spl_tlv_account_resolution::state::ExtraAccountMetaList, std::future::Future, }; -/// Type representing the output of an account fetching function, for easy -/// chaining between APIs -pub type AccountDataResult = Result>, AccountFetchError>; -/// Generic error type that can come out of any client while fetching account data -pub type AccountFetchError = Box; - /// Offchain helper to get all additional required account metas for a mint /// /// To be client-agnostic and to avoid pulling in the full solana-sdk, this @@ -36,9 +35,9 @@ pub type AccountFetchError = Box; /// &program_id, /// ).await?; /// ``` -pub async fn get_extra_account_metas( - account_metas: &mut Vec, - get_account_data_fn: F, +pub async fn resolve_extra_account_metas( + instruction: &mut Instruction, + fetch_account_data_fn: F, mint: &Pubkey, permissioned_transfer_program_id: &Pubkey, ) -> Result<(), AccountFetchError> @@ -48,17 +47,24 @@ where { let validation_address = get_extra_account_metas_address(mint, permissioned_transfer_program_id); - let validation_account_data = get_account_data_fn(validation_address) + let validation_account_data = fetch_account_data_fn(validation_address) .await? .ok_or(ProgramError::InvalidAccountData)?; - ExtraAccountMetas::add_to_vec::(account_metas, &validation_account_data)?; + ExtraAccountMetaList::add_to_instruction::( + instruction, + fetch_account_data_fn, + &validation_account_data, + ) + .await?; // The onchain helpers pull out the required accounts from an opaque // slice by pubkey, so the order doesn't matter here! - account_metas.push(AccountMeta::new_readonly( + instruction.accounts.push(AccountMeta::new_readonly( *permissioned_transfer_program_id, false, )); - account_metas.push(AccountMeta::new_readonly(validation_address, false)); + instruction + .accounts + .push(AccountMeta::new_readonly(validation_address, false)); Ok(()) } diff --git a/token/transfer-hook-interface/src/onchain.rs b/token/transfer-hook/interface/src/onchain.rs similarity index 92% rename from token/transfer-hook-interface/src/onchain.rs rename to token/transfer-hook/interface/src/onchain.rs index bb5cc6a65bb..1c0e0fb76ef 100644 --- a/token/transfer-hook-interface/src/onchain.rs +++ b/token/transfer-hook/interface/src/onchain.rs @@ -9,7 +9,7 @@ use { program::invoke, pubkey::Pubkey, }, - spl_tlv_account_resolution::state::ExtraAccountMetas, + spl_tlv_account_resolution::state::ExtraAccountMetaList, }; /// Helper to CPI into a transfer-hook program on-chain, looking through the /// additional account infos to create the proper instruction @@ -44,7 +44,7 @@ pub fn invoke_execute<'a>( authority_info, validation_info.clone(), ]; - ExtraAccountMetas::add_to_cpi_instruction::( + ExtraAccountMetaList::add_to_cpi_instruction::( &mut cpi_instruction, &mut cpi_account_infos, &validation_info.try_borrow_data()?, @@ -73,7 +73,7 @@ pub fn add_cpi_accounts_for_execute<'a>( .find(|&x| x.key == program_id) .ok_or(TransferHookError::IncorrectAccount)?; - ExtraAccountMetas::add_to_cpi_instruction::( + ExtraAccountMetaList::add_to_cpi_instruction::( cpi_instruction, cpi_account_infos, &validation_info.try_borrow_data()?, diff --git a/update-solana-dependencies.sh b/update-solana-dependencies.sh index e2063645a53..0c753833d1a 100755 --- a/update-solana-dependencies.sh +++ b/update-solana-dependencies.sh @@ -22,25 +22,62 @@ while IFS='' read -r line; do tomls+=("$line"); done < <(find . -name Cargo.toml crates=( solana-account-decoder solana-banks-client + solana-banks-interface solana-banks-server - solana-bpf-loader-program + solana-bloom + solana-bucket-map solana-clap-utils solana-clap-v3-utils solana-cli-config solana-cli-output solana-client + solana-connection-cache solana-core + solana-entry + solana-faucet + solana-frozen-abi + solana-frozen-abi-macro + solana-geyser-plugin-interface + solana-geyser-plugin-manager + solana-gossip + solana-ledger solana-logger - solana-notifier - solana-program + solana-measure + solana-merkle-tree + solana-metrics + solana-net-utils + solana-perf + solana-poh + solana-program-runtime solana-program-test + solana-address-lookup-table-program + solana-bpf-loader-program + solana-compute-budget-program + solana-config-program + solana-stake-program + solana-vote-program + solana-zk-token-proof-program + solana-pubsub-client + solana-quic-client + solana-rayon-threadlimit solana-remote-wallet + solana-rpc + solana-rpc-client + solana-rpc-client-api + solana-rpc-client-nonce-utils solana-runtime solana-sdk - solana-stake-program + solana-sdk-macro + solana-program + solana-send-transaction-service + solana-storage-bigtable + solana-storage-proto + solana-streamer solana-test-validator + solana-thin-client + solana-tpu-client solana-transaction-status - solana-vote-program + solana-udp-client solana-version solana-zk-token-sdk ) diff --git a/utils/test-client/Cargo.toml b/utils/test-client/Cargo.toml index 814fc30ca6c..da4097af24d 100644 --- a/utils/test-client/Cargo.toml +++ b/utils/test-client/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" # Used to ensure that SPL programs are buildable by external clients [dependencies] -solana-sdk = "1.16.1" +solana-sdk = "1.17.2" spl-memo = { path = "../../memo/program", features = [ "no-entrypoint" ] } spl-token = { path = "../../token/program", features = [ "no-entrypoint" ] } spl-token-swap = { path = "../../token-swap/program", features = [ "no-entrypoint" ] }