diff --git a/.github/actions/build-program/action.yaml b/.github/actions/build-program/action.yaml index a318c2a15..cbecb40fe 100644 --- a/.github/actions/build-program/action.yaml +++ b/.github/actions/build-program/action.yaml @@ -7,25 +7,25 @@ inputs: runs: using: "composite" steps: - - uses: actions/cache@v2 - name: Cache Cargo registry + index - id: cache-cargo-registry - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # name: Cache Cargo registry + index + # id: cache-cargo-registry + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry/index/ + # ~/.cargo/registry/cache/ + # ~/.cargo/git/db/ + # key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - - name: Cache Anchor Build - uses: actions/cache@v2 - id: cache-anchor-build - with: - path: | - ./target/ - key: build-${{ runner.os }}-${{env.ANCHOR_CLI_VERSION}}-${{env.ANCHOR_SHA}}-v0002-${{ hashFiles('./programs/**/**', '**/Cargo.lock') }}-${{ inputs.program_lib_name }} + # - name: Cache Anchor Build + # uses: actions/cache@v2 + # id: cache-anchor-build + # with: + # path: | + # ./target/ + # key: build-${{ runner.os }}-${{env.ANCHOR_CLI_VERSION}}-${{env.ANCHOR_SHA}}-v0002-${{ hashFiles('./programs/**/**', '**/Cargo.lock') }}-${{ inputs.program_lib_name }} - run: ./scripts/build-program.sh ${{ inputs.program_lib_name }} mainnet - if: steps.cache-anchor-build.outputs.cache-hit != 'true' + # if: steps.cache-anchor-build.outputs.cache-hit != 'true' shell: bash diff --git a/.github/actions/build-verifiable-program/action.yaml b/.github/actions/build-verifiable-program/action.yaml index c827ea693..a8d15b2bd 100644 --- a/.github/actions/build-verifiable-program/action.yaml +++ b/.github/actions/build-verifiable-program/action.yaml @@ -13,30 +13,29 @@ inputs: runs: using: "composite" steps: - - uses: actions/cache@v2 - name: Cache Cargo registry + index - id: cache-cargo-registry - with: - path: | - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-${{ runner.os }}-v0001-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # name: Cache Cargo registry + index + # id: cache-cargo-registry + # with: + # path: | + # ~/.cargo/registry/index/ + # ~/.cargo/registry/cache/ + # ~/.cargo/git/db/ + # key: cargo-${{ runner.os }}-v0001-${{ hashFiles('**/Cargo.lock') }} - - uses: actions/cache@v2 - name: Cache Solana Verify - id: cache-solana-verify - with: - path: | - ~/.cargo/bin/solana-verify - key: cargo-${{ runner.os }}-solana-verify + # - uses: actions/cache@v2 + # name: Cache Solana Verify + # id: cache-solana-verify + # with: + # path: | + # ~/.cargo/bin/solana-verify + # key: cargo-${{ runner.os }}-solana-verify - run: cargo install solana-verify --git https://github.com/Ellipsis-Labs/solana-verifiable-build --rev 8f6f56908a0b0f35cb84d06f167c25c286ccf0ac - if: steps.cache-solana-verify.outputs.cache-hit != 'true' + # if: steps.cache-solana-verify.outputs.cache-hit != 'true' shell: bash - run: ./scripts/build-program-verifiable.sh ${{ inputs.program_lib_name }} ${{ inputs.devnet == 'true' && 'devnet' || 'mainnet' }} shell: bash env: PROGRAM: ${{ inputs.program_lib_name }} - \ No newline at end of file diff --git a/.github/actions/build-workspace/action.yaml b/.github/actions/build-workspace/action.yaml index 76462b55b..399339df3 100644 --- a/.github/actions/build-workspace/action.yaml +++ b/.github/actions/build-workspace/action.yaml @@ -3,25 +3,25 @@ description: "Build Anchor Workspace" runs: using: "composite" steps: - - uses: actions/cache@v2 - name: Cache Cargo registry + index - id: cache-cargo-registry - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # name: Cache Cargo registry + index + # id: cache-cargo-registry + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry/index/ + # ~/.cargo/registry/cache/ + # ~/.cargo/git/db/ + # key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - - name: Cache Anchor Build - uses: actions/cache@v2 - id: cache-anchor-build - with: - path: | - ./target/ - key: build-${{ runner.os }}-${{env.ANCHOR_CLI_VERSION}}-${{env.ANCHOR_SHA}}-v0002-${{ hashFiles('./programs/**/**', '**/Cargo.lock') }}-workspace + # - name: Cache Anchor Build + # uses: actions/cache@v2 + # id: cache-anchor-build + # with: + # path: | + # ./target/ + # key: build-${{ runner.os }}-${{env.ANCHOR_CLI_VERSION}}-${{env.ANCHOR_SHA}}-v0002-${{ hashFiles('./programs/**/**', '**/Cargo.lock') }}-workspace - run: ./scripts/build-workspace.sh - if: steps.cache-anchor-build.outputs.cache-hit != 'true' + # if: steps.cache-anchor-build.outputs.cache-hit != 'true' shell: bash diff --git a/.github/actions/setup-anchor-cli/action.yaml b/.github/actions/setup-anchor-cli/action.yaml index e1152ced9..cf24a4f23 100644 --- a/.github/actions/setup-anchor-cli/action.yaml +++ b/.github/actions/setup-anchor-cli/action.yaml @@ -6,23 +6,23 @@ runs: steps: - uses: ./.github/actions/setup-common/ - uses: ./.github/actions/setup-solana-cli/ - - uses: actions/cache@v2 - name: Cache Anchor Cli - id: cache-anchor-cli - with: - path: | - ~/.cargo/bin/anchor - key: anchor-cli-${{ runner.os }}-v0003-${{ env.ANCHOR_CLI_VERSION }}-${{ env.ANCHOR_SHA }} + # - uses: actions/cache@v2 + # name: Cache Anchor Cli + # id: cache-anchor-cli + # with: + # path: | + # ~/.cargo/bin/anchor + # key: anchor-cli-${{ runner.os }}-v0003-${{ env.ANCHOR_CLI_VERSION }}-${{ env.ANCHOR_SHA }}-${{ env.RUST_TOOLCHAIN }} - run: cargo install --git https://github.com/coral-xyz/anchor --tag "v$ANCHOR_CLI_VERSION" anchor-cli --locked shell: bash - if: steps.cache-anchor-cli.outputs.cache-hit != 'true' - - uses: actions/cache@v2 - name: Cache Toml Cli - id: cache-toml-cli - with: - path: | - ~/.cargo/bin/toml - key: toml-cli-${{ runner.os }}-v0002 + # if: steps.cache-anchor-cli.outputs.cache-hit != 'true' + # - uses: actions/cache@v2 + # name: Cache Toml Cli + # id: cache-toml-cli + # with: + # path: | + # ~/.cargo/bin/toml + # key: toml-cli-${{ runner.os }}-v0002 - run: (cargo install toml-cli || true) - if: steps.cache-toml-cli.outputs.cache-hit != 'true' + # if: steps.cache-toml-cli.outputs.cache-hit != 'true' shell: bash diff --git a/.github/actions/setup-common/action.yaml b/.github/actions/setup-common/action.yaml index 1c66c9332..76abe554f 100644 --- a/.github/actions/setup-common/action.yaml +++ b/.github/actions/setup-common/action.yaml @@ -3,15 +3,20 @@ description: "Setup common" runs: using: "composite" steps: - - run: sudo apt-get update - shell: bash - - run: sudo apt-get install -y pkg-config build-essential libudev-dev - shell: bash - - uses: actions-rs/toolchain@v1 - name: Install minimal rust toolchain with clippy and rustfmt - with: - profile: minimal - toolchain: nightly-2023-04-19 - target: x86_64-unknown-linux-gnu - components: rustfmt, clippy - default: true + - run: sudo apt-get update + shell: bash + + - run: sudo apt-get install -y pkg-config build-essential libudev-dev + shell: bash + + - uses: actions-rs/toolchain@v1 + name: Install minimal rust toolchain with clippy and rustfmt + with: + profile: minimal + toolchain: ${{ env.RUST_TOOLCHAIN }} + target: x86_64-unknown-linux-gnu + components: rustfmt, clippy + default: true + + - run: (cargo install cargo-nextest || true) + shell: bash diff --git a/.github/actions/setup-solana-cli/action.yaml b/.github/actions/setup-solana-cli/action.yaml index 3db998fc3..649a67f2c 100644 --- a/.github/actions/setup-solana-cli/action.yaml +++ b/.github/actions/setup-solana-cli/action.yaml @@ -3,20 +3,23 @@ description: "Setup Solana CLI" runs: using: "composite" steps: - - uses: actions/cache@v2 - name: Cache Solana Tool Suite - id: cache-solana - with: - path: | - ~/.cache/solana/ - ~/.local/share/solana/ - key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }} - - run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)" - shell: bash - if: steps.cache-solana.outputs.cache-hit != 'true' - - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH - shell: bash - - run: solana-keygen new -s --no-bip39-passphrase --force - shell: bash - - run: solana config set --url localhost - shell: bash + # - uses: actions/cache@v2 + # name: Cache Solana Tool Suite + # id: cache-solana + # with: + # path: | + # ~/.cache/solana/ + # ~/.local/share/solana/ + # key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }}-${{ env.RUST_TOOLCHAIN }} + - run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)" + shell: bash + # if: steps.cache-solana.outputs.cache-hit != 'true' + - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + shell: bash + - run: cargo install solana-keygen --version ${{ env.SOLANA_CLI_VERSION }} --locked + # if: steps.cache-solana-verify.outputs.cache-hit != 'true' + shell: bash + - run: solana-keygen new -s --no-bip39-passphrase --force + shell: bash + - run: solana config set --url localhost + shell: bash diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f1c49d696..3cbe64cbe 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,10 +14,10 @@ defaults: working-directory: . env: - RUST_TOOLCHAIN: 1.66.1 - SOLANA_CLI_VERSION: 1.16.20 - ANCHOR_CLI_VERSION: 0.29.0 - ANCHOR_SHA: fc9fd6d24b9be84abb2f40e47ed3faf7b11864ae + RUST_TOOLCHAIN: 1.75.0 + SOLANA_CLI_VERSION: 1.18.11 + ANCHOR_CLI_VERSION: 0.30.1 + ANCHOR_SHA: e6d7dafe12da661a36ad1b4f3b5970e8986e5321 CARGO_TERM_COLOR: always concurrency: @@ -28,8 +28,6 @@ jobs: lint: name: Rust Lint runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v3 @@ -37,20 +35,20 @@ jobs: - uses: ./.github/actions/setup-common/ - uses: ./.github/actions/setup-anchor-cli/ - - uses: actions/cache@v2 - name: Cache Cargo registry + index - id: cache-cargo-build - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # name: Cache Cargo registry + index + # id: cache-cargo-build + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry/index/ + # ~/.cargo/registry/cache/ + # ~/.cargo/git/db/ + # key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - run: cargo fmt -- --check - - run: cargo clippy --features=test,test-bpf,admin -- -D warnings -A unused-imports -A clippy::result_large_err -A clippy::await_holding_refcell_ref -A clippy::comparison_chain -A clippy::bind_instead_of_map -A clippy::to-string-trait-impl + - run: ./scripts/lint.sh test-unit: name: Rust Unit Tests @@ -63,24 +61,22 @@ jobs: - uses: ./.github/actions/setup-common/ - uses: ./.github/actions/setup-anchor-cli/ - - uses: actions/cache@v2 - name: Cache Cargo registry + index - id: cache-cargo-build - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + # - uses: actions/cache@v2 + # name: Cache Cargo registry + index + # id: cache-cargo-build + # with: + # path: | + # ~/.cargo/bin/ + # ~/.cargo/registry/index/ + # ~/.cargo/registry/cache/ + # ~/.cargo/git/db/ + # key: cargo-${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - run: cargo test --lib - test-programs: - name: Build and Test Anchor Programs + build-and-test-workspace: + name: Build And Test Anchor Programs runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v2 @@ -90,34 +86,31 @@ jobs: - uses: ./.github/actions/build-workspace/ - - run: ./scripts/test-program.sh marginfi - shell: bash - - - run: ./scripts/test-program.sh liquidity-incentive-program + - run: ./scripts/test-program.sh all --sane shell: bash fuzz: name: Fuzz The marginfi Program runs-on: ubuntu-latest - env: - RUST_TOOLCHAIN: 1.77.1 - RUSTUP_TOOLCHAIN: nightly-2024-03-26 + defaults: + run: + shell: bash + working-directory: ./programs/marginfi/fuzz steps: - uses: actions/checkout@v3 - - name: cache dependencies - uses: Swatinem/rust-cache@v2 + # - name: cache dependencies + # uses: Swatinem/rust-cache@v2 - name: Install full rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2024-03-26 + toolchain: nightly-2024-06-05 components: rust-src - override: true - name: Run fuzz tests in fuzz dir run: | - cd programs/marginfi + python ./generate_corpus.py cargo install cargo-fuzz --locked - cargo fuzz run lend -Zbuild-std --strip-dead-code --no-cfg-fuzzing -- -max_total_time=300 + cargo +nightly-2024-06-05 fuzz run lend -Zbuild-std --strip-dead-code --no-cfg-fuzzing -- -max_total_time=300 - name: Pass after fuzzing run: echo "Fuzzing completed" diff --git a/.gitignore b/.gitignore index 642c60846..07e6e2c18 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ node_modules .idea/ test-ledger/ *.profraw + +# keypairs +*.json \ No newline at end of file diff --git a/Anchor.toml b/Anchor.toml index 45df23e37..ea06333a6 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,17 +1,18 @@ [toolchain] -solana_version = "1.16.23" +anchor_version = "0.30.1" +solana_version = "1.18.11" [features] -seeds = true +resolution = true skip-lint = false [programs.localnet] -marginfi = "Mfi1111111111111111111111111111111111111111" liquidity_incentive_program = "Lip1111111111111111111111111111111111111111" +marginfi = "Mfi1111111111111111111111111111111111111111" [programs.mainnet] -marginfi = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" liquidity_incentive_program = "LipsxuAkFkwa4RKNzn51wAsW7Dedzt1RNHMkTkDEZUW" +marginfi = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" [registry] url = "https://api.apr.dev" @@ -23,6 +24,16 @@ wallet = "~/.config/solana/id.json" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" +[test] +startup_wait = 5000 +shutdown_wait = 2000 +upgradeable = false + +[test.validator] +bind_address = "0.0.0.0" +ledger = ".anchor/test-ledger" +rpc_port = 8899 + [[test.validator.account]] address = "8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN" filename = "tests/fixtures/localnet_usdc.json" diff --git a/Cargo.lock b/Cargo.lock index abada5149..226e95585 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -65,23 +65,23 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -119,89 +119,150 @@ dependencies = [ [[package]] name = "anchor-attribute-access-control" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa5be5b72abea167f87c868379ba3c2be356bfca9e6f474fd055fa0f7eeb4f2" +checksum = "e5f619f1d04f53621925ba8a2e633ba5a6081f2ae14758cbb67f38fd823e0a3e" dependencies = [ - "anchor-syn", - "anyhow", - "proc-macro2 1.0.79", - "quote 1.0.35", - "regex", + "anchor-syn 0.29.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-account" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f468970344c7c9f9d03b4da854fd7c54f21305059f53789d0045c1dd803f0018" +checksum = "e7f2a3e1df4685f18d12a943a9f2a7456305401af21a07c9fe076ef9ecd6e400" dependencies = [ - "anchor-syn", - "anyhow", - "bs58 0.5.0", - "proc-macro2 1.0.79", - "quote 1.0.35", - "rustversion", + "anchor-syn 0.29.0", + "bs58 0.5.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "bs58 0.5.1", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-constant" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59948e7f9ef8144c2aefb3f32a40c5fce2798baeec765ba038389e82301017ef" +checksum = "9423945cb55627f0b30903288e78baf6f62c6c8ab28fb344b6b25f1ffee3dca7" +dependencies = [ + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-syn", - "proc-macro2 1.0.79", + "anchor-syn 0.30.1", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-error" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc753c9d1c7981cb8948cf7e162fb0f64558999c0413058e2d43df1df5448086" +checksum = "93ed12720033cc3c3bf3cfa293349c2275cd5ab99936e33dd4bf283aaad3e241" +dependencies = [ + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-syn", - "proc-macro2 1.0.79", - "quote 1.0.35", + "anchor-syn 0.30.1", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-event" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38b4e172ba1b52078f53fdc9f11e3dc0668ad27997838a0aad2d148afac8c97" +checksum = "eef4dc0371eba2d8c8b54794b0b0eb786a234a559b77593d6f80825b6d2c77a2" dependencies = [ - "anchor-syn", - "anyhow", - "proc-macro2 1.0.79", - "quote 1.0.35", + "anchor-syn 0.29.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-program" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eebd21543606ab61e2d83d9da37d24d3886a49f390f9c43a1964735e8c0f0d5" +checksum = "b18c4f191331e078d4a6a080954d1576241c29c56638783322a18d308ab27e4f" +dependencies = [ + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-syn", + "anchor-lang-idl", + "anchor-syn 0.30.1", "anyhow", - "proc-macro2 1.0.79", - "quote 1.0.35", + "bs58 0.5.1", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde_json", "syn 1.0.109", ] [[package]] name = "anchor-client" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434a6bf33efba0c93157f7fa2fafac658cb26ab75396886dcedd87c2a8ad445" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-lang", + "anchor-lang 0.30.1", "anyhow", "futures", "regex", @@ -216,76 +277,201 @@ dependencies = [ [[package]] name = "anchor-derive-accounts" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4720d899b3686396cced9508f23dab420f1308344456ec78ef76f98fda42af" +checksum = "5de10d6e9620d3bcea56c56151cad83c5992f50d5960b3a9bebc4a50390ddc3c" dependencies = [ - "anchor-syn", - "anyhow", - "proc-macro2 1.0.79", - "quote 1.0.35", + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e2e5be518ec6053d90a2a7f26843dbee607583c779e6c8395951b9739bdfbe" +dependencies = [ + "anchor-syn 0.29.0", + "borsh-derive-internal 0.10.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "borsh-derive-internal 0.10.3", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-derive-space" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f495e85480bd96ddeb77b71d499247c7d4e8b501e75ecb234e9ef7ae7bd6552a" +checksum = "1ecc31d19fa54840e74b7a979d44bcea49d70459de846088a1d71e87ba53c419" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-lang" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d4b20100f1310a774aba3471ef268e5c4ba4d5c28c0bbe663c2658acbc414" -dependencies = [ - "anchor-attribute-access-control", - "anchor-attribute-account", - "anchor-attribute-constant", - "anchor-attribute-error", - "anchor-attribute-event", - "anchor-attribute-program", - "anchor-derive-accounts", - "anchor-derive-space", +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35da4785497388af0553586d55ebdc08054a8b1724720ef2749d313494f2b8ad" +dependencies = [ + "anchor-attribute-access-control 0.29.0", + "anchor-attribute-account 0.29.0", + "anchor-attribute-constant 0.29.0", + "anchor-attribute-error 0.29.0", + "anchor-attribute-event 0.29.0", + "anchor-attribute-program 0.29.0", + "anchor-derive-accounts 0.29.0", + "anchor-derive-serde 0.29.0", + "anchor-derive-space 0.29.0", "arrayref", "base64 0.13.1", "bincode", "borsh 0.10.3", "bytemuck", - "getrandom 0.2.11", + "getrandom 0.2.15", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-lang" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-attribute-access-control 0.30.1", + "anchor-attribute-account 0.30.1", + "anchor-attribute-constant 0.30.1", + "anchor-attribute-error 0.30.1", + "anchor-attribute-event 0.30.1", + "anchor-attribute-program 0.30.1", + "anchor-derive-accounts 0.30.1", + "anchor-derive-serde 0.30.1", + "anchor-derive-space 0.30.1", + "anchor-lang-idl", + "arrayref", + "base64 0.21.7", + "bincode", + "borsh 0.10.3", + "bytemuck", + "getrandom 0.2.15", "solana-program", "thiserror", ] +[[package]] +name = "anchor-lang-idl" +version = "0.1.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck 0.3.3", + "regex", + "serde", + "serde_json", + "sha2 0.10.8", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anyhow", + "serde", +] + [[package]] name = "anchor-spl" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f860599da1c2354e7234c768783049eb42e2f54509ecfc942d2e0076a2da7b" +checksum = "6c4fd6e43b2ca6220d2ef1641539e678bfc31b6cc393cf892b373b5997b6a39a" dependencies = [ - "anchor-lang", + "anchor-lang 0.29.0", "solana-program", - "spl-associated-token-account 1.1.3", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-associated-token-account 2.3.0", + "spl-token", + "spl-token-2022 0.9.0", +] + +[[package]] +name = "anchor-spl" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-lang 0.30.1", + "mpl-token-metadata", + "spl-associated-token-account 3.0.2", + "spl-pod 0.2.2", + "spl-token", + "spl-token-2022 3.0.2", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", ] [[package]] name = "anchor-syn" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a125e4b0cc046cfec58f5aa25038e34cf440151d58f0db3afc55308251fe936d" +checksum = "d9101b84702fed2ea57bd22992f75065da5648017135b844283a2f6d74f27825" +dependencies = [ + "anyhow", + "bs58 0.5.1", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.8", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anyhow", - "bs58 0.5.0", + "bs58 0.5.1", + "cargo_toml", "heck 0.3.3", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "serde", "serde_json", "sha2 0.10.8", @@ -319,9 +505,23 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "aquamarine" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" +dependencies = [ + "include_dir", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] name = "ark-bn254" @@ -364,7 +564,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version", @@ -377,7 +577,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.35", + "quote", "syn 1.0.109", ] @@ -387,10 +587,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -416,7 +616,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint 0.4.4", + "num-bigint 0.4.6", ] [[package]] @@ -425,8 +625,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -440,12 +640,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" @@ -486,8 +680,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", "synstructure", ] @@ -498,8 +692,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -522,9 +716,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.5" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" dependencies = [ "brotli", "flate2", @@ -543,37 +737,15 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", -] - [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -603,18 +775,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "autotools" -version = "0.2.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8da1805e028a172334c3b680f93e71126f2327622faef2ec3d893c0a4ad77" -dependencies = [ - "cc", -] +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-creds" @@ -642,76 +805,17 @@ dependencies = [ "thiserror", ] -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "az" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "futures-core", - "getrandom 0.2.11", - "instant", - "pin-project-lite", - "rand 0.8.5", - "tokio", -] - [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -736,9 +840,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -763,9 +867,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitmaps" @@ -776,11 +883,23 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", @@ -835,6 +954,16 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive 1.5.1", + "cfg_aliases", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -844,7 +973,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.79", + "proc-macro2", "syn 1.0.109", ] @@ -857,18 +986,32 @@ dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.79", + "proc-macro2", "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -878,8 +1021,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -889,8 +1032,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -900,16 +1043,24 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] +[[package]] +name = "brick" +version = "0.1.0" +dependencies = [ + "anchor-lang 0.30.1", + "solana-program", +] + [[package]] name = "brotli" -version = "3.4.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -918,9 +1069,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -934,18 +1085,18 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "tinyvec", ] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bv" @@ -958,36 +1109,58 @@ dependencies = [ ] [[package]] -name = "bytemuck" -version = "1.14.0" +name = "bytecheck" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ - "bytemuck_derive", + "bytecheck_derive", + "ptr_meta", + "simdutf8", ] [[package]] -name = "bytemuck_derive" -version = "1.5.0" +name = "bytecheck_derive" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "byteorder" +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2" @@ -1020,14 +1193,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cargo_toml" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" +dependencies = [ + "serde", + "toml 0.8.14", +] + [[package]] name = "cc" -version = "1.0.83" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -1036,11 +1220,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1048,7 +1238,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1060,28 +1250,6 @@ dependencies = [ "chrono", ] -[[package]] -name = "chrono-tz" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - [[package]] name = "cipher" version = "0.3.0" @@ -1120,7 +1288,7 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.0", + "textwrap 0.16.1", ] [[package]] @@ -1131,8 +1299,8 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1160,24 +1328,24 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -1214,9 +1382,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1224,70 +1392,61 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1340,9 +1499,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -1350,45 +1509,48 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.79", - "quote 1.0.35", - "strsim 0.10.0", - "syn 2.0.55", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.58", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", - "quote 1.0.35", - "syn 2.0.55", + "quote", + "syn 2.0.58", ] [[package]] name = "dashmap" -version = "4.0.2" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "num_cpus", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", "rayon", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -1408,16 +1570,16 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -1435,8 +1597,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1458,6 +1620,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -1480,9 +1648,9 @@ dependencies = [ [[package]] name = "dir-diff" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b" +checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" dependencies = [ "walkdir", ] @@ -1530,36 +1698,36 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[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", ] [[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.58", ] [[package]] @@ -1569,16 +1737,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] -name = "dotenv" -version = "0.15.0" +name = "downcast" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "eager" @@ -1628,16 +1796,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -1647,31 +1815,31 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "enum-iterator" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7add3873b5dd076766ee79c8e406ad1a472c385476b9e38849f8eec24f1be689" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" dependencies = [ "enum-iterator-derive", ] [[package]] name = "enum-iterator-derive" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -1680,23 +1848,23 @@ version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -1713,36 +1881,26 @@ dependencies = [ ] [[package]] -name = "envconfig" -version = "0.10.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea81cc7e21f55a9d9b1efb6816904978d0bfbe31a50347cb24b2e75564bcac9b" -dependencies = [ - "envconfig_derive", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "envconfig_derive" -version = "0.10.0" +name = "erased-serde" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfca278e5f84b45519acaaff758ebfa01f18e96998bc24b8f1b722dd804b9bf" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", + "serde", + "typeid", ] -[[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.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1754,11 +1912,20 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fast-math" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2465292146cdfc2011350fe3b1c616ac83cf0faeedb33463ba1c332ed8948d66" +dependencies = [ + "ieee754", +] + [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "feature-probe" @@ -1768,21 +1935,21 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] name = "fixed" -version = "1.23.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79386fdcec5e0fde91b1a6a5bcd89677d1f9304f7f986b154a1b9109038854d9" +checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", @@ -1810,8 +1977,8 @@ dependencies = [ "fixed", "paste", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1825,22 +1992,25 @@ dependencies = [ "fixed-macro-impl", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1871,11 +2041,23 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1888,9 +2070,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1898,15 +2080,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1915,38 +2097,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1960,29 +2142,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gcp-bigquery-client" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb67dedb3fce3a6d25829158e40b22c5e9aa107fdbae0f2fef564931c9161a6a" -dependencies = [ - "async-stream", - "async-trait", - "dyn-clone", - "hyper", - "hyper-rustls", - "log", - "reqwest", - "serde", - "serde_json", - "thiserror", - "time", - "tokio", - "tokio-stream", - "url", - "yup-oauth2", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -2019,9 +2178,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -2032,9 +2191,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "goblin" @@ -2047,103 +2206,11 @@ dependencies = [ "scroll", ] -[[package]] -name = "google-cloud-auth" -version = "0.9.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "async-trait", - "base64 0.21.5", - "google-cloud-metadata", - "google-cloud-token", - "home", - "jsonwebtoken", - "reqwest", - "serde", - "serde_json", - "thiserror", - "time", - "tokio", - "tracing", - "urlencoding", -] - -[[package]] -name = "google-cloud-default" -version = "0.1.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "async-trait", - "google-cloud-auth", - "google-cloud-gax", - "google-cloud-pubsub", -] - -[[package]] -name = "google-cloud-gax" -version = "0.13.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "google-cloud-token", - "http", - "thiserror", - "tokio", - "tokio-retry", - "tokio-util 0.7.10", - "tonic 0.8.3", - "tower", - "tracing", -] - -[[package]] -name = "google-cloud-googleapis" -version = "0.7.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", - "tonic 0.8.3", -] - -[[package]] -name = "google-cloud-metadata" -version = "0.3.2" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "reqwest", - "thiserror", - "tokio", -] - -[[package]] -name = "google-cloud-pubsub" -version = "0.13.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "async-channel", - "async-stream", - "google-cloud-gax", - "google-cloud-googleapis", - "google-cloud-token", - "prost-types 0.11.9", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "google-cloud-token" -version = "0.1.0" -source = "git+https://github.com/mrgnlabs/google-cloud-rust.git?rev=3f651f2d9fd8cca547bb11490d2575d9bf90f994#3f651f2d9fd8cca547bb11490d2575d9bf90f994" -dependencies = [ - "async-trait", -] - [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -2154,16 +2221,17 @@ dependencies = [ "indexmap 2.2.6", "slab", "tokio", - "tokio-util 0.7.10", + "tokio-util 0.7.11", "tracing", ] [[package]] name = "half" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ + "cfg-if", "crunchy", ] @@ -2182,7 +2250,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -2191,7 +2259,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -2200,14 +2268,14 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -2235,9 +2303,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -2284,20 +2352,11 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -2306,9 +2365,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -2317,9 +2376,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2335,9 +2394,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -2350,7 +2409,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -2366,23 +2425,9 @@ dependencies = [ "futures-util", "http", "hyper", - "log", - "rustls 0.21.9", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", + "rustls", "tokio", - "tokio-io-timeout", + "tokio-rustls", ] [[package]] @@ -2400,9 +2445,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2437,6 +2482,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ieee754" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" + [[package]] name = "im" version = "15.1.0" @@ -2454,10 +2505,29 @@ dependencies = [ ] [[package]] -name = "index_list" -version = "0.2.11" +name = "include_dir" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70891286cb8e844fdfcf1178b47569699f9e20b5ecc4b45a6240a64771444638" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "index_list" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb725b6505e51229de32027e0cfcd9db29da4d89156f9747b0a5195643fa3e1" [[package]] name = "indexmap" @@ -2476,14 +2546,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -2494,9 +2564,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -2518,34 +2588,28 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" - [[package]] name = "jsonrpc-core" version = "18.0.0" @@ -2562,49 +2626,43 @@ dependencies = [ ] [[package]] -name = "jsonwebtoken" -version = "8.3.0" +name = "keccak" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ - "base64 0.21.5", - "pem", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", + "cpufeatures", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "kv-log-macro" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ - "cpufeatures", + "log", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "libc", - "redox_syscall 0.4.1", ] [[package]] @@ -2617,15 +2675,34 @@ dependencies = [ "base64 0.12.3", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.2.2", + "libsecp256k1-gen-ecmult 0.2.1", + "libsecp256k1-gen-genmult 0.2.1", "rand 0.7.3", "serde", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core 0.3.0", + "libsecp256k1-gen-ecmult 0.3.0", + "libsecp256k1-gen-genmult 0.3.0", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + [[package]] name = "libsecp256k1-core" version = "0.2.2" @@ -2637,13 +2714,33 @@ dependencies = [ "subtle", ] +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + [[package]] name = "libsecp256k1-gen-ecmult" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0", ] [[package]] @@ -2652,21 +2749,42 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0", +] + +[[package]] +name = "light-poseidon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +dependencies = [ + "ark-bn254", + "ark-ff", + "num-bigint 0.4.6", + "thiserror", ] [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "liquidity-incentive-program" version = "0.1.0" dependencies = [ - "anchor-lang", - "anchor-spl", + "anchor-lang 0.30.1", + "anchor-spl 0.30.1", "anyhow", "assert_matches", "bincode", @@ -2687,6 +2805,7 @@ name = "llama-snapshot-tool" version = "0.1.0" dependencies = [ "anchor-client", + "anchor-spl 0.30.1", "anyhow", "bytemuck", "clap 3.2.25", @@ -2704,15 +2823,14 @@ dependencies = [ "solana-account-decoder", "solana-client", "solana-sdk", - "spl-token 4.0.0", "tokio", ] [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2720,9 +2838,12 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] [[package]] name = "lru" @@ -2735,9 +2856,9 @@ dependencies = [ [[package]] name = "lz4" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +checksum = "d6eab492fe7f8651add23237ea56dbf11b3c4ff762ab83d40a47f11433421f91" dependencies = [ "libc", "lz4-sys", @@ -2745,9 +2866,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" dependencies = [ "cc", "libc", @@ -2757,12 +2878,14 @@ dependencies = [ name = "marginfi" version = "0.1.0" dependencies = [ - "anchor-lang", - "anchor-spl", + "anchor-lang 0.29.0", + "anchor-lang 0.30.1", + "anchor-spl 0.30.1", "anyhow", "assert_matches", - "base64 0.21.5", + "base64 0.21.7", "bincode", + "borsh 0.10.3", "bytemuck", "cfg-if", "enum_dispatch", @@ -2770,8 +2893,10 @@ dependencies = [ "fixed-macro", "futures", "lazy_static", + "mpl-token-metadata", "pretty_assertions", "pyth-sdk-solana", + "pyth-solana-receiver-sdk", "rust_decimal", "serde", "serde_json", @@ -2782,8 +2907,12 @@ dependencies = [ "solana-program-test", "solana-sdk", "solana-security-txt", + "spl-tlv-account-resolution 0.6.3", + "spl-token", + "spl-transfer-hook-interface 0.6.3", "static_assertions", - "switchboard-v2", + "switchboard-solana", + "test-case", "test-utilities", "type-layout", ] @@ -2793,9 +2922,10 @@ name = "marginfi-v2-cli" version = "0.1.0" dependencies = [ "anchor-client", - "anchor-spl", + "anchor-spl 0.30.1", "anyhow", "bincode", + "borsh 0.10.3", "bs58 0.4.0", "bytemuck", "chrono", @@ -2804,10 +2934,13 @@ dependencies = [ "env_logger", "fixed", "fixed-macro", + "hex", "liquidity-incentive-program", "log", "marginfi", "pyth-sdk-solana", + "pyth-solana-receiver-sdk", + "rand 0.8.5", "serde", "serde_json", "shellexpand", @@ -2815,96 +2948,21 @@ dependencies = [ "solana-address-lookup-table-program", "solana-client", "solana-sdk", - "spl-associated-token-account 2.2.0", - "spl-token 4.0.0", - "switchboard-v2", + "spl-associated-token-account 2.3.0", + "spl-token", + "switchboard-solana", "type-layout", ] -[[package]] -name = "marginfi-v2-indexer" -version = "0.1.0" -dependencies = [ - "anchor-client", - "anyhow", - "backoff", - "base64 0.21.5", - "bincode", - "bs58 0.4.0", - "bytemuck", - "bytes", - "chrono", - "chrono-tz", - "clap 3.2.25", - "concurrent-queue", - "dotenv", - "envconfig", - "fixed", - "fixed-macro", - "futures", - "gcp-bigquery-client", - "google-cloud-auth", - "google-cloud-default", - "google-cloud-gax", - "google-cloud-googleapis", - "google-cloud-pubsub", - "itertools", - "json", - "lazy_static", - "marginfi", - "prost 0.11.9", - "prost-derive 0.11.9", - "protobuf-src", - "pyth-sdk-solana", - "rayon", - "serde", - "serde_json", - "serde_yaml", - "solana-account-decoder", - "solana-client", - "solana-measure", - "solana-metrics", - "solana-sdk", - "solana-transaction-status", - "spl-token 4.0.0", - "thiserror", - "tokio", - "tokio-stream", - "tonic 0.8.3", - "tonic-build 0.8.4", - "tracing", - "tracing-stackdriver", - "tracing-subscriber", - "uuid", - "yellowstone-grpc-client", - "yellowstone-grpc-proto", - "yup-oauth2", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "maybe-async" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -2915,9 +2973,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2948,9 +3006,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -2990,24 +3048,51 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "modular-bitfield" version = "0.11.2" @@ -3024,24 +3109,30 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] -name = "multimap" -version = "0.8.3" +name = "mpl-token-metadata" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "caf0f61b553e424a6234af1268456972ee66c2222e1da89079242251fa7479e5" +dependencies = [ + "borsh 0.10.3", + "num-derive 0.3.3", + "num-traits", + "solana-program", + "thiserror", +] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -3077,14 +3168,10 @@ dependencies = [ ] [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "normalize-line-endings" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num" @@ -3113,11 +3200,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -3132,43 +3218,48 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -3189,9 +3280,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -3202,19 +3293,10 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - [[package]] name = "num_enum" version = "0.6.1" @@ -3226,23 +3308,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" -dependencies = [ - "num_enum_derive 0.7.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", + "num_enum_derive 0.7.2", ] [[package]] @@ -3252,30 +3322,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "num_enum_derive" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" -dependencies = [ - "proc-macro-crate 2.0.0", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", -] - -[[package]] -name = "num_threads" -version = "0.1.6" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "libc", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -3286,9 +3347,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.1" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] @@ -3304,23 +3365,23 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -3335,9 +3396,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -3348,9 +3409,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -3411,22 +3472,16 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3434,31 +3489,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" -dependencies = [ - "regex", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -3503,90 +3549,42 @@ dependencies = [ ] [[package]] -name = "petgraph" -version = "0.6.4" +name = "pin-project" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "fixedbitset", - "indexmap 2.2.6", + "pin-project-internal", ] [[package]] -name = "phf" -version = "0.11.2" +name = "pin-project-internal" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "phf_codegen" -version = "0.11.2" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "phf_generator" -version = "0.11.2" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "phf_shared" -version = "0.11.2" +name = "pkcs8" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" dependencies = [ "der", "spki", @@ -3595,9 +3593,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plain" @@ -3619,9 +3617,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "powerfmt" @@ -3636,39 +3634,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "pretty-hex" -version = "0.3.0" +name = "predicates" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] [[package]] -name = "pretty_assertions" -version = "1.4.0" +name = "predicates-core" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] -name = "prettyplease" -version = "0.1.25" +name = "predicates-tree" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ - "proc-macro2 1.0.79", - "syn 1.0.109", + "predicates-core", + "termtree", ] [[package]] -name = "prettyplease" -version = "0.2.15" +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "proc-macro2 1.0.79", - "syn 2.0.55", + "diff", + "yansi", ] [[package]] @@ -3677,7 +3685,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -3692,11 +3700,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.20.7", + "toml_edit 0.21.1", ] [[package]] @@ -3706,8 +3714,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", "version_check", ] @@ -3718,20 +3726,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "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.79" @@ -3742,122 +3741,25 @@ dependencies = [ ] [[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive 0.12.3", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-build" -version = "0.12.3" +name = "ptr_meta" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ - "bytes", - "heck 0.4.1", - "itertools", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease 0.2.15", - "prost 0.12.3", - "prost-types 0.12.3", - "regex", - "syn 2.0.55", - "tempfile", - "which", + "ptr_meta_derive", ] [[package]] -name = "prost-derive" -version = "0.11.9" +name = "ptr_meta_derive" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "anyhow", - "itertools", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", -] - -[[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 = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost 0.12.3", -] - -[[package]] -name = "protobuf-src" -version = "1.1.0+21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ac8852baeb3cc6fb83b93646fb93c0ffe5d14bf138c945ceb4b9948ee0e3c1" -dependencies = [ - "autotools", -] - [[package]] name = "pyth-sdk" version = "0.8.0" @@ -3866,7 +3768,7 @@ checksum = "1e7aeef4d5f0a9c98ff5af2ddd84a8b89919c512188305b497a9eb9afa97a949" dependencies = [ "borsh 0.10.3", "borsh-derive 0.10.3", - "getrandom 0.2.11", + "getrandom 0.2.15", "hex", "schemars", "serde", @@ -3874,9 +3776,9 @@ dependencies = [ [[package]] name = "pyth-sdk-solana" -version = "0.8.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa571ea6ea51102b8fc03303d0e6fea4f788f77bb4e0d65ae2d3c5e384e3187" +checksum = "7f913de6eb29d8def199af3beaee645e84c5281327d58777eff3fdd9f1d37105" dependencies = [ "borsh 0.10.3", "borsh-derive 0.10.3", @@ -3889,6 +3791,40 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pyth-solana-receiver-sdk" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e6559643f0b377b6f293269251f6a804ae7332c37f7310371f50c833453cd0" +dependencies = [ + "anchor-lang 0.29.0", + "hex", + "pythnet-sdk", + "solana-program", +] + +[[package]] +name = "pythnet-sdk" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbbc0456f9f27c9ad16b6c3bf1b2a7fea61eebf900f4d024a0468b9a84fe0c1" +dependencies = [ + "anchor-lang 0.29.0", + "bincode", + "borsh 0.10.3", + "bytemuck", + "byteorder", + "fast-math", + "hex", + "proc-macro2", + "rustc_version", + "serde", + "sha3 0.10.8", + "slow_primes", + "solana-program", + "thiserror", +] + [[package]] name = "qstring" version = "0.7.2" @@ -3898,6 +3834,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "qualifier_attr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "quick-xml" version = "0.26.0" @@ -3910,71 +3857,66 @@ dependencies = [ [[package]] name = "quinn" -version = "0.9.4" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.20.9", + "rustls", "thiserror", "tokio", "tracing", - "webpki", ] [[package]] name = "quinn-proto" -version = "0.9.6" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", "rand 0.8.5", "ring 0.16.20", "rustc-hash", - "rustls 0.20.9", + "rustls", "rustls-native-certs", "slab", "thiserror", "tinyvec", "tracing", - "webpki", ] [[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 0.4.10", + "socket2", "tracing", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 0.4.30", + "proc-macro2", ] [[package]] -name = "quote" -version = "1.0.35" +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2 1.0.79", -] +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" @@ -4035,7 +3977,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", ] [[package]] @@ -4058,9 +4000,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -4068,9 +4010,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4090,85 +4032,79 @@ dependencies = [ [[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 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "rend" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "async-compression", - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -4187,23 +4123,24 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.9", + "rustls", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.24.1", - "tokio-util 0.7.10", + "tokio-rustls", + "tokio-util 0.7.11", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", "winreg", ] @@ -4224,16 +4161,46 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -4302,20 +4269,25 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.26.1" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" dependencies = [ "arrayvec", + "borsh 1.5.1", + "bytes", "num-traits", + "rand 0.8.5", + "rkyv", "serde", + "serde_json", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -4343,37 +4315,25 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.6", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -4396,7 +4356,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -4405,15 +4365,15 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.6", + "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rxml" @@ -4434,9 +4394,9 @@ checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -4449,18 +4409,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -4470,14 +4430,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] @@ -4501,9 +4461,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -4512,7 +4472,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.6", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -4524,11 +4484,11 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4537,9 +4497,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -4547,55 +4507,73 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "seqlock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c67b6f14ecc5b86c66fa63d76b5092352678545a8a3cdae80aef5128371910" +dependencies = [ + "parking_lot", +] [[package]] name = "serde" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "indexmap 2.2.6", "itoa", @@ -4603,6 +4581,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4632,16 +4619,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap 2.2.6", "itoa", @@ -4651,10 +4638,10 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.10.1" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -4733,9 +4720,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -4747,16 +4734,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] -name = "simple_asn1" -version = "0.6.2" +name = "simdutf8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint 0.4.4", - "num-traits", - "thiserror", - "time", -] +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] name = "siphasher" @@ -4783,11 +4764,20 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slow_primes" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58267dd2fbaa6dceecba9e3e106d2d90a2b02497c0e8b01b8759beccf5113938" +dependencies = [ + "num", +] + [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smartstring" @@ -4802,32 +4792,22 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "solana-account-decoder" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "815b7eeb8cfc0cc27c3500658845bc0adbfb51a9212814af522f4912e1bdab2e" +checksum = "4973213a11c2e1b924b36e0c6688682b5aa4623f8d4eeaa1204c32cee524e6d6" dependencies = [ "Inflector", - "base64 0.21.5", + "base64 0.21.7", "bincode", "bs58 0.4.0", "bv", @@ -4835,26 +4815,87 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "solana-address-lookup-table-program", "solana-config-program", "solana-sdk", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", - "spl-token-metadata-interface", + "spl-token", + "spl-token-2022 1.0.0", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", "thiserror", "zstd", ] +[[package]] +name = "solana-accounts-db" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c06263320e399af20d46c8cebea7a1d5dc1bc56f31f8dfaacf7119576c48a7" +dependencies = [ + "arrayref", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "flate2", + "fnv", + "im", + "index_list", + "itertools", + "lazy_static", + "log", + "lz4", + "memmap2", + "modular-bitfield", + "num-derive 0.4.2", + "num-traits", + "num_cpus", + "num_enum 0.7.2", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "seqlock", + "serde", + "serde_derive", + "smallvec", + "solana-bucket-map", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-nohash-hasher", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", + "static_assertions", + "strum", + "strum_macros", + "tar", + "tempfile", + "thiserror", +] + [[package]] name = "solana-address-lookup-table-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9137f2199f70e082f15f91076f31fa6e67d98d40168de759feab12c6b60542a8" +checksum = "f4e57cb8f2e90361280b246f70bb7f5f86f4e4ff1ad5bbdfe18a81bea141f03a" dependencies = [ "bincode", "bytemuck", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "rustc_version", "serde", @@ -4868,11 +4909,11 @@ dependencies = [ [[package]] name = "solana-banks-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "785bca4464357f3d7aad38b3f57b1dcec5fc47f7eb3e0c1ffba907ce15aae66c" +checksum = "7c65a9540370523f3ade7190526309337cc50f1d742b3341dfa7357da3f59a56" dependencies = [ - "borsh 0.10.3", + "borsh 1.5.1", "futures", "solana-banks-interface", "solana-program", @@ -4885,9 +4926,9 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb13f6fff944f56df1c5b4bb708676efb856991bc368c969c436cc444e6f5e83" +checksum = "62b1dc20a7a71cf37bcbc2a3a5dfd73d7410a13850aa68d954a9c09e6a77e652" dependencies = [ "serde", "solana-sdk", @@ -4896,13 +4937,14 @@ dependencies = [ [[package]] name = "solana-banks-server" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "893926dda1a8d1594b55b753298df54fb9ed9ed1e802cac69ad3a225b7cdc63e" +checksum = "d449d55d3c5c3fe4c9f0c9f790a9feabe294f8ff0b4c6b771a20b2313ad8974a" dependencies = [ "bincode", "crossbeam-channel", "futures", + "solana-accounts-db", "solana-banks-interface", "solana-client", "solana-runtime", @@ -4915,15 +4957,15 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb0ed3691420f8ca6c7d58eb1335ecb0b73b1b6188039b7b631bcd0253b24377" +checksum = "6b1a55b8533f2dc716602e7c1b2bd555d5ac598ef6e80d28a517e6f31baf042e" dependencies = [ "bincode", "byteorder", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", - "rand 0.7.3", + "scopeguard", "solana-measure", "solana-program-runtime", "solana-sdk", @@ -4934,16 +4976,17 @@ dependencies = [ [[package]] name = "solana-bucket-map" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b64e2a282391cffdd2985971abf571922fd58db5f2743014f8a0428f719af2" +checksum = "fda213af7ae26ce249120f211060d2a85d87fe367c6490ee19b70845cbd320fc" dependencies = [ "bv", + "bytemuck", "log", "memmap2", "modular-bitfield", - "num_enum 0.6.1", - "rand 0.7.3", + "num_enum 0.7.2", + "rand 0.8.5", "solana-measure", "solana-sdk", "tempfile", @@ -4951,14 +4994,13 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a99a1aa397ec62a9b10f2f072fb95d78c6f4303bbca11d6af5f3f198e3d62e8" +checksum = "909f4553d0b31bb5b97533a6b64cc321a4eace9112d6efbabcf4408ea1b3f1db" dependencies = [ "chrono", "clap 2.34.0", "rpassword", - "solana-perf", "solana-remote-wallet", "solana-sdk", "thiserror", @@ -4969,9 +5011,9 @@ dependencies = [ [[package]] name = "solana-cli-config" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864dce71e749d7a523eca56f8ee22c35fe8dae4c182c50a4a9e7acd49313ac48" +checksum = "2242c4a0776cdaec1358d0ffc61b32131985a7b2210c491fa465d28c313eb880" dependencies = [ "dirs-next", "lazy_static", @@ -4985,12 +5027,12 @@ dependencies = [ [[package]] name = "solana-cli-output" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6338b80376eb99f4dbffed10a5f2efea2894704106f1a9c09891fab924770b" +checksum = "bada4ba96ef2f351363ba64ce4f592bc584ac48bb7d9da4e41303416b0a21026" dependencies = [ "Inflector", - "base64 0.21.5", + "base64 0.21.7", "chrono", "clap 2.34.0", "console", @@ -5007,24 +5049,24 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-vote-program", - "spl-memo 4.0.0", + "spl-memo", ] [[package]] name = "solana-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd1d1b50f6937ec5b7b05faa171956dc052ad593d058de5046e325cc0ec4c23" +checksum = "c5cc431df6cc1dd964134fa4ec7df765d3af3fae9c2148f96a3c4fb500290633" dependencies = [ "async-trait", "bincode", + "dashmap", "futures", "futures-util", - "indexmap 1.9.3", + "indexmap 2.2.6", "indicatif", "log", "quinn", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -5045,9 +5087,9 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674cbca707a6a38dc860cef40b988debb24e0347a04cd123bd2b05cb6f75eed4" +checksum = "9eb36ef3c3a1f38515c1ae0d255c4d6e5e635a856ac2aa1cd5f892b3db58e857" dependencies = [ "solana-program-runtime", "solana-sdk", @@ -5055,9 +5097,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7448528e2fd237e7d7ca93d4d8541a8a9f346b9f947405799d9a6dd5c22aa41c" +checksum = "e38b040d3a42e8f7d80c4a86bb0d49d7aed663b56b0fe0ae135d2d145fb7ae3a" dependencies = [ "bincode", "chrono", @@ -5069,16 +5111,17 @@ dependencies = [ [[package]] name = "solana-connection-cache" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51fe3a80fc59a93392a103e6ab492305a6ac614abee70cde6e34fe74fc55dd" +checksum = "ae02622c63943485f0af3d0896626eaf6478e734f0b6bc61c7cc5320963c6e75" dependencies = [ "async-trait", "bincode", + "crossbeam-channel", "futures-util", - "indexmap 1.9.3", + "indexmap 2.2.6", "log", - "rand 0.7.3", + "rand 0.8.5", "rayon", "rcgen", "solana-measure", @@ -5088,33 +5131,49 @@ dependencies = [ "tokio", ] +[[package]] +name = "solana-cost-model" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "838532d8437d00958621d2589d6033e9c69ea95cd0936efa8964146e49dcff53" +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-frozen-abi" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8e68a37635d475c40f026bfbc39df3298ce91ec0f4db848979b1dbcd9bc675" +checksum = "4867f66e9527fa44451c861c1dc6d9b2a7c7a668d7c6a297cdefbe39f4395b33" dependencies = [ - "ahash 0.8.6", - "blake3", "block-buffer 0.10.4", "bs58 0.4.0", "bv", - "byteorder", - "cc", "either", "generic-array", - "getrandom 0.1.16", "im", "lazy_static", "log", "memmap2", - "once_cell", - "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", "serde_derive", - "serde_json", "sha2 0.10.8", "solana-frozen-abi-macro", "subtle", @@ -5123,24 +5182,23 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ea45edfe53a4d95f18bd627f1b60e200611a436afd0c58c9c529c085af8965" +checksum = "168f24d97347b85f05192df58d6be3e3047a4aadc4001bc1b9e711a5ec878eea" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "rustc_version", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] name = "solana-loader-v4-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4091f5bc56ecd65473ad5ae4f0bae43a5ea26b916a824eaf74909ed0b0154a7d" +checksum = "98c426482234b7c267a5e0dfa8198442e1ffad2ad6c521f6b810949bc2719215" dependencies = [ "log", - "rand 0.7.3", "solana-measure", "solana-program-runtime", "solana-sdk", @@ -5149,9 +5207,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db83d89279b0620958ae1278fd52f340c68be79980a5f6ebfb3d4e4623d7241" +checksum = "a0511082fc62f2d086520fff5aa1917c389d8c840930c08ad255ae05952c08a2" dependencies = [ "env_logger", "lazy_static", @@ -5160,9 +5218,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6745b818b9d2d88b0011ac5532e3dcd4cde0bd1613464ee1bcb98db423ab97" +checksum = "be55a3df105431d25f86f2a7da0cbbde5f54c1f0782ca59367ea4a8037bc6797" dependencies = [ "log", "solana-sdk", @@ -5170,9 +5228,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b3782e709a4546a77354e6b0fbc176a34f19b420e65c0d9c9c48f93459fbab" +checksum = "ddec097ed7572804389195128dbd57958b427829153c6cd8ec3343c86fe3cd22" dependencies = [ "crossbeam-channel", "gethostname", @@ -5180,23 +5238,24 @@ dependencies = [ "log", "reqwest", "solana-sdk", + "thiserror", ] [[package]] name = "solana-net-utils" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea302ba1a7186826fecace83da4adce9b288e97ea370999a9aee2bfc71129b" +checksum = "258fa7c29fb7605b8d2ed89aa0d43c640d14f4147ad1f5b3fdad19a1ac145ca5" dependencies = [ "bincode", "clap 3.2.25", "crossbeam-channel", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_derive", - "socket2 0.4.10", + "socket2", "solana-logger", "solana-sdk", "solana-version", @@ -5204,27 +5263,35 @@ dependencies = [ "url", ] +[[package]] +name = "solana-nohash-hasher" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8a731ed60e89177c8a7ab05fe0f1511cedd3e70e773f288f9de33a9cfdc21e" + [[package]] name = "solana-perf" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a5d5de014354c112349667c51f80ce01bca0c6b0bfa027cbc069e972c1c0c7" +checksum = "ca422edcf16a6e64003ca118575ea641f7b750f14a0ad28c71dd84f33dcb912a" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "bincode", "bv", "caps", "curve25519-dalek", - "dlopen", - "dlopen_derive", + "dlopen2", "fnv", "lazy_static", "libc", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "rayon", + "rustc_version", "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", "solana-metrics", "solana-rayon-threadlimit", "solana-sdk", @@ -5233,21 +5300,21 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2560d24192b60301c1219c054a34bcd9d9723bb64ec9b5b987882d86c32868e6" +checksum = "2bc5a636dc75e5c25651e34f7a36afc9ae60d38166687c5b0375abb580ac81a2" dependencies = [ "ark-bn254", "ark-ec", "ark-ff", "ark-serialize", - "array-bytes", - "base64 0.21.5", + "base64 0.21.7", "bincode", - "bitflags 1.3.2", + "bitflags 2.6.0", "blake3", "borsh 0.10.3", "borsh 0.9.3", + "borsh 1.5.1", "bs58 0.4.0", "bv", "bytemuck", @@ -5255,20 +5322,20 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.11", + "getrandom 0.2.15", "itertools", "js-sys", "lazy_static", "libc", - "libsecp256k1", + "libsecp256k1 0.6.0", + "light-poseidon", "log", - "memoffset 0.9.0", - "num-bigint 0.4.4", - "num-derive 0.3.3", + "memoffset 0.9.1", + "num-bigint 0.4.6", + "num-derive 0.4.2", "num-traits", "parking_lot", - "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", @@ -5288,21 +5355,21 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726697292d3f551898537f921749352e965510a9cfe7e7b2ff7f1a0fcc6e1db" +checksum = "bf373c3da0387f47fee4c5ed2465a9628b9db026a62211a692a9285aa9251544" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bincode", "eager", "enum-iterator", "itertools", "libc", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "percentage", - "rand 0.7.3", + "rand 0.8.5", "rustc_version", "serde", "solana-frozen-abi", @@ -5316,18 +5383,19 @@ dependencies = [ [[package]] name = "solana-program-test" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d071392c72f4e12527fa5e13e9ab9bd23a785eda1331a597277ee8f8c0800" +checksum = "9194b8744c5b135401ab4a2923a1072d3a67697bd50f7450a4ed5302f36a6999" dependencies = [ "assert_matches", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "bincode", "chrono-humanize", "crossbeam-channel", "log", "serde", + "solana-accounts-db", "solana-banks-client", "solana-banks-interface", "solana-banks-server", @@ -5337,15 +5405,17 @@ dependencies = [ "solana-runtime", "solana-sdk", "solana-vote-program", + "solana_rbpf", + "test-case", "thiserror", "tokio", ] [[package]] name = "solana-pubsub-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f134152897fe6d3fad3da9945ae452dfc6c2d71465ddce1ad8a423d54ad38bee" +checksum = "97b9abc76168d19927561db6a3685b98752bd0961b4ce4f8b7f85ee12238c017" dependencies = [ "crossbeam-channel", "futures-util", @@ -5368,9 +5438,9 @@ dependencies = [ [[package]] name = "solana-quic-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409c0182a32bb11acdf84c96361cff4628e93e7e8b293a8cc43e5ef354ffa46a" +checksum = "7952c5306a0be5f5276448cd20246b31265bfa884f29a077a24303c6a16aeb34" dependencies = [ "async-mutex", "async-trait", @@ -5380,9 +5450,8 @@ dependencies = [ "log", "quinn", "quinn-proto", - "quinn-udp", "rcgen", - "rustls 0.20.9", + "rustls", "solana-connection-cache", "solana-measure", "solana-metrics", @@ -5396,9 +5465,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce5c2d7f4e92580e6dd18877f0cd5f152e662dbda9c2eed69d29ae9a6f6e5d0" +checksum = "a4fa0cc66f8e73d769bca2ede3012ba2ef8ab67963e832808665369f2cf81743" dependencies = [ "lazy_static", "num_cpus", @@ -5406,14 +5475,14 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a9e49486e3f31009cfd24869de318e0fac261257f0e87e6f692e0bbf6a053b6" +checksum = "289803796d4ff7b4699504d3ab9e9d9c5205ea3892b2ebe397b377494dbd75d4" dependencies = [ "console", "dialoguer", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "parking_lot", "qstring", @@ -5425,12 +5494,12 @@ dependencies = [ [[package]] name = "solana-rpc-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336cdd2dbb4dcfdb7c905eb45fdd32de30f594b12f00d894160a8e4d12fc76a3" +checksum = "6cb55a08018776a62ecff52139fbcdab1a7baa4e8f077202be58156e8dde4d5f" dependencies = [ "async-trait", - "base64 0.21.5", + "base64 0.21.7", "bincode", "bs58 0.4.0", "indicatif", @@ -5451,11 +5520,11 @@ dependencies = [ [[package]] name = "solana-rpc-client-api" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0cc64f5092d9c3e0bbfbd459689ffc17617b9f52773ffb7e26a2483a33d5ace" +checksum = "72a8403038f4d6ab65bc7e7afb3afe8d9824c592232553c5cef55cf3de36025d" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bs58 0.4.0", "jsonrpc-core", "reqwest", @@ -5467,15 +5536,15 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-version", - "spl-token-2022 0.9.0", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "solana-rpc-client-nonce-utils" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c54440695e2a3b14749b52f2021172aeb2387f8bd95f4e0cc2f97e5d27b5ea4" +checksum = "4caca735caf76d51c074c3bacbfe38094bf7f92cfbe7b5b13f3bc4946e64f889" dependencies = [ "clap 2.34.0", "solana-clap-utils", @@ -5486,12 +5555,13 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63578440eb0526fc3b3155be56c33dec115d8739e0964ec563a8ae8c80b4ffd2" +checksum = "b699943045665038bfa4e76dd2582b4c390f1aec6ab5edef36da43afe3469f1d" dependencies = [ + "aquamarine", "arrayref", - "base64 0.21.5", + "base64 0.21.7", "bincode", "blake3", "bv", @@ -5511,26 +5581,29 @@ dependencies = [ "lru", "lz4", "memmap2", + "mockall", "modular-bitfield", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "num_cpus", - "num_enum 0.6.1", - "once_cell", + "num_enum 0.7.2", "ouroboros", "percentage", - "rand 0.7.3", + "qualifier_attr", + "rand 0.8.5", "rayon", "regex", "rustc_version", "serde", "serde_derive", "serde_json", + "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", @@ -5543,6 +5616,7 @@ dependencies = [ "solana-stake-program", "solana-system-program", "solana-version", + "solana-vote", "solana-vote-program", "solana-zk-token-proof-program", "solana-zk-token-sdk", @@ -5558,15 +5632,15 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fc9581f8345a67da71386274084d9a2e35f25689871ad644f5992c786df7c7" +checksum = "df43d3a1e1637397ab43cbc216a5a8f977ec8a3cc3f3ae8c3851c83a3255dbcf" dependencies = [ "assert_matches", - "base64 0.21.5", + "base64 0.21.7", "bincode", - "bitflags 1.3.2", - "borsh 0.10.3", + "bitflags 2.6.0", + "borsh 1.5.1", "bs58 0.4.0", "bytemuck", "byteorder", @@ -5580,16 +5654,17 @@ dependencies = [ "itertools", "js-sys", "lazy_static", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", "memmap2", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.2", "pbkdf2 0.11.0", "qstring", + "qualifier_attr", "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", @@ -5599,6 +5674,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "sha3 0.10.8", + "siphasher", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", @@ -5611,15 +5687,15 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d749979b74d6ca1d8b0f1da1d0333332cfac425a34d71ed1149cccc322e0533" +checksum = "86c76414183a325038ff020b22c07d1e9d2da0703ddc0244acfed37ee2921d96" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "rustversion", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -5630,9 +5706,9 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" [[package]] name = "solana-send-transaction-service" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dad2cb6f9462e7e1c9900df76b9ad601b199aab3d26855d28bf8494698def3" +checksum = "e056d865d22548bb7228121e118aa632486fc1a33a100961e5e98b5663371384" dependencies = [ "crossbeam-channel", "log", @@ -5646,9 +5722,9 @@ dependencies = [ [[package]] name = "solana-stake-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec54610fafa934717e90e8ab0774867b054d4c3852b5ca24d5947edf14a61e1" +checksum = "c5dd1bc07beb75da5df5e07301d3d0d6104872c9afade22b910af9061fb4bc15" dependencies = [ "bincode", "log", @@ -5661,16 +5737,16 @@ dependencies = [ [[package]] name = "solana-streamer" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a848b9b56af99988e6273ccf79f2bd816633dc3da9ea0eb4488a5b0f8ec820" +checksum = "fad1bdb955ec6d23a1dbf87e403ff3e610d68616275693125a893d7ed4b2d323" dependencies = [ "async-channel", "bytes", "crossbeam-channel", "futures-util", "histogram", - "indexmap 1.9.3", + "indexmap 2.2.6", "itertools", "libc", "log", @@ -5680,10 +5756,10 @@ dependencies = [ "pkcs8", "quinn", "quinn-proto", - "quinn-udp", - "rand 0.7.3", + "rand 0.8.5", "rcgen", - "rustls 0.20.9", + "rustls", + "smallvec", "solana-metrics", "solana-perf", "solana-sdk", @@ -5694,9 +5770,9 @@ dependencies = [ [[package]] name = "solana-system-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a427a441f95ee0b9d8e2065a2978d86142c6fa40cbdccd1de724f77b6cc885af" +checksum = "78733745268c96d5a29c09cde9f0a6c9d662abba43e661b75dd858da8e3d0b2e" dependencies = [ "bincode", "log", @@ -5708,9 +5784,9 @@ dependencies = [ [[package]] name = "solana-thin-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db5ba7eddcc0cefc3c5c116387097cb81bb13d7598fbdb3b40c5a964105e879" +checksum = "bc301310ba0755c449a8800136f67f8ad14419b366404629894cd10021495360" dependencies = [ "bincode", "log", @@ -5723,17 +5799,16 @@ dependencies = [ [[package]] name = "solana-tpu-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bdf56494fd1b509c5428f969a10e4e0865b3eaf40aac1640c8f72dac3112b89" +checksum = "fb887bd5078ff015e103e9ee54a6713380590efa8ff1804b3a653f07188928c6" dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 1.9.3", + "indexmap 2.2.6", "indicatif", "log", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -5748,12 +5823,12 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3c52eaa1977b0121a099243de4b5b44de936e67869d3298400fb6e974a2f7b" +checksum = "4a0cdfdf63192fb60de094fae8e81159e4e3e9aac9659fe3f9ef0e707023fb32" dependencies = [ "Inflector", - "base64 0.21.5", + "base64 0.21.7", "bincode", "borsh 0.10.3", "bs58 0.4.0", @@ -5763,20 +5838,19 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", - "solana-address-lookup-table-program", "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-associated-token-account 2.3.0", + "spl-memo", + "spl-token", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "solana-udp-client" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1225fb057b8b5e5aa5b0ee01b974e6ef2c6f01727dfd217c23b89b6547a8b17b" +checksum = "3ea0d6d8d66e36371577f51c4d1d6192a66f1fa4efe7161a36d94677640dcadb" dependencies = [ "async-trait", "solana-connection-cache", @@ -5789,9 +5863,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f7b09ffc8f5446bee6ee1ab4ce4c98504d23222313de1d0ed762f736a3ffe3" +checksum = "6f4c2f531c22ce806b211118be8928a791425f97de4592371fb57b246ed33e34" dependencies = [ "log", "rustc_version", @@ -5803,15 +5877,34 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-vote" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ab95a5d19ff0464def1777adaae5a74e1edc9e6818103064c18fdc2643f6cb" +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.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be239ebe1d73af268ce9ba5111ce9595a430aa98576105e87b00e92a5ef2a0b" +checksum = "6d8a6486017e71a3714a8e1a635e17209135cc20535ba9808ccf106d80ff6e8b" dependencies = [ "bincode", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "rustc_version", "serde", @@ -5827,13 +5920,12 @@ dependencies = [ [[package]] name = "solana-zk-token-proof-program" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a813c2c577b9eb24b62d7149d1e5340425f55650f3ff6a01eb2ded828a95774" +checksum = "f1e3dfb2deb449f7eb1dbd0c7e66dd95ec7b1303a5788673f9fbc9b5a5ea59f2" dependencies = [ "bytemuck", - "getrandom 0.1.16", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "solana-program-runtime", "solana-sdk", @@ -5842,12 +5934,12 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.16.23" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4b0547480462cfec9dddaa8adcf2fa7c8b022021738bf71c790c0c7be54a34" +checksum = "513407f88394e437b4ff5aad892bc5bf51a655ae2401e6e63549734d3695c46f" dependencies = [ "aes-gcm-siv", - "base64 0.21.5", + "base64 0.21.7", "bincode", "bytemuck", "byteorder", @@ -5856,7 +5948,7 @@ dependencies = [ "itertools", "lazy_static", "merlin", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "rand 0.7.3", "serde", @@ -5871,9 +5963,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d4ba1e58947346e360fabde0697029d36ba83c42f669199b16a8931313cf29" +checksum = "3d457cc2ba742c120492a64b7fa60e22c575e891f6b55039f4d736568fb112a3" dependencies = [ "byteorder", "combine", @@ -5912,33 +6004,33 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.1.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" +checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" dependencies = [ "assert_matches", - "borsh 0.9.3", - "num-derive 0.3.3", + "borsh 0.10.3", + "num-derive 0.4.2", "num-traits", "solana-program", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "spl-associated-token-account" -version = "2.2.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3" +checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" dependencies = [ "assert_matches", - "borsh 0.10.3", - "num-derive 0.4.1", + "borsh 1.5.1", + "num-derive 0.4.2", "num-traits", "solana-program", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token", + "spl-token-2022 3.0.2", "thiserror", ] @@ -5950,40 +6042,66 @@ checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator-derive", + "spl-discriminator-derive 0.1.2", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d1814406e98b08c5cd02c1126f83fd407ad084adce0b05fda5730677822eac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.2.0", ] [[package]] name = "spl-discriminator-derive" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" +dependencies = [ + "quote", + "spl-discriminator-syn 0.1.2", + "syn 2.0.58", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ - "quote 1.0.35", - "spl-discriminator-syn", - "syn 2.0.55", + "quote", + "spl-discriminator-syn 0.2.0", + "syn 2.0.58", ] [[package]] name = "spl-discriminator-syn" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2" +checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "sha2 0.10.8", - "syn 2.0.55", + "syn 2.0.58", "thiserror", ] [[package]] -name = "spl-memo" -version = "3.0.1" +name = "spl-discriminator-syn" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" dependencies = [ - "solana-program", + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", + "thiserror", ] [[package]] @@ -6005,7 +6123,20 @@ dependencies = [ "bytemuck", "solana-program", "solana-zk-token-sdk", - "spl-program-error", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-pod" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" +dependencies = [ + "borsh 1.5.1", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.4.1", ] [[package]] @@ -6014,23 +6145,48 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c" dependencies = [ - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "solana-program", - "spl-program-error-derive", + "spl-program-error-derive 0.3.2", "thiserror", ] [[package]] -name = "spl-program-error-derive" -version = "0.3.1" +name = "spl-program-error" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c" +checksum = "49065093ea91f57b9b2bd81493ff705e2ad4e64507a07dbc02b085778e02770e" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "sha2 0.10.8", - "syn 2.0.55", + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.4.1", + "thiserror", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", ] [[package]] @@ -6041,25 +6197,38 @@ checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "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.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" +checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" dependencies = [ - "arrayref", "bytemuck", - "num-derive 0.3.3", - "num-traits", - "num_enum 0.5.11", "solana-program", - "thiserror", + "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-tlv-account-resolution" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cace91ba08984a41556efe49cbf2edca4db2f577b649da7827d3621161784bf8" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", ] [[package]] @@ -6079,44 +6248,100 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "0.6.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" +checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", - "num_enum 0.5.11", + "num_enum 0.7.2", "solana-program", "solana-zk-token-sdk", - "spl-memo 3.0.1", - "spl-token 3.5.0", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "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" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86" +checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.2", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.4.1", + "spl-type-length-value 0.3.0", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", - "num_enum 0.7.1", + "num_enum 0.7.2", "solana-program", + "solana-security-txt", "solana-zk-token-sdk", - "spl-memo 4.0.0", - "spl-pod", - "spl-token 4.0.0", - "spl-token-metadata-interface", - "spl-transfer-hook-interface", - "spl-type-length-value", + "spl-memo", + "spl-pod 0.2.2", + "spl-token", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", + "spl-transfer-hook-interface 0.6.3", + "spl-type-length-value 0.4.3", "thiserror", ] +[[package]] +name = "spl-token-group-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d419b5cfa3ee8e0f2386fd7e02a33b3ec8a7db4a9c7064a2ea24849dc4a273b6" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", +] + [[package]] name = "spl-token-metadata-interface" version = "0.2.0" @@ -6125,10 +6350,24 @@ checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" dependencies = [ "borsh 0.10.3", "solana-program", - "spl-discriminator", - "spl-pod", - "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-metadata-interface" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" +dependencies = [ + "borsh 1.5.1", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", ] [[package]] @@ -6140,11 +6379,43 @@ dependencies = [ "arrayref", "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-tlv-account-resolution", - "spl-type-length-value", + "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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aabdb7c471566f6ddcee724beb8618449ea24b399e58d464d6b5bc7db550259" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.5.1", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a98359769cd988f7b35c02558daa56d496a7e3bd8626e61f90a7c757eedb9b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-tlv-account-resolution 0.6.3", + "spl-type-length-value 0.4.3", ] [[package]] @@ -6155,9 +6426,22 @@ checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ce13429dbd41d2cee8a73931c05fda0b0c8ca156a8b0c19445642550bb61a" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", ] [[package]] @@ -6178,6 +6462,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.24.1" @@ -6194,8 +6484,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "rustversion", "syn 1.0.109", ] @@ -6213,17 +6503,117 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] -name = "switchboard-v2" -version = "0.4.0" +name = "sval" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eb957fbc79a55306d5d25d87daf3627bc3800681491cda0709eef36c748bfe" + +[[package]] +name = "sval_buffer" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e860aef60e9cbf37888d4953a13445abf523c534640d1f6174d310917c410d" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b81886169f446e22edc18ead7addd9ebd141c39bf2286cb37943c92cd3af724" +checksum = "ea3f2b07929a1127d204ed7cb3905049381708245727680e9139dac317ed556f" dependencies = [ - "anchor-lang", - "anchor-spl", + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e188677497de274a1367c4bda15bd2296de4070d91729aac8f0a09c1abf64d" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f456c07dae652744781f2245d5e3b78e6a9ebad70790ac11eb15dbdbce5282" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886feb24709f0476baaebbf9ac10671a50163caa7e439d7a7beb7f6d81d0a6fb" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2e7fc517d778f44f8cb64140afa36010999565528d48985f55e64d45f369ce" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bf66549a997ff35cd2114a27ac4b0c2843280f2cfa84b240d169ecaa0add46" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + +[[package]] +name = "switchboard-common" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96fe58be35530580b729fa5d846661c89a007982527f4ff0ca6010168564159" +dependencies = [ + "base64 0.21.7", + "hex", + "log", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", +] + +[[package]] +name = "switchboard-solana" +version = "0.29.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb0cb2cd26bcd72a11fae679d07207bca093c303c9cc72bcdc7866bb7bf8a6b" +dependencies = [ + "anchor-lang 0.29.0", + "anchor-spl 0.29.0", "bytemuck", + "hex", + "kv-log-macro", + "lazy_static", + "libsecp256k1 0.7.1", + "log", "rust_decimal", + "solana-address-lookup-table-program", "solana-program", "superslice", + "switchboard-common", ] [[package]] @@ -6234,35 +6624,36 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" [[package]] name = "syn" -version = "0.15.44" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] -name = "syn" -version = "2.0.55" +name = "syn_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "unicode-ident", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -6277,10 +6668,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", - "unicode-xid 0.2.4", + "unicode-xid", ] [[package]] @@ -6304,11 +6695,17 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -6345,39 +6742,78 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "test-case-core", +] + [[package]] name = "test-utilities" version = "0.1.0" dependencies = [ - "anchor-lang", - "anchor-spl", + "anchor-lang 0.29.0", + "anchor-lang 0.30.1", + "anchor-spl 0.30.1", "anyhow", "assert_matches", "bincode", @@ -6390,15 +6826,34 @@ dependencies = [ "marginfi", "pretty_assertions", "pyth-sdk-solana", + "pyth-solana-receiver-sdk", + "serde", + "serde_json", + "solana-cli-output", "solana-logger", "solana-program", "solana-program-test", "solana-sdk", + "spl-discriminator 0.2.2", + "spl-tlv-account-resolution 0.6.3", + "spl-token-2022 3.0.2", + "spl-transfer-hook-interface 0.6.3", "static_assertions", - "switchboard-v2", + "switchboard-solana", + "test_transfer_hook", "type-layout", ] +[[package]] +name = "test_transfer_hook" +version = "0.1.0" +dependencies = [ + "solana-program", + "spl-tlv-account-resolution 0.6.3", + "spl-token-2022 3.0.2", + "spl-transfer-hook-interface 0.6.3", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -6410,35 +6865,35 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -6446,14 +6901,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", - "libc", - "num_threads", + "num-conv", "powerfmt", "serde", "time-core", @@ -6468,10 +6922,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -6496,9 +6951,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -6511,9 +6966,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -6523,30 +6978,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -6559,35 +7004,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" -dependencies = [ - "pin-project", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", -] - [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.9", + "rustls", "tokio", ] @@ -6609,9 +7032,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -6620,18 +7043,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.9", + "rustls", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls", "tungstenite", - "webpki", - "webpki-roots 0.22.6", + "webpki-roots 0.25.4", ] [[package]] @@ -6651,16 +7073,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -6668,170 +7089,65 @@ name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tonic" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.13.1", - "bytes", - "flate2", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost 0.11.9", - "prost-derive 0.11.9", - "rustls-native-certs", - "rustls-pemfile", - "tokio", - "tokio-rustls 0.23.4", - "tokio-stream", - "tokio-util 0.7.10", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", - "webpki-roots 0.22.6", -] - -[[package]] -name = "tonic" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.21.5", - "bytes", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost 0.12.3", - "rustls 0.21.9", - "rustls-native-certs", - "rustls-pemfile", - "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", +dependencies = [ + "serde", ] [[package]] -name = "tonic-build" -version = "0.8.4" +name = "toml" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ - "prettyplease 0.1.25", - "proc-macro2 1.0.79", - "prost-build 0.11.9", - "quote 1.0.35", - "syn 1.0.109", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.15", ] [[package]] -name = "tonic-build" -version = "0.10.2" +name = "toml_datetime" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ - "prettyplease 0.2.15", - "proc-macro2 1.0.79", - "prost-build 0.12.3", - "quote 1.0.35", - "syn 2.0.55", + "serde", ] [[package]] -name = "tonic-health" -version = "0.10.2" +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80db390246dfb46553481f6024f0082ba00178ea495dbb99e70ba9a4fafb5e1" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "async-stream", - "prost 0.12.3", - "tokio", - "tokio-stream", - "tonic 0.10.2", + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", ] [[package]] -name = "tower" -version = "0.4.13" +name = "toml_edit" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util 0.7.10", - "tower-layer", - "tower-service", - "tracing", + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", ] [[package]] -name = "tower-layer" -version = "0.3.2" +name = "toml_edit" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", +] [[package]] name = "tower-service" @@ -6857,9 +7173,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -6872,27 +7188,6 @@ dependencies = [ "valuable", ] -[[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-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-opentelemetry" version = "0.17.4" @@ -6906,78 +7201,42 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-stackdriver" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff9dd91761e07727176a3dd3a1d64bbb577ea656b7b82fa4be4021832674c49" -dependencies = [ - "Inflector", - "serde", - "serde_json", - "thiserror", - "time", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", "sharded-slab", - "smallvec", "thread_local", - "tracing", "tracing-core", - "tracing-log", - "tracing-serde", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[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.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", "rand 0.8.5", - "rustls 0.20.9", - "sha-1", + "rustls", + "sha1", "thiserror", "url", "utf-8", - "webpki", - "webpki-roots 0.22.6", + "webpki-roots 0.24.0", ] [[package]] @@ -6996,11 +7255,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4a1cf66ce820973c4b31c5ef47a8e930a53ffbcec191212c33f5a3ad75c6cd" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2", + "quote", "syn 1.0.109", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -7009,9 +7274,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -7021,30 +7286,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "unicode-xid" -version = "0.1.0" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -7073,9 +7332,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -7101,21 +7360,15 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" @@ -7124,12 +7377,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" -dependencies = [ - "getrandom 0.2.11", -] +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" [[package]] name = "valuable" @@ -7137,6 +7387,42 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -7163,9 +7449,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -7194,9 +7480,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -7204,24 +7490,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -7231,38 +7517,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.35", + "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -7273,50 +7559,28 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.6", - "untrusted 0.9.0", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ - "webpki", + "rustls-webpki", ] [[package]] name = "webpki-roots" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" - -[[package]] -name = "which" -version = "4.4.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" @@ -7336,11 +7600,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -7351,35 +7615,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[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.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.6", ] [[package]] @@ -7397,22 +7637,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] @@ -7432,25 +7657,20 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -7459,15 +7679,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -7477,15 +7691,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -7495,15 +7703,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -7513,15 +7721,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -7531,15 +7733,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -7549,33 +7745,36 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.5.19" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -7590,6 +7789,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-parser" version = "0.14.0" @@ -7610,11 +7818,13 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", + "linux-raw-sys", + "rustix", ] [[package]] @@ -7632,81 +7842,24 @@ dependencies = [ "time", ] -[[package]] -name = "yellowstone-grpc-client" -version = "1.12.0+solana.1.16.23" -source = "git+https://github.com/rpcpool/yellowstone-grpc.git?rev=a2cd1498ac64baa1017d4a4cdefbf46100215b4c#a2cd1498ac64baa1017d4a4cdefbf46100215b4c" -dependencies = [ - "bytes", - "futures", - "http", - "thiserror", - "tonic 0.10.2", - "tonic-health", - "yellowstone-grpc-proto", -] - -[[package]] -name = "yellowstone-grpc-proto" -version = "1.11.0+solana.1.16.23" -source = "git+https://github.com/rpcpool/yellowstone-grpc.git?rev=a2cd1498ac64baa1017d4a4cdefbf46100215b4c#a2cd1498ac64baa1017d4a4cdefbf46100215b4c" -dependencies = [ - "anyhow", - "bincode", - "prost 0.12.3", - "protobuf-src", - "solana-account-decoder", - "solana-sdk", - "solana-transaction-status", - "tonic 0.10.2", - "tonic-build 0.10.2", -] - -[[package]] -name = "yup-oauth2" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364ca376b5c04d9b2be9693054e3e0d2d146b363819d0f9a10c6ee66e4c8406b" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.13.1", - "futures", - "http", - "hyper", - "hyper-rustls", - "itertools", - "log", - "percent-encoding", - "rustls 0.21.9", - "rustls-pemfile", - "seahash", - "serde", - "serde_json", - "time", - "tokio", - "tower-service", - "url", -] - [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -7724,9 +7877,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -7750,9 +7903,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.12+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 1752730ba..411d58040 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,28 +1,37 @@ [workspace] resolver = "2" -members = ["programs/*", "clients/rust/*", "tools/*", "observability/indexer"] -exclude = ["programs/brick"] +members = ["programs/*", "clients/rust/*", "tools/*"] [workspace.dependencies] -solana-cli-output = "1.16.23" -solana-client = "1.16.23" -solana-sdk = "1.16.23" -solana-logger = "1.16.23" -solana-program = "1.16.23" -solana-program-test = "1.16.23" -solana-account-decoder = "1.16.23" -solana-measure = "1.16.23" -solana-metrics = "1.16.23" -solana-transaction-status = "1.16.23" +solana-cli-output = "=1.18.17" +solana-client = "=1.18.17" +solana-sdk = "=1.18.17" +solana-logger = "=1.18.17" +solana-program = "=1.18.17" +solana-program-test = "=1.18.17" +solana-account-decoder = "=1.18.17" +solana-measure = "=1.18.17" +solana-metrics = "=1.18.17" +solana-transaction-status = "=1.18.17" +solana-address-lookup-table-program = "=1.18.17" spl-token = "4.0.0" spl-associated-token-account = "2.2.0" +spl-transfer-hook-interface = "0.6.3" +spl-tlv-account-resolution = "0.6.3" +spl-discriminator = "0.2.2" +spl-token-2022 = "3.0.2" -anchor-lang = "0.28.0" -anchor-spl = "0.28.0" -anchor-client = "0.28.0" +anchor-lang = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" } +anchor-lang-29 = { version = "0.29.0", package = "anchor-lang" } +anchor-spl = { git = "https://github.com/mrgnlabs/anchor.git", features = [ + "token_2022", +], rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" } +anchor-client = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" } -switchboard-v2 = "0.4.0" -pyth-sdk-solana = "0.8.0" +pyth-sdk-solana = "=0.10.1" +pyth-solana-receiver-sdk = "0.3.0" +switchboard-solana = "0.29.0" +borsh = "0.10.3" [profile.release] overflow-checks = true diff --git a/clients/rust/marginfi-cli/Cargo.toml b/clients/rust/marginfi-cli/Cargo.toml index 1c067f383..f3b8ad0e5 100644 --- a/clients/rust/marginfi-cli/Cargo.toml +++ b/clients/rust/marginfi-cli/Cargo.toml @@ -10,9 +10,8 @@ path = "src/bin/main.rs" [features] devnet = ["marginfi/devnet"] mainnet-beta = ["marginfi/mainnet-beta"] +staging = ["marginfi/staging"] default = ["mainnet-beta"] -admin = [] -dev = [] lip = [] [dependencies] @@ -21,12 +20,14 @@ solana-sdk = { workspace = true } solana-account-decoder = { workspace = true } spl-token = { workspace = true } spl-associated-token-account = { workspace = true } +solana-address-lookup-table-program = { workspace = true } anchor-client = { workspace = true } -anchor-spl = { workspace = true } +anchor-spl = { workspace = true, features = ["token_2022"] } pyth-sdk-solana = { workspace = true } -switchboard-v2 = { workspace = true } +switchboard-solana = { workspace = true } +borsh = "0.10.3" marginfi = { path = "../../../programs/marginfi", version = "0.1.0", features = [ "client", @@ -51,4 +52,6 @@ type-layout = "0.2.0" chrono = "0.4.23" bincode = "1.3.1" bs58 = "0.4.0" -solana-address-lookup-table-program = "1.14" +rand = "0.8.4" +pyth-solana-receiver-sdk = "0.3.0" +hex = "0.4.3" diff --git a/clients/rust/marginfi-cli/src/config.rs b/clients/rust/marginfi-cli/src/config.rs index 76f2c34da..cfc29e6c3 100644 --- a/clients/rust/marginfi-cli/src/config.rs +++ b/clients/rust/marginfi-cli/src/config.rs @@ -1,5 +1,3 @@ -use solana_sdk::signature::Signature; - use { anchor_client::{Client, Cluster, Program}, clap::Parser, @@ -52,14 +50,6 @@ pub enum CliSigner { Keypair(Keypair), } -impl CliSigner { - pub fn pubkey(&self) -> Pubkey { - match self { - CliSigner::Keypair(keypair) => keypair.pubkey(), - } - } -} - pub fn clone_keypair(keypair: &Keypair) -> Keypair { Keypair::from_bytes(&keypair.to_bytes()).unwrap() } @@ -72,27 +62,6 @@ impl Clone for CliSigner { } } -impl Signer for CliSigner { - fn try_pubkey(&self) -> Result { - Ok(self.pubkey()) - } - - fn try_sign_message( - &self, - message: &[u8], - ) -> Result { - match self { - CliSigner::Keypair(keypair) => Ok(keypair.try_sign_message(message)?), - } - } - - fn is_interactive(&self) -> bool { - match self { - CliSigner::Keypair(_) => true, - } - } -} - impl Deref for CliSigner { type Target = Keypair; @@ -103,15 +72,20 @@ impl Deref for CliSigner { } } +#[allow(dead_code)] pub struct Config { + #[allow(dead_code)] pub cluster: Cluster, pub fee_payer: Keypair, pub multisig: Option, pub program_id: Pubkey, + #[allow(dead_code)] pub commitment: CommitmentConfig, pub dry_run: bool, + #[allow(dead_code)] pub client: Client, pub mfi_program: Program, + #[allow(dead_code)] pub lip_program: Program, } diff --git a/clients/rust/marginfi-cli/src/entrypoint.rs b/clients/rust/marginfi-cli/src/entrypoint.rs index f81c1e0fd..5013eee69 100644 --- a/clients/rust/marginfi-cli/src/entrypoint.rs +++ b/clients/rust/marginfi-cli/src/entrypoint.rs @@ -1,3 +1,4 @@ +use crate::processor::oracle::find_pyth_push_oracles_for_feed_id; use crate::{ config::GlobalOptions, processor::{self, process_set_user_flag}, @@ -6,26 +7,21 @@ use crate::{ use anchor_client::Cluster; use anyhow::Result; use clap::{clap_derive::ArgEnum, Parser}; -#[cfg(feature = "admin")] use fixed::types::I80F48; -#[cfg(any(feature = "admin", feature = "dev"))] -use marginfi::state::marginfi_group::{BankConfigOpt, InterestRateConfigOpt}; -use marginfi::state::{ - marginfi_account::FLASHLOAN_ENABLED_FLAG, - marginfi_group::{BankOperationalState, RiskTier}, - price::OracleSetup, -}; -#[cfg(feature = "dev")] use marginfi::{ - prelude::{GroupConfig, MarginfiGroup}, + prelude::*, state::{ - marginfi_account::{Balance, LendingAccount, MarginfiAccount}, - marginfi_group::{Bank, BankConfig, InterestRateConfig, OracleConfig, WrappedI80F48}, + marginfi_account::{Balance, LendingAccount, MarginfiAccount, FLASHLOAN_ENABLED_FLAG}, + marginfi_group::{ + Bank, BankConfig, BankConfigOpt, BankOperationalState, InterestRateConfig, + InterestRateConfigOpt, OracleConfig, RiskTier, WrappedI80F48, + }, + price::OracleSetup, }, }; +use pyth_solana_receiver_sdk::price_update::get_feed_id_from_hex; +use rand::Rng; use solana_sdk::{commitment_config::CommitmentLevel, pubkey::Pubkey}; - -#[cfg(feature = "dev")] use type_layout::TypeLayout; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -53,12 +49,16 @@ pub enum Command { #[clap(subcommand)] subcmd: ProfileCommand, }, - #[cfg(feature = "dev")] + InspectPadding {}, - #[cfg(feature = "dev")] - PatchIdl { idl_path: String }, - #[cfg(feature = "dev")] + + PatchIdl { + idl_path: String, + }, + InspectSize {}, + + MakeTestI80F48, Account { #[clap(subcommand)] subcmd: AccountCommand, @@ -68,13 +68,18 @@ pub enum Command { #[clap(subcommand)] subcmd: LipCommand, }, - #[cfg(feature = "dev")] - InspectSwitchboardFeed { switchboard_feed: Pubkey }, - #[cfg(feature = "dev")] + // + // InspectSwitchboardFeed { switchboard_feed: Pubkey }, ShowOracleAges { #[clap(long, action)] only_stale: bool, }, + InspectPythPushOracleFeed { + pyth_feed: Pubkey, + }, + FindPythPull { + feed_id: String, + }, } #[derive(Debug, Parser)] @@ -83,17 +88,14 @@ pub enum GroupCommand { marginfi_group: Option, }, GetAll {}, - #[cfg(feature = "admin")] Create { admin: Option, #[clap(short = 'f', long = "override")] override_existing_profile_group: bool, }, - #[cfg(feature = "admin")] Update { admin: Option, }, - #[cfg(feature = "admin")] AddBank { #[clap(long)] mint: Pubkey, @@ -139,11 +141,9 @@ pub enum GroupCommand { )] oracle_max_age: u16, }, - #[cfg(feature = "admin")] HandleBankruptcy { accounts: Vec, }, - #[cfg(feature = "admin")] UpdateLookupTable { #[clap(short = 't', long)] existing_token_lookup_tables: Vec, @@ -167,15 +167,17 @@ impl From for RiskTier { #[derive(Clone, Copy, Debug, Parser, ArgEnum)] pub enum OracleTypeArg { - PythEma, + PythLegacy, Switchboard, + PythPushOracle, } impl From for OracleSetup { fn from(value: OracleTypeArg) -> Self { match value { - OracleTypeArg::PythEma => OracleSetup::PythEma, + OracleTypeArg::PythLegacy => OracleSetup::PythLegacy, OracleTypeArg::Switchboard => OracleSetup::SwitchboardV2, + OracleTypeArg::PythPushOracle => OracleSetup::PythPushOracle, } } } @@ -205,7 +207,6 @@ pub enum BankCommand { GetAll { marginfi_group: Option, }, - #[cfg(feature = "admin")] Update { bank_pk: Pubkey, #[clap(long)] @@ -257,11 +258,9 @@ pub enum BankCommand { )] permissionless_bad_debt_settlement: Option, }, - #[cfg(feature = "dev")] InspectPriceOracle { bank_pk: Pubkey, }, - #[cfg(feature = "admin")] SetupEmissions { bank: Pubkey, #[clap(long)] @@ -275,7 +274,6 @@ pub enum BankCommand { #[clap(long)] total_amount_ui: f64, }, - #[cfg(feature = "admin")] UpdateEmissions { bank: Pubkey, #[clap(long)] @@ -289,22 +287,18 @@ pub enum BankCommand { #[clap(long)] additional_amount_ui: Option, }, - #[cfg(feature = "admin")] SettleAllEmissions { bank: Pubkey, }, - #[cfg(feature = "admin")] CollectFees { bank: Pubkey, }, - #[cfg(feature = "admin")] WithdrawFees { bank: Pubkey, amount: f64, #[clap(help = "Destination address, defaults to the profile authority")] destination_address: Option, }, - #[cfg(feature = "admin")] WithdrawInsurance { bank: Pubkey, amount: f64, @@ -416,30 +410,46 @@ pub fn entry(opts: Opts) -> Result<()> { Command::Group { subcmd } => group(subcmd, &opts.cfg_override), Command::Bank { subcmd } => bank(subcmd, &opts.cfg_override), Command::Profile { subcmd } => profile(subcmd), - #[cfg(feature = "dev")] + Command::InspectPadding {} => inspect_padding(), - #[cfg(feature = "dev")] + Command::PatchIdl { idl_path } => patch_marginfi_idl(idl_path), Command::Account { subcmd } => process_account_subcmd(subcmd, &opts.cfg_override), #[cfg(feature = "lip")] Command::Lip { subcmd } => process_lip_subcmd(subcmd, &opts.cfg_override), - #[cfg(feature = "dev")] - Command::InspectSwitchboardFeed { switchboard_feed } => { + + Command::InspectSize {} => inspect_size(), + + Command::ShowOracleAges { only_stale } => { let profile = load_profile()?; let config = profile.get_config(Some(&opts.cfg_override))?; - processor::process_inspect_switchboard_feed(&config, &switchboard_feed); + processor::show_oracle_ages(config, only_stale)?; Ok(()) } - #[cfg(feature = "dev")] - Command::InspectSize {} => inspect_size(), - #[cfg(feature = "dev")] - Command::ShowOracleAges { only_stale } => { + + Command::MakeTestI80F48 => { + process_make_test_i80f48(); + + Ok(()) + } + Command::InspectPythPushOracleFeed { pyth_feed } => { let profile = load_profile()?; let config = profile.get_config(Some(&opts.cfg_override))?; - processor::show_oracle_ages(config, only_stale)?; + processor::inspect_pyth_push_feed(&config, pyth_feed)?; + + Ok(()) + } + Command::FindPythPull { feed_id } => { + let profile = load_profile()?; + let config = profile.get_config(Some(&opts.cfg_override))?; + let feed_id = get_feed_id_from_hex(&feed_id).unwrap(); + + let rpc = config.mfi_program.rpc(); + + find_pyth_push_oracles_for_feed_id(&rpc, feed_id)?; Ok(()) } @@ -504,7 +514,7 @@ fn group(subcmd: GroupCommand, global_options: &GlobalOptions) -> Result<()> { match subcmd { GroupCommand::Get { marginfi_group: _ } => (), GroupCommand::GetAll {} => (), - #[cfg(feature = "admin")] + _ => get_consent(&subcmd, &profile)?, } } @@ -514,14 +524,14 @@ fn group(subcmd: GroupCommand, global_options: &GlobalOptions) -> Result<()> { processor::group_get(config, marginfi_group.or(profile.marginfi_group)) } GroupCommand::GetAll {} => processor::group_get_all(config), - #[cfg(feature = "admin")] + GroupCommand::Create { admin, override_existing_profile_group, } => processor::group_create(config, profile, admin, override_existing_profile_group), - #[cfg(feature = "admin")] + GroupCommand::Update { admin } => processor::group_configure(config, profile, admin), - #[cfg(feature = "admin")] + GroupCommand::AddBank { mint: bank_mint, seed, @@ -565,11 +575,11 @@ fn group(subcmd: GroupCommand, global_options: &GlobalOptions) -> Result<()> { risk_tier, oracle_max_age, ), - #[cfg(feature = "admin")] + GroupCommand::HandleBankruptcy { accounts } => { processor::handle_bankruptcy_for_accounts(&config, &profile, accounts) } - #[cfg(feature = "admin")] + GroupCommand::UpdateLookupTable { existing_token_lookup_tables, } => processor::group::process_update_lookup_tables( @@ -587,7 +597,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { if !global_options.skip_confirmation { match subcmd { BankCommand::Get { .. } | BankCommand::GetAll { .. } => (), - #[cfg(feature = "dev")] + BankCommand::InspectPriceOracle { .. } => (), #[allow(unreachable_patterns)] _ => get_consent(&subcmd, &profile)?, @@ -597,7 +607,6 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { match subcmd { BankCommand::Get { bank } => processor::bank_get(config, bank), BankCommand::GetAll { marginfi_group } => processor::bank_get_all(config, marginfi_group), - #[cfg(feature = "admin")] BankCommand::Update { asset_weight_init, asset_weight_maint, @@ -671,11 +680,9 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { }, ) } - #[cfg(feature = "dev")] BankCommand::InspectPriceOracle { bank_pk } => { processor::bank_inspect_price_oracle(config, bank_pk) } - #[cfg(feature = "admin")] BankCommand::SetupEmissions { bank, deposits, @@ -686,7 +693,6 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { } => processor::bank_setup_emissions( &config, &profile, bank, deposits, borrows, mint, rate, total_ui, ), - #[cfg(feature = "admin")] BankCommand::UpdateEmissions { bank, deposits, @@ -704,19 +710,15 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { rate, additional_amount_ui, ), - #[cfg(feature = "admin")] BankCommand::SettleAllEmissions { bank } => { processor::emissions::claim_all_emissions_for_bank(&config, &profile, bank) } - #[cfg(feature = "admin")] BankCommand::CollectFees { bank } => processor::admin::process_collect_fees(config, bank), - #[cfg(feature = "admin")] BankCommand::WithdrawFees { bank, amount, destination_address, } => processor::admin::process_withdraw_fees(config, bank, amount, destination_address), - #[cfg(feature = "admin")] BankCommand::WithdrawInsurance { bank, amount, @@ -727,7 +729,6 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> { } } -#[cfg(feature = "dev")] fn inspect_padding() -> Result<()> { println!("MarginfiGroup: {}", MarginfiGroup::type_layout()); println!("GroupConfig: {}", GroupConfig::type_layout()); @@ -748,7 +749,6 @@ fn inspect_padding() -> Result<()> { Ok(()) } -#[cfg(feature = "dev")] fn inspect_size() -> Result<()> { use std::mem::size_of; @@ -771,10 +771,8 @@ fn inspect_size() -> Result<()> { Ok(()) } -#[cfg(feature = "dev")] fn patch_marginfi_idl(target_dir: String) -> Result<()> { use crate::patch_type_layout; - use std::io::Write; let idl_path = format!("{}/idl/marginfi.json", target_dir); @@ -783,7 +781,7 @@ fn patch_marginfi_idl(target_dir: String) -> Result<()> { let mut idl: serde_json::Value = serde_json::from_reader(reader)?; let idl_original_path = idl_path.replace(".json", "_original.json"); - let file = std::fs::File::create(&idl_original_path)?; + let file = std::fs::File::create(idl_original_path)?; let writer = std::io::BufWriter::new(file); serde_json::to_writer_pretty(writer, &idl)?; @@ -798,7 +796,7 @@ fn patch_marginfi_idl(target_dir: String) -> Result<()> { } } - patch_type_layout!(idl, "Bank", Bank, "accounts"); + patch_type_layout!(idl, "Bank", Bank, "types"); patch_type_layout!(idl, "Balance", Balance, "types"); patch_type_layout!(idl, "BankConfig", BankConfig, "types"); patch_type_layout!(idl, "BankConfigCompact", BankConfig, "types"); @@ -807,35 +805,6 @@ fn patch_marginfi_idl(target_dir: String) -> Result<()> { let writer = std::io::BufWriter::new(file); serde_json::to_writer_pretty(writer, &idl)?; - // Patch types - - let types_path = format!("{}/types/marginfi.ts", target_dir); - let mut ts_file = std::fs::File::create(types_path)?; - - if let Some(accounts) = idl.get_mut("accounts").and_then(|a| a.as_array_mut()) { - for account in accounts.iter_mut() { - if let Some(name) = account.get_mut("name").and_then(|n| n.as_str()) { - let mut chars = name.chars(); - if let Some(first_char) = chars.next() { - let name_with_lowercase_first_letter = - first_char.to_lowercase().collect::() + chars.as_str(); - account["name"] = serde_json::Value::String(name_with_lowercase_first_letter); - } - } - } - } - - write!( - ts_file, - "export type Marginfi = {};\n", - serde_json::to_string_pretty(&idl)? - )?; - write!( - ts_file, - "export const IDL: Marginfi = {};\n", - serde_json::to_string_pretty(&idl)? - )?; - Ok(()) } @@ -938,3 +907,42 @@ fn get_consent(cmd: T, profile: &Profile) -> Result<()> { Ok(()) } + +pub fn process_make_test_i80f48() { + let mut rng = rand::thread_rng(); + + let i80f48s: Vec = (0..30i128) + .map(|_| { + let i = rng.gen_range(-1_000_000_000_000i128..1_000_000_000_000i128); + I80F48::from_num(i) / I80F48::from_num(1_000_000) + }) + .collect(); + + println!("const testCases = ["); + for i80f48 in i80f48s { + println!( + " {{ number: {:?}, innerValue: {:?} }},", + i80f48, + marginfi::state::marginfi_group::WrappedI80F48::from(i80f48).value + ); + } + + let explicit = vec![ + 0., + 1., + -1., + 0.328934, + 423947246342.487, + 1783921462347640., + 0.00000000000232, + ]; + for f in explicit { + let i80f48 = I80F48::from_num(f); + println!( + " {{ number: {:?}, innerValue: {:?} }},", + i80f48, + marginfi::state::marginfi_group::WrappedI80F48::from(i80f48).value + ); + } + println!("];"); +} diff --git a/clients/rust/marginfi-cli/src/processor/admin.rs b/clients/rust/marginfi-cli/src/processor/admin.rs index c2d42e599..0b1a9bb1a 100644 --- a/clients/rust/marginfi-cli/src/processor/admin.rs +++ b/clients/rust/marginfi-cli/src/processor/admin.rs @@ -1,5 +1,8 @@ -use anchor_client::anchor_lang::prelude::*; -use anchor_client::anchor_lang::InstructionData; +use crate::{ + config::Config, + utils::{process_transaction, ui_to_native}, +}; +use anchor_client::anchor_lang::{prelude::*, InstructionData}; use anchor_spl::associated_token; use anyhow::Result; use marginfi::{ @@ -10,11 +13,6 @@ use solana_sdk::{ instruction::Instruction, message::Message, pubkey::Pubkey, transaction::Transaction, }; -use crate::{ - config::Config, - utils::{process_transaction, ui_to_native}, -}; - pub fn process_collect_fees(config: Config, bank_pk: Pubkey) -> Result<()> { let bank = config.mfi_program.account::(bank_pk)?; let rpc_client = config.mfi_program.rpc(); @@ -24,7 +22,7 @@ pub fn process_collect_fees(config: Config, bank_pk: Pubkey) -> Result<()> { &marginfi::id(), ); - let ix = Instruction { + let mut ix = Instruction { program_id: marginfi::id(), accounts: marginfi::accounts::LendingPoolCollectBankFees { marginfi_group: bank.group, @@ -38,6 +36,8 @@ pub fn process_collect_fees(config: Config, bank_pk: Pubkey) -> Result<()> { .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolCollectBankFees {}.data(), }; + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let signing_keypairs = config.get_signers(false); @@ -80,7 +80,7 @@ pub fn process_withdraw_fees( &spl_token::id(), ); - let ix = Instruction { + let mut ix = Instruction { program_id: marginfi::id(), accounts: marginfi::accounts::LendingPoolWithdrawFees { marginfi_group: bank.group, @@ -94,6 +94,8 @@ pub fn process_withdraw_fees( .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolWithdrawFees { amount }.data(), }; + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let signing_keypairs = config.get_signers(false); @@ -136,7 +138,7 @@ pub fn process_withdraw_insurance( &spl_token::id(), ); - let ix = Instruction { + let mut ix = Instruction { program_id: marginfi::id(), accounts: marginfi::accounts::LendingPoolWithdrawInsurance { marginfi_group: bank.group, @@ -150,6 +152,8 @@ pub fn process_withdraw_insurance( .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolWithdrawInsurance { amount }.data(), }; + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let signing_keypairs = config.get_signers(false); diff --git a/clients/rust/marginfi-cli/src/processor/group.rs b/clients/rust/marginfi-cli/src/processor/group.rs index f135b9c04..4a83e6d16 100644 --- a/clients/rust/marginfi-cli/src/processor/group.rs +++ b/clients/rust/marginfi-cli/src/processor/group.rs @@ -1,5 +1,4 @@ -use std::mem::size_of; - +use crate::{config::Config, profile::Profile, utils}; use anyhow::Result; use log::{debug, info, warn}; use marginfi::state::marginfi_group::Bank; @@ -11,8 +10,7 @@ use solana_client::rpc_filter::{Memcmp, RpcFilterType}; use solana_sdk::{ account::Account, pubkey::Pubkey, signer::Signer, system_program, transaction::Transaction, }; - -use crate::{config::Config, profile::Profile, utils}; +use std::mem::size_of; const CHUNK_SIZE: usize = 22; const KEY_BATCH_SIZE: usize = 20; diff --git a/clients/rust/marginfi-cli/src/processor/mod.rs b/clients/rust/marginfi-cli/src/processor/mod.rs index 39d319493..0bcca4eb4 100644 --- a/clients/rust/marginfi-cli/src/processor/mod.rs +++ b/clients/rust/marginfi-cli/src/processor/mod.rs @@ -1,45 +1,59 @@ -#[cfg(feature = "admin")] -pub mod emissions; - -#[cfg(feature = "admin")] pub mod admin; - -#[cfg(feature = "admin")] +pub mod emissions; pub mod group; +pub mod oracle; use { crate::{ config::Config, profile::{self, get_cli_config_dir, load_profile, CliConfig, Profile}, utils::{ - find_bank_emssions_token_account_pda, find_bank_vault_authority_pda, - find_bank_vault_pda, load_observation_account_metas, process_transaction, - EXP_10_I80F48, + bank_to_oracle_key, calc_emissions_rate, create_oracle_key_array, + find_bank_emssions_auth_pda, find_bank_emssions_token_account_pda, + find_bank_vault_authority_pda, find_bank_vault_pda, load_observation_account_metas, + process_transaction, EXP_10_I80F48, }, }, anchor_client::{ anchor_lang::{InstructionData, ToAccountMetas}, Cluster, }, - anchor_spl::token::{self, spl_token}, + anchor_spl::token_2022::spl_token_2022, anyhow::{anyhow, bail, Result}, + borsh::BorshDeserialize, fixed::types::I80F48, log::info, marginfi::{ - prelude::MarginfiGroup, + constants::{ + EMISSIONS_FLAG_BORROW_ACTIVE, EMISSIONS_FLAG_LENDING_ACTIVE, + PYTH_PUSH_PYTH_SPONSORED_SHARD_ID, ZERO_AMOUNT_THRESHOLD, + }, + prelude::*, state::{ marginfi_account::{BankAccountWrapper, MarginfiAccount}, - marginfi_group::{Bank, BankVaultType}, + marginfi_group::{ + Bank, BankConfig, BankConfigOpt, BankOperationalState, BankVaultType, + InterestRateConfig, WrappedI80F48, + }, + price::{OraclePriceFeedAdapter, OracleSetup, PriceAdapter, PythPushOraclePriceFeed}, }, + utils::NumTraitsWithTolerance, + }, + pyth_sdk_solana::state::{load_price_account, SolanaPriceAccount}, + pyth_solana_receiver_sdk::price_update::PriceUpdateV2, + solana_client::{ + rpc_client::RpcClient, + rpc_filter::{Memcmp, RpcFilterType}, }, - solana_client::rpc_filter::{Memcmp, RpcFilterType}, solana_sdk::{ + account::ReadableAccount, account_info::IntoAccountInfo, clock::Clock, commitment_config::CommitmentLevel, compute_budget::ComputeBudgetInstruction, instruction::{AccountMeta, Instruction}, message::Message, + program_pack::Pack, pubkey::Pubkey, signature::Keypair, signer::Signer, @@ -47,40 +61,17 @@ use { sysvar::{self, Sysvar}, transaction::Transaction, }, - spl_associated_token_account::instruction::create_associated_token_account_idempotent, + spl_associated_token_account::{ + get_associated_token_address, instruction::create_associated_token_account_idempotent, + }, std::{ collections::HashMap, - fs, + fs, io, mem::size_of, ops::{Neg, Not}, time::{Duration, SystemTime, UNIX_EPOCH}, }, -}; - -#[cfg(feature = "dev")] -use marginfi::state::price::{OraclePriceFeedAdapter, PriceAdapter}; -use marginfi::{constants::ZERO_AMOUNT_THRESHOLD, utils::NumTraitsWithTolerance}; -use solana_client::rpc_client::RpcClient; - -#[cfg(feature = "admin")] -use { - crate::utils::{calc_emissions_rate, create_oracle_key_array, find_bank_emssions_auth_pda}, - marginfi::{ - constants::{EMISSIONS_FLAG_BORROW_ACTIVE, EMISSIONS_FLAG_LENDING_ACTIVE}, - prelude::GroupConfig, - state::marginfi_group::{ - BankConfig, BankConfigOpt, BankOperationalState, InterestRateConfig, WrappedI80F48, - }, - }, - solana_sdk::program_pack::Pack, - spl_associated_token_account::get_associated_token_address, - std::io, -}; - -#[cfg(feature = "lip")] -use { - chrono::{DateTime, NaiveDateTime, Utc}, - liquidity_incentive_program::state::{Campaign, Deposit}, + switchboard_solana::AggregatorAccountData, }; // -------------------------------------------------------------------------------------------------------------------- @@ -215,7 +206,6 @@ Last Update: {:?}h ago ({}) ) } -#[cfg(feature = "admin")] pub fn group_create( config: Config, profile: Profile, @@ -267,7 +257,6 @@ pub fn group_create( Ok(()) } -#[cfg(feature = "admin")] pub fn group_configure(config: Config, profile: Profile, admin: Option) -> Result<()> { let rpc_client = config.mfi_program.rpc(); @@ -305,7 +294,7 @@ pub fn group_configure(config: Config, profile: Profile, admin: Option) } #[allow(clippy::too_many_arguments)] -#[cfg(feature = "admin")] + pub fn group_add_bank( config: Config, profile: Profile, @@ -347,10 +336,11 @@ pub fn group_add_bank( let insurance_ir_fee: WrappedI80F48 = I80F48::from_num(insurance_ir_fee).into(); let protocol_fixed_fee_apr: WrappedI80F48 = I80F48::from_num(protocol_fixed_fee_apr).into(); let protocol_ir_fee: WrappedI80F48 = I80F48::from_num(protocol_ir_fee).into(); - let mint_account = rpc_client.get_account(&bank_mint)?; - let mint = spl_token::state::Mint::unpack(&mint_account.data)?; - + let token_program = mint_account.owner; + let mint = spl_token_2022::state::Mint::unpack( + &mint_account.data[..spl_token_2022::state::Mint::LEN], + )?; let deposit_limit = deposit_limit_ui * 10_u64.pow(mint.decimals as u32); let borrow_limit = borrow_limit_ui * 10_u64.pow(mint.decimals as u32); @@ -381,6 +371,7 @@ pub fn group_add_bank( profile, &rpc_client, bank_mint, + token_program, oracle_key, asset_weight_init, asset_weight_maint, @@ -398,6 +389,7 @@ pub fn group_add_bank( &config, profile, bank_mint, + token_program, &bank_keypair, oracle_key, asset_weight_init, @@ -413,8 +405,11 @@ pub fn group_add_bank( )? }; + let mut ixs = vec![ComputeBudgetInstruction::set_compute_unit_price(1)]; + ixs.extend(add_bank_ixs); + let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); - let message = Message::new(&add_bank_ixs, None); + let message = Message::new(&ixs, None); let mut transaction = Transaction::new_unsigned(message); transaction.partial_sign(&signing_keypairs, recent_blockhash); @@ -427,12 +422,13 @@ pub fn group_add_bank( } #[allow(clippy::too_many_arguments)] -#[cfg(feature = "admin")] + fn create_bank_ix_with_seed( config: &Config, profile: Profile, rpc_client: &RpcClient, bank_mint: Pubkey, + token_program: Pubkey, oracle_key: Pubkey, asset_weight_init: WrappedI80F48, asset_weight_maint: WrappedI80F48, @@ -456,7 +452,7 @@ fn create_bank_ix_with_seed( println!("Seed option enabled -- generating a PDA account"); let (pda, _) = Pubkey::find_program_address( [group_key.as_ref(), bank_mint.as_ref(), &i.to_le_bytes()].as_slice(), - &marginfi::id(), + &config.program_id, ); if rpc_client .get_account_with_commitment(&pda, CommitmentConfig::default())? @@ -510,7 +506,7 @@ fn create_bank_ix_with_seed( ) .0, rent: sysvar::rent::id(), - token_program: token::ID, + token_program, system_program: system_program::id(), fee_payer: config.authority(), }) @@ -542,11 +538,12 @@ fn create_bank_ix_with_seed( } #[allow(clippy::too_many_arguments)] -#[cfg(feature = "admin")] + fn create_bank_ix( config: &Config, profile: Profile, bank_mint: Pubkey, + token_program: Pubkey, bank_keypair: &Keypair, oracle_key: Pubkey, asset_weight_init: WrappedI80F48, @@ -604,7 +601,7 @@ fn create_bank_ix( ) .0, rent: sysvar::rent::id(), - token_program: token::ID, + token_program, system_program: system_program::id(), fee_payer: config.explicit_fee_payer(), }) @@ -635,7 +632,7 @@ fn create_bank_ix( } #[allow(clippy::too_many_arguments, dead_code)] -#[cfg(feature = "admin")] + pub fn group_handle_bankruptcy( config: &Config, profile: Profile, @@ -733,6 +730,11 @@ fn handle_bankruptcy_for_an_account( bank_pk: Pubkey, ) -> Result<()> { println!("Handling bankruptcy for bank {}", bank_pk); + + let bank = banks.get(&bank_pk).unwrap(); + + let bank_mint_account = rpc_client.get_account(&bank.mint)?; + let token_program = bank_mint_account.owner; let mut handle_bankruptcy_ix = Instruction { program_id: config.program_id, accounts: marginfi::accounts::LendingPoolHandleBankruptcy { @@ -758,12 +760,17 @@ fn handle_bankruptcy_for_an_account( &config.program_id, ) .0, - token_program: token::ID, + token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolHandleBankruptcy {}.data(), }; + if token_program == spl_token_2022::ID { + handle_bankruptcy_ix + .accounts + .push(AccountMeta::new_readonly(bank.mint, false)); + } handle_bankruptcy_ix .accounts .extend(load_observation_account_metas( @@ -789,10 +796,8 @@ fn handle_bankruptcy_for_an_account( Ok(()) } -#[cfg(feature = "admin")] const BANKRUPTCY_CHUNKS: usize = 4; -#[cfg(feature = "admin")] pub fn handle_bankruptcy_for_accounts( config: &Config, profile: &Profile, @@ -868,7 +873,7 @@ pub fn handle_bankruptcy_for_accounts( Ok(()) } -#[cfg(feature = "admin")] + fn make_bankruptcy_ix( config: &Config, profile: &Profile, @@ -878,6 +883,12 @@ fn make_bankruptcy_ix( bank_pk: Pubkey, ) -> Result { println!("Handling bankruptcy for bank {}", bank_pk); + let rpc_client = config.mfi_program.rpc(); + + let bank = banks.get(&bank_pk).unwrap(); + + let bank_mint_account = rpc_client.get_account(&bank.mint)?; + let token_program = bank_mint_account.owner; let mut handle_bankruptcy_ix = Instruction { program_id: config.program_id, accounts: marginfi::accounts::LendingPoolHandleBankruptcy { @@ -903,12 +914,17 @@ fn make_bankruptcy_ix( &config.program_id, ) .0, - token_program: token::ID, + token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolHandleBankruptcy {}.data(), }; + if token_program == spl_token_2022::ID { + handle_bankruptcy_ix + .accounts + .push(AccountMeta::new_readonly(bank.mint, false)); + } handle_bankruptcy_ix .accounts .extend(load_observation_account_metas( @@ -937,7 +953,7 @@ pub fn process_set_user_flag( } .to_account_metas(Some(true)), data: marginfi::instruction::SetAccountFlag { flag }.data(), - program_id: marginfi::id(), + program_id: config.program_id, }; let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); @@ -993,9 +1009,12 @@ pub fn bank_get(config: Config, bank_pk: Option) -> Result<()> { insurance_vault_balance.amount ); if bank.emissions_mint != Pubkey::default() { - let emissions_token_account = - find_bank_emssions_token_account_pda(address, bank.emissions_mint, marginfi::id()) - .0; + let emissions_token_account = find_bank_emssions_token_account_pda( + address, + bank.emissions_mint, + config.program_id, + ) + .0; let emissions_vault_balance = rpc_client.get_token_account_balance(&emissions_token_account)?; println!( @@ -1041,7 +1060,6 @@ pub fn bank_get_all(config: Config, marginfi_group: Option) -> Result<() Ok(()) } -#[cfg(feature = "dev")] pub fn bank_inspect_price_oracle(config: Config, bank_pk: Pubkey) -> Result<()> { use marginfi::state::price::{OraclePriceType, PriceBias}; @@ -1056,7 +1074,7 @@ pub fn bank_inspect_price_oracle(config: Config, bank_pk: Pubkey) -> Result<()> let opfa = OraclePriceFeedAdapter::try_from_bank_config_with_max_age( &bank.config, &[price_oracle_ai], - 0, + &Clock::default(), u64::MAX, ) .unwrap(); @@ -1097,18 +1115,12 @@ Prince: Ok(()) } -#[cfg(feature = "dev")] pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { - use marginfi::state::price::OracleSetup; - use pyth_sdk_solana::state::load_price_account; - use solana_sdk::{account::ReadableAccount, pubkey}; - use switchboard_v2::AggregatorAccountData; - let banks = config .mfi_program .accounts::(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8 + size_of::() + size_of::(), - pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") + solana_sdk::pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") .to_bytes() .to_vec(), ))])?; @@ -1120,11 +1132,11 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { b.config.oracle_setup, b.config.oracle_max_age, b.mint, - b.config.oracle_keys.clone().get(0).unwrap().clone(), + *b.config.oracle_keys.clone().first().unwrap(), ) }) .partition(|(setup, _, _, _)| match setup { - OracleSetup::PythEma => true, + OracleSetup::PythLegacy => true, OracleSetup::SwitchboardV2 => false, _ => panic!("Unknown oracle setup"), }); @@ -1142,28 +1154,28 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { let mut pyth_max_ages: HashMap = HashMap::from_iter( pyth_feeds .iter() - .map(|(max_age, mint, _)| (max_age.clone(), mint.clone())) + .map(|(max_age, mint, _)| (*max_age, *mint)) .map(|(max_age, mint)| (mint, (max_age, 0f64))), ); let mut swb_max_ages: HashMap = HashMap::from_iter( swb_feeds .iter() - .map(|(max_age, mint, _)| (max_age.clone(), mint.clone())) + .map(|(max_age, mint, _)| (*max_age, *mint)) .map(|(max_age, mint)| (mint, (max_age, 0f64))), ); loop { let pyth_keys = pyth_feeds .iter() - .map(|(_, _, key)| key.clone()) + .map(|(_, _, key)| *key) .collect::>(); let pyth_mints = pyth_feeds .iter() - .map(|(_, key, _)| key.clone()) + .map(|(_, key, _)| *key) .collect::>(); let pyth_max_age = pyth_feeds .iter() - .map(|(max_age, _, _)| max_age.clone()) + .map(|(max_age, _, _)| *max_age) .collect::>(); let pyth_feed_accounts = config .mfi_program @@ -1174,23 +1186,17 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { .zip(pyth_max_age) .map(|((maybe_account, mint), max_age)| { let account = maybe_account.unwrap(); - let pa = load_price_account(account.data()).unwrap().clone(); + let pa: SolanaPriceAccount = *load_price_account(account.data()).unwrap(); (mint, pa, max_age) }) .collect::>(); - let swb_keys = swb_feeds - .iter() - .map(|(_, _, key)| key.clone()) - .collect::>(); - let swb_mints = swb_feeds - .iter() - .map(|(_, key, _)| key.clone()) - .collect::>(); + let swb_keys = swb_feeds.iter().map(|(_, _, key)| *key).collect::>(); + let swb_mints = swb_feeds.iter().map(|(_, key, _)| *key).collect::>(); let swb_max_age = swb_feeds .iter() - .map(|(max_age, _, _)| max_age.clone()) + .map(|(max_age, _, _)| *max_age) .collect::>(); let swb_feed_accounts = config .mfi_program @@ -1201,9 +1207,7 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { .zip(swb_max_age) .map(|((maybe_account, mint), max_age)| { let account = maybe_account.unwrap(); - let pa = AggregatorAccountData::new_from_bytes(account.data()) - .unwrap() - .clone(); + let pa = *AggregatorAccountData::new_from_bytes(account.data()).unwrap(); (mint, pa, max_age) }) @@ -1216,7 +1220,7 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { let mut pyth_ages = pyth_feed_accounts .iter() - .map(|(mint, pa, _)| ((now - pa.get_publish_time()) as f64 / 60f64, mint.clone())) + .map(|(mint, pa, _)| ((now - pa.get_publish_time()) as f64 / 60f64, *mint)) .collect::>(); pyth_ages.sort_by(|(a, _), (b, _)| b.partial_cmp(a).unwrap()); @@ -1225,7 +1229,7 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { .map(|(mint, pa, _)| { ( (now - pa.latest_confirmed_round.round_open_timestamp) as f64 / 60f64, - mint.clone(), + *mint, ) }) .collect::>(); @@ -1273,7 +1277,7 @@ pub fn show_oracle_ages(config: Config, only_stale: bool) -> Result<()> { } #[allow(clippy::too_many_arguments)] -#[cfg(feature = "admin")] + pub fn bank_setup_emissions( config: &Config, profile: &Profile, @@ -1286,7 +1290,6 @@ pub fn bank_setup_emissions( ) -> Result<()> { let rpc_client = config.mfi_program.rpc(); - let funding_account_ata = get_associated_token_address(&config.authority(), &mint); let mut flags = 0; if deposits { @@ -1297,12 +1300,21 @@ pub fn bank_setup_emissions( flags |= EMISSIONS_FLAG_BORROW_ACTIVE; } - let emissions_mint_decimals = config.mfi_program.rpc().get_account(&mint).unwrap(); + let emissions_mint_account = config.mfi_program.rpc().get_account(&mint).unwrap(); + let token_program = emissions_mint_account.owner; - let emissions_mint_decimals = - spl_token::state::Mint::unpack_from_slice(&emissions_mint_decimals.data) - .unwrap() - .decimals; + let funding_account_ata = + anchor_spl::associated_token::get_associated_token_address_with_program_id( + &config.authority(), + &mint, + &token_program, + ); + + let emissions_mint = spl_token_2022::state::Mint::unpack( + &emissions_mint_account.data[..spl_token_2022::state::Mint::LEN], + ) + .unwrap(); + let emissions_mint_decimals = emissions_mint.decimals; let total_emissions = (total * 10u64.pow(emissions_mint_decimals as u32) as f64) as u64; let rate = crate::utils::calc_emissions_rate(rate, emissions_mint_decimals); @@ -1326,21 +1338,21 @@ pub fn bank_setup_emissions( } let ix = Instruction { - program_id: marginfi::id(), + program_id: config.program_id, accounts: marginfi::accounts::LendingPoolSetupEmissions { marginfi_group: profile.marginfi_group.expect("marginfi group not set"), admin: config.authority(), bank, emissions_mint: mint, - emissions_auth: find_bank_emssions_auth_pda(bank, mint, marginfi::id()).0, + emissions_auth: find_bank_emssions_auth_pda(bank, mint, config.program_id).0, emissions_token_account: find_bank_emssions_token_account_pda( bank, mint, - marginfi::id(), + config.program_id, ) .0, emissions_funding_account: funding_account_ata, - token_program: spl_token::id(), + token_program, system_program: system_program::id(), } .to_account_metas(Some(true)), @@ -1368,7 +1380,7 @@ pub fn bank_setup_emissions( } #[allow(clippy::too_many_arguments)] -#[cfg(feature = "admin")] + pub fn bank_update_emissions( config: &Config, profile: &Profile, @@ -1397,10 +1409,11 @@ pub fn bank_update_emissions( .get_account(&emission_mint) .unwrap(); - let emissions_mint_decimals = - spl_token::state::Mint::unpack_from_slice(&emissions_mint_decimals.data) - .unwrap() - .decimals; + let emissions_mint_decimals = spl_token_2022::state::Mint::unpack( + &emissions_mint_decimals.data[..spl_token_2022::state::Mint::LEN], + ) + .unwrap() + .decimals; let emissions_rate = rate.map(|rate| calc_emissions_rate(rate, emissions_mint_decimals)); let additional_emissions = additional_emissions @@ -1443,7 +1456,7 @@ pub fn bank_update_emissions( } let ix = Instruction { - program_id: marginfi::id(), + program_id: config.program_id, accounts: marginfi::accounts::LendingPoolUpdateEmissionsParameters { marginfi_group: profile.marginfi_group.expect("marginfi group not set"), admin: config.authority(), @@ -1452,7 +1465,7 @@ pub fn bank_update_emissions( emissions_token_account: find_bank_emssions_token_account_pda( bank_pk, emission_mint, - marginfi::id(), + config.program_id, ) .0, emissions_funding_account: funding_account_ata, @@ -1482,18 +1495,34 @@ pub fn bank_update_emissions( Ok(()) } -#[cfg(feature = "admin")] pub fn bank_configure( config: Config, profile: Profile, bank_pk: Pubkey, - bank_config_opt: BankConfigOpt, + mut bank_config_opt: BankConfigOpt, ) -> Result<()> { let rpc_client = config.mfi_program.rpc(); let configure_bank_ixs_builder = config.mfi_program.request(); let signing_keypairs = config.get_signers(false); + let mut extra_accounts = vec![]; + + if let Some(oracle) = &mut bank_config_opt.oracle { + extra_accounts.push(AccountMeta::new_readonly(oracle.keys[0], false)); + + if oracle.setup == OracleSetup::PythPushOracle { + let oracle_address = oracle.keys[0]; + let mut account = rpc_client.get_account(&oracle_address)?; + let ai = (&oracle_address, &mut account).into_account_info(); + let feed_id = PythPushOraclePriceFeed::peek_feed_id(&ai)?; + + let feed_id_as_pubkey = Pubkey::new_from_array(feed_id); + + oracle.keys[0] = feed_id_as_pubkey; + } + } + let mut configure_bank_ixs = configure_bank_ixs_builder .accounts(marginfi::accounts::LendingPoolConfigureBank { marginfi_group: profile.marginfi_group.unwrap(), @@ -1505,11 +1534,7 @@ pub fn bank_configure( }) .instructions()?; - if let Some(oracle) = &bank_config_opt.oracle { - configure_bank_ixs[0] - .accounts - .push(AccountMeta::new_readonly(oracle.keys[0], false)); - } + configure_bank_ixs[0].accounts.extend(extra_accounts); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let message = Message::new(&configure_bank_ixs, Some(&config.authority())); @@ -1849,10 +1874,16 @@ pub fn marginfi_account_deposit( bail!("Bank does not belong to group") } - let deposit_ata = - anchor_spl::associated_token::get_associated_token_address(&signer.pubkey(), &bank.mint); + let bank_mint_account = rpc_client.get_account(&bank.mint)?; + let token_program = bank_mint_account.owner; - let ix = Instruction { + let deposit_ata = anchor_spl::associated_token::get_associated_token_address_with_program_id( + &signer.pubkey(), + &bank.mint, + &token_program, + ); + + let mut ix = Instruction { program_id: config.program_id, accounts: marginfi::accounts::LendingAccountDeposit { marginfi_group: profile.marginfi_group.unwrap(), @@ -1861,11 +1892,15 @@ pub fn marginfi_account_deposit( bank: bank_pk, signer_token_account: deposit_ata, bank_liquidity_vault: bank.liquidity_vault, - token_program: token::ID, + token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingAccountDeposit { amount }.data(), }; + if token_program == spl_token_2022::ID { + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); + } let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let tx = Transaction::new_signed_with_payer( @@ -1915,8 +1950,14 @@ pub fn marginfi_account_withdraw( bail!("Bank does not belong to group") } - let withdraw_ata = - anchor_spl::associated_token::get_associated_token_address(&signer.pubkey(), &bank.mint); + let bank_mint_account = rpc_client.get_account(&bank.mint)?; + let token_program = bank_mint_account.owner; + + let withdraw_ata = anchor_spl::associated_token::get_associated_token_address_with_program_id( + &signer.pubkey(), + &bank.mint, + &token_program, + ); let mut ix = Instruction { program_id: config.program_id, @@ -1926,7 +1967,7 @@ pub fn marginfi_account_withdraw( signer: signer.pubkey(), bank: bank_pk, bank_liquidity_vault: bank.liquidity_vault, - token_program: token::ID, + token_program, destination_token_account: withdraw_ata, bank_liquidity_vault_authority: find_bank_vault_authority_pda( &bank_pk, @@ -1943,6 +1984,10 @@ pub fn marginfi_account_withdraw( .data(), }; + if token_program == spl_token_2022::ID { + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); + } ix.accounts.extend(load_observation_account_metas( &marginfi_account, &banks, @@ -1954,7 +1999,7 @@ pub fn marginfi_account_withdraw( &signer.pubkey(), &signer.pubkey(), &bank.mint, - &spl_token::ID, + &token_program, ); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); @@ -2004,8 +2049,14 @@ pub fn marginfi_account_borrow( bail!("Bank does not belong to group") } - let withdraw_ata = - anchor_spl::associated_token::get_associated_token_address(&signer.pubkey(), &bank.mint); + let bank_mint_account = rpc_client.get_account(&bank.mint)?; + let token_program = bank_mint_account.owner; + + let borrow_ata = anchor_spl::associated_token::get_associated_token_address_with_program_id( + &signer.pubkey(), + &bank.mint, + &token_program, + ); let mut ix = Instruction { program_id: config.program_id, @@ -2015,8 +2066,8 @@ pub fn marginfi_account_borrow( signer: signer.pubkey(), bank: bank_pk, bank_liquidity_vault: bank.liquidity_vault, - token_program: token::ID, - destination_token_account: withdraw_ata, + token_program, + destination_token_account: borrow_ata, bank_liquidity_vault_authority: find_bank_vault_authority_pda( &bank_pk, BankVaultType::Liquidity, @@ -2028,6 +2079,10 @@ pub fn marginfi_account_borrow( data: marginfi::instruction::LendingAccountBorrow { amount }.data(), }; + if token_program == spl_token_2022::ID { + ix.accounts + .push(AccountMeta::new_readonly(bank.mint, false)); + } ix.accounts.extend(load_observation_account_metas( &marginfi_account, &banks, @@ -2039,7 +2094,7 @@ pub fn marginfi_account_borrow( &signer.pubkey(), &signer.pubkey(), &bank.mint, - &spl_token::ID, + &token_program, ); let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); @@ -2102,6 +2157,9 @@ pub fn marginfi_account_liquidate( bail!("Liability bank does not belong to group") } + let liability_mint_account = rpc_client.get_account(&liability_bank.mint)?; + let token_program = liability_mint_account.owner; + let mut ix = Instruction { program_id: config.program_id, accounts: marginfi::accounts::LendingAccountLiquidate { @@ -2119,12 +2177,34 @@ pub fn marginfi_account_liquidate( .0, bank_liquidity_vault: liability_bank.liquidity_vault, bank_insurance_vault: liability_bank.insurance_vault, - token_program: token::ID, + token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingAccountLiquidate { asset_amount }.data(), }; + let oracle_accounts = vec![asset_bank.config, liability_bank.config] + .into_iter() + .map(|bank_config| { + let oracle_key = bank_to_oracle_key(&bank_config, PYTH_PUSH_PYTH_SPONSORED_SHARD_ID); + AccountMeta::new_readonly(oracle_key, false) + }); + + ix.accounts.extend(oracle_accounts); + + let oracle_accounts = vec![asset_bank.config, liability_bank.config] + .into_iter() + .map(|bank_config| { + let oracle_key = bank_to_oracle_key(&bank_config, PYTH_PUSH_PYTH_SPONSORED_SHARD_ID); + AccountMeta::new_readonly(oracle_key, false) + }); + + ix.accounts.extend(oracle_accounts); + + if token_program == spl_token_2022::ID { + ix.accounts + .push(AccountMeta::new_readonly(liability_bank.mint, false)); + } ix.accounts.push(AccountMeta { pubkey: asset_bank.config.oracle_keys[0], is_signer: false, @@ -2315,18 +2395,22 @@ fn timestamp_to_string(timestamp: i64) -> String { .to_string() } -// Switchboard tests -#[cfg(feature = "dev")] -pub fn process_inspect_switchboard_feed(config: &Config, aggregator_pk: &Pubkey) { - let aggregator_account_data = config - .mfi_program - .rpc() - .get_account_data(aggregator_pk) - .expect("Aggregator account not found"); +pub fn inspect_pyth_push_feed(config: &Config, address: Pubkey) -> anyhow::Result<()> { + let mut account = config.mfi_program.rpc().get_account(&address)?; + let ai = (&address, &mut account).into_account_info(); - let aggregator_account = - switchboard_v2::AggregatorAccountData::new_from_bytes(&aggregator_account_data) - .expect("Invalid aggregator account data"); + let mut data = &ai.try_borrow_data()?[8..]; + let price_update = PriceUpdateV2::deserialize(&mut data)?; - println!("Aggregator account: {:#?}", aggregator_account); + println!("Pyth Push Feed: {}", address); + let feed = PythPushOraclePriceFeed::load_unchecked(&ai)?; + + println!( + "Price: {}", + feed.get_price_of_type(marginfi::state::price::OraclePriceType::RealTime, None)? + ); + + println!("Feed id: {:?}", price_update.price_message.feed_id); + + Ok(()) } diff --git a/clients/rust/marginfi-cli/src/processor/oracle.rs b/clients/rust/marginfi-cli/src/processor/oracle.rs new file mode 100644 index 000000000..f3b89e276 --- /dev/null +++ b/clients/rust/marginfi-cli/src/processor/oracle.rs @@ -0,0 +1,47 @@ +use pyth_solana_receiver_sdk::price_update::FeedId; +use solana_account_decoder::UiAccountEncoding; +use solana_client::rpc_client::RpcClient; +use solana_client::rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}; +use solana_client::rpc_filter::{Memcmp, RpcFilterType}; +use solana_sdk::account_info::IntoAccountInfo; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub fn find_pyth_push_oracles_for_feed_id( + rpc_client: &RpcClient, + feed_id: FeedId, +) -> anyhow::Result<()> { + let mut res = rpc_client.get_program_accounts_with_config( + &pyth_solana_receiver_sdk::ID, + RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( + 8 + 32 + 1, + feed_id.to_vec(), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + ..Default::default() + }, + ..Default::default() + }, + )?; + + println!("Found {} price feeds", res.len()); + + for (ref address, account) in res.iter_mut() { + let ai = (address, account).into_account_info(); + let price_update_v2 = marginfi::state::price::load_price_update_v2_checked(&ai)?; + + let feed_id = &price_update_v2.price_message.feed_id; + let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; + let age_secs = current_timestamp - price_update_v2.price_message.publish_time; + let verification_level = price_update_v2.verification_level; + + let feed_id_hex = hex::encode(feed_id); + println!( + "Found pyth account {}, feed_id: 0x{}, min_verification_level: {:?}, age: {}s", + address, feed_id_hex, verification_level, age_secs + ); + } + + Ok(()) +} diff --git a/clients/rust/marginfi-cli/src/profile.rs b/clients/rust/marginfi-cli/src/profile.rs index 1cbb77c0d..73a3bcba2 100644 --- a/clients/rust/marginfi-cli/src/profile.rs +++ b/clients/rust/marginfi-cli/src/profile.rs @@ -1,7 +1,5 @@ -use crate::config::CliSigner; - use { - crate::config::{Config, GlobalOptions}, + crate::config::{CliSigner, Config, GlobalOptions}, anchor_client::{Client, Cluster}, anyhow::{anyhow, bail, Result}, dirs::home_dir, @@ -168,7 +166,6 @@ impl Profile { .unwrap_or_else(|| panic!("No marginfi account set for profile \"{}\"", self.name)) } - #[cfg(feature = "admin")] pub fn set_marginfi_group(&mut self, address: Pubkey) -> Result<()> { self.marginfi_group = Some(address); self.write_to_file()?; diff --git a/clients/rust/marginfi-cli/src/utils.rs b/clients/rust/marginfi-cli/src/utils.rs index 3fa72b556..e41e75474 100644 --- a/clients/rust/marginfi-cli/src/utils.rs +++ b/clients/rust/marginfi-cli/src/utils.rs @@ -1,18 +1,19 @@ -use crate::config::TxMode; -use marginfi::bank_seed; -use marginfi::constants::EMISSIONS_TOKEN_ACCOUNT_SEED; -#[cfg(feature = "admin")] -use marginfi::constants::{EMISSIONS_AUTH_SEED, MAX_ORACLE_KEYS}; use { + crate::config::TxMode, anyhow::{bail, Result}, fixed::types::I80F48, fixed_macro::types::I80F48, log::error, marginfi::{ - bank_authority_seed, + bank_authority_seed, bank_seed, + constants::{ + EMISSIONS_AUTH_SEED, EMISSIONS_TOKEN_ACCOUNT_SEED, MAX_ORACLE_KEYS, + PYTH_PUSH_PYTH_SPONSORED_SHARD_ID, + }, state::{ marginfi_account::MarginfiAccount, - marginfi_group::{Bank, BankVaultType}, + marginfi_group::{Bank, BankConfig, BankVaultType}, + price::PythPushOraclePriceFeed, }, }, solana_client::rpc_client::RpcClient, @@ -64,6 +65,21 @@ pub fn process_transaction( } } +pub fn bank_to_oracle_key(bank_config: &BankConfig, shard_id: u16) -> Pubkey { + let oracle_key_or_price_feed_id = bank_config.oracle_keys.first().unwrap(); + + match bank_config.oracle_setup { + marginfi::state::price::OracleSetup::PythPushOracle => { + PythPushOraclePriceFeed::find_oracle_address( + shard_id, + bank_config.get_pyth_push_oracle_feed_id().unwrap(), + ) + .0 + } + _ => *oracle_key_or_price_feed_id, + } +} + pub fn find_bank_vault_pda( bank_pk: &Pubkey, vault_type: BankVaultType, @@ -80,7 +96,6 @@ pub fn find_bank_vault_authority_pda( Pubkey::find_program_address(bank_authority_seed!(vault_type, bank_pk), program_id) } -#[cfg(feature = "admin")] pub fn find_bank_emssions_auth_pda( bank: Pubkey, emissions_mint: Pubkey, @@ -111,7 +126,6 @@ pub fn find_bank_emssions_token_account_pda( ) } -#[cfg(feature = "admin")] pub fn create_oracle_key_array(oracle_key: Pubkey) -> [Pubkey; MAX_ORACLE_KEYS] { let mut oracle_keys = [Pubkey::default(); MAX_ORACLE_KEYS]; oracle_keys[0] = oracle_key; @@ -167,6 +181,8 @@ pub fn load_observation_account_metas( .iter() .zip(bank_pks.iter()) .flat_map(|(bank, bank_pk)| { + let oracle_key = bank_to_oracle_key(&bank.config, PYTH_PUSH_PYTH_SPONSORED_SHARD_ID); + vec![ AccountMeta { pubkey: *bank_pk, @@ -174,7 +190,7 @@ pub fn load_observation_account_metas( is_writable: false, }, AccountMeta { - pubkey: bank.config.oracle_keys[0], + pubkey: oracle_key, is_signer: false, is_writable: false, }, @@ -184,12 +200,10 @@ pub fn load_observation_account_metas( account_metas } -#[cfg(feature = "admin")] pub fn calc_emissions_rate(ui_rate: f64, emissions_mint_decimals: u8) -> u64 { (ui_rate * 10u64.pow(emissions_mint_decimals as u32) as f64) as u64 } -#[cfg(feature = "admin")] pub fn ui_to_native(ui_amount: f64, decimals: u8) -> u64 { (ui_amount * (10u64.pow(decimals as u32) as f64)) as u64 } diff --git a/observability/indexer/src/utils/marginfi_account_dup.rs b/observability/indexer/src/utils/marginfi_account_dup.rs index b44fb5e46..d29554acd 100644 --- a/observability/indexer/src/utils/marginfi_account_dup.rs +++ b/observability/indexer/src/utils/marginfi_account_dup.rs @@ -25,8 +25,7 @@ impl BankAccountWithPriceFeed2 { .balances .into_iter() .filter(|balance| balance.active) - .enumerate() - .map(|(_, balance)| { + .map(|balance| { let bank = banks.get(&balance.bank_pk).cloned().unwrap(); let price_feed = price_feeds .get(&bank.config.oracle_keys[0]) diff --git a/observability/indexer/src/utils/metrics.rs b/observability/indexer/src/utils/metrics.rs index c4db7daf5..5e7babb29 100644 --- a/observability/indexer/src/utils/metrics.rs +++ b/observability/indexer/src/utils/metrics.rs @@ -470,6 +470,9 @@ impl MarginfiAccountMetrics { *oracle_pk, OraclePriceFeedAdapter::SwitchboardV2(pf.clone()), ), + OracleData::PythPush(pf) => { + (*oracle_pk, OraclePriceFeedAdapter::PythPush(pf.clone())) + } } })); diff --git a/observability/indexer/src/utils/snapshot.rs b/observability/indexer/src/utils/snapshot.rs index 25d0f891a..53c0d1036 100644 --- a/observability/indexer/src/utils/snapshot.rs +++ b/observability/indexer/src/utils/snapshot.rs @@ -2,6 +2,8 @@ use anchor_client::anchor_lang::AccountDeserialize; use anchor_client::anchor_lang::Discriminator; use fixed::types::I80F48; use itertools::Itertools; +use marginfi::constants::PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID; +use marginfi::constants::PYTH_PUSH_PYTH_SPONSORED_SHARD_ID; use marginfi::{ prelude::MarginfiGroup, state::{marginfi_account::MarginfiAccount, marginfi_group::Bank, price::*}, @@ -39,6 +41,7 @@ pub enum AccountRoutingType { Bank(Pubkey, BankUpdateRoutingType), PriceFeedPyth, PriceFeedSwitchboard, + PriceFeedPythPushOracle, } #[derive(Clone, Debug, PartialEq)] @@ -53,6 +56,7 @@ pub enum BankUpdateRoutingType { pub enum OracleData { Pyth(PythEmaPriceFeed), Switchboard(SwitchboardV2PriceFeed), + PythPush(PythPushOraclePriceFeed), } impl OracleData { @@ -68,6 +72,9 @@ impl OracleData { OracleData::Switchboard(price_feed) => price_feed .get_price_of_type(oracle_price_type, bias) .unwrap(), + OracleData::PythPush(price_feed) => price_feed + .get_price_of_type(oracle_price_type, bias) + .unwrap(), } } } @@ -224,6 +231,28 @@ impl Snapshot { .insert(oracle_address, AccountRoutingType::PriceFeedSwitchboard); accounts_to_fetch.push(oracle_address); } + OracleSetup::PythPushOracle => { + let feed_id = bank.config.oracle_keys[0].to_bytes(); + let (pyth_sponsored_oracle_address, _) = + PythPushOraclePriceFeed::find_oracle_address( + PYTH_PUSH_PYTH_SPONSORED_SHARD_ID, + &feed_id, + ); + let (mfi_sponsored_oracle_address, _) = + PythPushOraclePriceFeed::find_oracle_address( + PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID, + &feed_id, + ); + + self.routing_lookup.insert( + pyth_sponsored_oracle_address, + AccountRoutingType::PriceFeedPythPushOracle, + ); + self.routing_lookup.insert( + mfi_sponsored_oracle_address, + AccountRoutingType::PriceFeedPythPushOracle, + ); + } } self.banks.insert( @@ -323,6 +352,16 @@ impl Snapshot { self.price_feeds .insert(*account_pubkey, OracleData::Switchboard(pf)); } + AccountRoutingType::PriceFeedPythPushOracle => { + let mut account = account.clone(); + let ai = (account_pubkey, &mut account).into_account_info(); + let pf = PythPushOraclePriceFeed::load_unchecked(&ai).unwrap(); + let feed_id = PythPushOraclePriceFeed::peek_feed_id(&ai).unwrap(); + let feed_id_pk = Pubkey::new_from_array(feed_id); + + self.price_feeds + .insert(feed_id_pk, OracleData::PythPush(pf)); + } } } } diff --git a/programs/brick/Cargo.lock b/programs/brick/Cargo.lock index 5f92b2114..9c2af824b 100644 --- a/programs/brick/Cargo.lock +++ b/programs/brick/Cargo.lock @@ -4,23 +4,22 @@ version = 3 [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.11", "once_cell", "version_check", "zerocopy", @@ -28,126 +27,110 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anchor-attribute-access-control" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf7d535e1381be3de2c0716c0a1c1e32ad9df1042cddcf7bc18d743569e53319" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "anyhow", "proc-macro2", "quote", - "regex", "syn 1.0.109", ] [[package]] name = "anchor-attribute-account" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcd731f21048a032be27c7791701120e44f3f6371358fc4261a7f716283d29" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "anyhow", - "bs58 0.4.0", + "bs58 0.5.1", "proc-macro2", "quote", - "rustversion", "syn 1.0.109", ] [[package]] name = "anchor-attribute-constant" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1be64a48e395fe00b8217287f226078be2cf32dae42fdf8a885b997945c3d28" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-error" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ea6713d1938c0da03656ff8a693b17dc0396da66d1ba320557f07e86eca0d4" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-event" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401f11efb3644285685f8339829a9786d43ed7490bb1699f33c478d04d5a582" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "anyhow", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "anchor-attribute-interface" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6700a6f5c888a9c33fe8afc0c64fd8575fa28d05446037306d0f96102ae4480" +name = "anchor-attribute-program" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ + "anchor-lang-idl", "anchor-syn", "anyhow", + "bs58 0.5.1", "heck", "proc-macro2", "quote", + "serde_json", "syn 1.0.109", ] [[package]] -name = "anchor-attribute-program" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad769993b5266714e8939e47fbdede90e5c030333c7522d99a4d4748cf26712" +name = "anchor-derive-accounts" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "anyhow", - "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "anchor-attribute-state" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e677fae4a016a554acdd0e3b7f178d3acafaa7e7ffac6b8690cf4e171f1c116" +name = "anchor-derive-serde" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anchor-syn", - "anyhow", + "borsh-derive-internal 0.10.3", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "anchor-derive-accounts" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340beef6809d1c3fcc7ae219153d981e95a8a277ff31985bd7050e32645dc9a8" +name = "anchor-derive-space" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-syn", - "anyhow", "proc-macro2", "quote", "syn 1.0.109", @@ -155,52 +138,75 @@ dependencies = [ [[package]] name = "anchor-lang" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662ceafe667448ee4199a4be2ee83b6bb76da28566eee5cea05f96ab38255af8" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" 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", + "anchor-lang-idl", "arrayref", - "base64 0.13.1", + "base64 0.21.7", "bincode", - "borsh 0.9.3", + "borsh 0.10.3", "bytemuck", + "getrandom 0.2.15", "solana-program", "thiserror", ] +[[package]] +name = "anchor-lang-idl" +version = "0.1.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck", + "regex", + "serde", + "serde_json", + "sha2 0.10.8", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anyhow", + "serde", +] + [[package]] name = "anchor-syn" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0418bcb5daac3b8cb1b60d8fdb1d468ca36f5509f31fb51179326fae1028fdcc" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anyhow", - "bs58 0.3.1", + "bs58 0.5.1", + "cargo_toml", "heck", "proc-macro2", - "proc-macro2-diagnostics", "quote", "serde", "serde_json", - "sha2 0.9.9", + "sha2 0.10.8", "syn 1.0.109", "thiserror", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "ark-bn254" @@ -333,9 +339,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base64" @@ -345,15 +351,9 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bincode" @@ -366,15 +366,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -390,9 +384,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", @@ -440,6 +434,16 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive 1.5.1", + "cfg_aliases", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -448,7 +452,7 @@ checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", - "proc-macro-crate", + "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", ] @@ -461,11 +465,25 @@ checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", - "proc-macro-crate", + "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.70", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -515,25 +533,30 @@ name = "brick" version = "0.1.0" dependencies = [ "anchor-lang", + "borsh 0.10.3", + "solana-program", ] [[package]] name = "bs58" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bv" @@ -547,22 +570,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] @@ -571,14 +594,25 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cargo_toml" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" +dependencies = [ + "serde", + "toml 0.8.14", +] + [[package]] name = "cc" -version = "1.0.83" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -587,6 +621,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -615,44 +655,37 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -727,9 +760,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "feature-probe" @@ -763,9 +802,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -780,7 +819,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -789,9 +828,15 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.3.3" @@ -838,6 +883,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + [[package]] name = "itertools" version = "0.10.5" @@ -849,48 +904,48 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libsecp256k1" @@ -954,9 +1009,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -964,15 +1019,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -985,50 +1040,48 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-derive" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.70", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1041,15 +1094,15 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1057,9 +1110,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", @@ -1070,9 +1123,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -1095,36 +1148,55 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] -name = "proc-macro2" -version = "1.0.70" +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "unicode-ident", + "toml_edit 0.21.1", ] [[package]] -name = "proc-macro2-diagnostics" -version = "0.9.1" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ + "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", "version_check", - "yansi", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1188,7 +1260,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", ] [[package]] @@ -1211,9 +1283,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1221,9 +1293,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1231,18 +1303,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1252,9 +1324,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1263,9 +1335,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-hash" @@ -1284,15 +1356,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -1302,50 +1374,59 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1392,23 +1473,19 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "solana-frozen-abi" -version = "1.17.12" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40252b49d68e33eedb76498714b4b52f1ee7522a3bb52b8dbd0d6d9bacc5e2b" +checksum = "4867f66e9527fa44451c861c1dc6d9b2a7c7a668d7c6a297cdefbe39f4395b33" dependencies = [ - "ahash 0.8.6", - "blake3", "block-buffer 0.10.4", "bs58 0.4.0", "bv", - "byteorder", - "cc", "either", "generic-array", "im", @@ -1419,7 +1496,6 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "serde_json", "sha2 0.10.8", "solana-frozen-abi-macro", "subtle", @@ -1428,32 +1504,33 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.17.12" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a106613b9d798ca6562cb372a49d62ac2f507bb6a9bfd9afd5d0a7276b08068b" +checksum = "168f24d97347b85f05192df58d6be3e3047a4aadc4001bc1b9e711a5ec878eea" dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] name = "solana-program" -version = "1.17.12" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bdfd6532551b8d8c1ed15e1b6bd7a62f37f8eecc540ae833bda6ff20f24679" +checksum = "2bc5a636dc75e5c25651e34f7a36afc9ae60d38166687c5b0375abb580ac81a2" dependencies = [ "ark-bn254", "ark-ec", "ark-ff", "ark-serialize", - "base64 0.21.5", + "base64 0.21.7", "bincode", - "bitflags 2.4.1", + "bitflags", "blake3", "borsh 0.10.3", "borsh 0.9.3", + "borsh 1.5.1", "bs58 0.4.0", "bv", "bytemuck", @@ -1461,7 +1538,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.11", + "getrandom 0.2.15", "itertools", "js-sys", "lazy_static", @@ -1494,22 +1571,22 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.17.12" +version = "1.18.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47a1ff2ebb2fd8d50a592cb109cc6139b790de45bdbbfcfbbe7ecabb3725f7f" +checksum = "86c76414183a325038ff020b22c07d1e9d2da0703ddc0244acfed37ee2921d96" dependencies = [ "bs58 0.4.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -1524,33 +1601,45 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.70", +] + [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] @@ -1574,9 +1663,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1596,6 +1685,51 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1610,18 +1744,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "version_check" @@ -1643,9 +1777,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1653,24 +1787,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1678,28 +1812,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -1707,13 +1841,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -1722,70 +1857,88 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "yansi" -version = "0.5.1" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", ] [[package]] @@ -1805,5 +1958,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.70", ] diff --git a/programs/brick/Cargo.toml b/programs/brick/Cargo.toml index ff010c3f6..4c395500a 100644 --- a/programs/brick/Cargo.toml +++ b/programs/brick/Cargo.toml @@ -14,10 +14,12 @@ no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] default = [] +idl-build = ["anchor-lang/idl-build"] test = [] [profile.release] overflow-checks = true [dependencies] -anchor-lang = "0.26.0" +solana-program.workspace = true +anchor-lang.workspace = true diff --git a/programs/liquidity-incentive-program/Cargo.toml b/programs/liquidity-incentive-program/Cargo.toml index de3ef3da0..a4eec9871 100644 --- a/programs/liquidity-incentive-program/Cargo.toml +++ b/programs/liquidity-incentive-program/Cargo.toml @@ -14,6 +14,7 @@ no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] default = [] +idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] devnet = ["marginfi/devnet"] mainnet-beta = ["marginfi/mainnet-beta"] test = [] @@ -28,7 +29,7 @@ fixed = "1.12.0" [dependencies.marginfi] path = "../marginfi" -features = [ "cpi" ] +features = ["cpi"] [dev-dependencies] solana-logger = { workspace = true } @@ -41,4 +42,6 @@ assert_matches = "1.5.0" bincode = "1.3.3" futures = "0.3.25" pretty_assertions = "1.2.1" -fixtures = { path = "../../test-utils", package = "test-utilities", features = [ "lip" ] } +fixtures = { path = "../../test-utils", package = "test-utilities", features = [ + "lip", +] } diff --git a/programs/liquidity-incentive-program/src/instructions/create_campaign.rs b/programs/liquidity-incentive-program/src/instructions/create_campaign.rs index 23028e95d..e08ffb7f7 100644 --- a/programs/liquidity-incentive-program/src/instructions/create_campaign.rs +++ b/programs/liquidity-incentive-program/src/instructions/create_campaign.rs @@ -3,37 +3,40 @@ use crate::{ state::Campaign, }; use anchor_lang::prelude::*; -use anchor_spl::token::{transfer, Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use marginfi::state::marginfi_group::Bank; use std::mem::size_of; -pub fn process( - ctx: Context, +pub fn process<'info>( + ctx: Context<'_, '_, '_, 'info, CreateCampaign<'info>>, lockup_period: u64, max_deposits: u64, max_rewards: u64, ) -> Result<()> { require_gt!(max_deposits, 0); - transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.funding_account.to_account_info(), - to: ctx.accounts.campaign_reward_vault.to_account_info(), - authority: ctx.accounts.admin.to_account_info(), - }, - ), + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + ctx.accounts.token_program.key, + ctx.accounts.funding_account.to_account_info(), + ctx.accounts.asset_mint.to_account_info(), + ctx.accounts.campaign_reward_vault.to_account_info(), + ctx.accounts.admin.to_account_info(), + ctx.remaining_accounts, max_rewards, + ctx.accounts.asset_mint.decimals, + &[], // seeds )?; + // Get new balance. This will account for any fees + ctx.accounts.campaign_reward_vault.reload()?; + ctx.accounts.campaign.set_inner(Campaign { admin: ctx.accounts.admin.key(), lockup_period, active: true, max_deposits, remaining_capacity: max_deposits, - max_rewards, + max_rewards: ctx.accounts.campaign_reward_vault.amount, marginfi_bank_pk: ctx.accounts.marginfi_bank.key(), _padding: [0; 16], }); @@ -60,7 +63,7 @@ pub struct CreateCampaign<'info> { ], bump, )] - pub campaign_reward_vault: Box>, + pub campaign_reward_vault: Box>, #[account( seeds = [ CAMPAIGN_AUTH_SEED.as_bytes(), @@ -75,7 +78,7 @@ pub struct CreateCampaign<'info> { )] /// CHECK: Must match the mint of the marginfi bank, /// asserted by comparing the mint of the marginfi bank - pub asset_mint: AccountInfo<'info>, + pub asset_mint: InterfaceAccount<'info, Mint>, pub marginfi_bank: AccountLoader<'info, Bank>, #[account(mut)] pub admin: Signer<'info>, @@ -83,6 +86,6 @@ pub struct CreateCampaign<'info> { #[account(mut)] pub funding_account: AccountInfo<'info>, pub rent: Sysvar<'info, Rent>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } diff --git a/programs/liquidity-incentive-program/src/instructions/create_deposit.rs b/programs/liquidity-incentive-program/src/instructions/create_deposit.rs index 06b7334b0..c7ffa93e5 100644 --- a/programs/liquidity-incentive-program/src/instructions/create_deposit.rs +++ b/programs/liquidity-incentive-program/src/instructions/create_deposit.rs @@ -4,8 +4,9 @@ use crate::{ state::{Campaign, Deposit}, }; use anchor_lang::prelude::*; -use anchor_spl::token::{ - close_account, transfer, CloseAccount, Mint, Token, TokenAccount, Transfer, +use anchor_spl::{ + token_2022::{close_account, CloseAccount}, + token_interface::{Mint, TokenAccount, TokenInterface}, }; use marginfi::{program::Marginfi, state::marginfi_group::Bank}; use std::mem::size_of; @@ -22,7 +23,10 @@ use std::mem::size_of; /// # Errors /// * `LIPError::CampaignNotActive` if the relevant campaign is not active. /// * `LIPError::DepositAmountTooLarge` is the deposit amount exceeds the amount of remaining deposits that can be made into the campaign. -pub fn process(ctx: Context, amount: u64) -> Result<()> { +pub fn process<'info>( + ctx: Context<'_, '_, '_, 'info, CreateDeposit<'info>>, + amount: u64, +) -> Result<()> { require!(ctx.accounts.campaign.active, LIPError::CampaignNotActive); require_gte!( @@ -35,22 +39,22 @@ pub fn process(ctx: Context, amount: u64) -> Result<()> { msg!("User depositing {} tokens", amount); - transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.funding_account.to_account_info(), - to: ctx.accounts.temp_token_account.to_account_info(), - authority: ctx.accounts.signer.to_account_info(), - }, - ), + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + ctx.accounts.token_program.key, + ctx.accounts.funding_account.to_account_info(), + ctx.accounts.asset_mint.to_account_info(), + ctx.accounts.temp_token_account.to_account_info(), + ctx.accounts.signer.to_account_info(), + ctx.remaining_accounts, amount, + ctx.accounts.asset_mint.decimals, + &[], // seeds )?; let mfi_signer_seeds: &[&[u8]] = &[ DEPOSIT_MFI_AUTH_SIGNER_SEED.as_bytes(), &ctx.accounts.deposit.key().to_bytes(), - &[*ctx.bumps.get("mfi_pda_signer").unwrap()], + &[ctx.bumps.mfi_pda_signer], ]; marginfi::cpi::marginfi_account_initialize(CpiContext::new_with_signer( @@ -67,27 +71,36 @@ pub fn process(ctx: Context, amount: u64) -> Result<()> { &[ MARGINFI_ACCOUNT_SEED.as_bytes(), &ctx.accounts.deposit.key().to_bytes(), - &[*ctx.bumps.get("marginfi_account").unwrap()], + &[ctx.bumps.marginfi_account], ], ], ))?; - marginfi::cpi::lending_account_deposit( - CpiContext::new_with_signer( - ctx.accounts.marginfi_program.to_account_info(), - marginfi::cpi::accounts::LendingAccountDeposit { - marginfi_group: ctx.accounts.marginfi_group.to_account_info(), - marginfi_account: ctx.accounts.marginfi_account.to_account_info(), - signer: ctx.accounts.mfi_pda_signer.to_account_info(), - bank: ctx.accounts.marginfi_bank.to_account_info(), - signer_token_account: ctx.accounts.temp_token_account.to_account_info(), - bank_liquidity_vault: ctx.accounts.marginfi_bank_vault.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - &[mfi_signer_seeds], - ), - amount, - )?; + let signer_seeds = &[mfi_signer_seeds]; + let mut cpi_ctx = CpiContext::new_with_signer( + ctx.accounts.marginfi_program.to_account_info(), + marginfi::cpi::accounts::LendingAccountDeposit { + marginfi_group: ctx.accounts.marginfi_group.to_account_info(), + marginfi_account: ctx.accounts.marginfi_account.to_account_info(), + signer: ctx.accounts.mfi_pda_signer.to_account_info(), + bank: ctx.accounts.marginfi_bank.to_account_info(), + signer_token_account: ctx.accounts.temp_token_account.to_account_info(), + bank_liquidity_vault: ctx.accounts.marginfi_bank_vault.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + }, + signer_seeds, + ); + cpi_ctx.remaining_accounts = ctx.remaining_accounts.to_vec(); + + if marginfi::utils::nonzero_fee( + ctx.accounts.asset_mint.to_account_info(), + Clock::get()?.epoch, + )? { + msg!("nonzero transfer fee not supported"); + return Err(ProgramError::InvalidAccountData.into()); + } + + marginfi::cpi::lending_account_deposit(cpi_ctx, amount)?; close_account(CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), @@ -152,10 +165,10 @@ pub struct CreateDeposit<'info> { token::mint = asset_mint, token::authority = mfi_pda_signer, )] - pub temp_token_account: Box>, + pub temp_token_account: Box>, #[account(address = marginfi_bank.load()?.mint)] - pub asset_mint: Box>, + pub asset_mint: Box>, /// CHECK: Asserted by mfi cpi call /// marginfi_bank is tied to a specific marginfi_group @@ -187,7 +200,7 @@ pub struct CreateDeposit<'info> { /// CHECK: Asserted by CPI call pub marginfi_program: Program<'info, Marginfi>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, } diff --git a/programs/liquidity-incentive-program/src/instructions/end_deposit.rs b/programs/liquidity-incentive-program/src/instructions/end_deposit.rs index 3461dfc5f..2860e82a1 100644 --- a/programs/liquidity-incentive-program/src/instructions/end_deposit.rs +++ b/programs/liquidity-incentive-program/src/instructions/end_deposit.rs @@ -1,5 +1,8 @@ use anchor_lang::prelude::*; -use anchor_spl::token::{close_account, transfer, Token, TokenAccount, Transfer}; +use anchor_spl::{ + token_2022::{close_account, CloseAccount}, + token_interface::{Mint, TokenAccount, TokenInterface}, +}; use fixed::types::I80F48; use marginfi::{program::Marginfi, state::marginfi_group::Bank}; @@ -28,7 +31,7 @@ use crate::{ /// * Reloading ephemeral token account fails /// * Transferring additional reward to ephemeral token account fails /// * Reloading ephemeral token account after transfer fails -pub fn process(ctx: Context) -> Result<()> { +pub fn process<'info>(ctx: Context<'_, '_, '_, 'info, EndDeposit<'info>>) -> Result<()> { // Solana clock isn't the most precise, but an offset of a few hours on a half year lockup is fine // // Check if the lockup period has passed @@ -39,31 +42,31 @@ pub fn process(ctx: Context) -> Result<()> { LIPError::DepositNotMature ); - marginfi::cpi::lending_account_withdraw( - CpiContext::new_with_signer( - ctx.accounts.marginfi_program.to_account_info(), - marginfi::cpi::accounts::LendingAccountWithdraw { - marginfi_group: ctx.accounts.marginfi_group.to_account_info(), - marginfi_account: ctx.accounts.marginfi_account.to_account_info(), - signer: ctx.accounts.mfi_pda_signer.to_account_info(), - bank: ctx.accounts.marginfi_bank.to_account_info(), - destination_token_account: ctx.accounts.temp_token_account.to_account_info(), - bank_liquidity_vault: ctx.accounts.marginfi_bank_vault.to_account_info(), - bank_liquidity_vault_authority: ctx - .accounts - .marginfi_bank_vault_authority - .to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - &[&[ - DEPOSIT_MFI_AUTH_SIGNER_SEED.as_bytes(), - ctx.accounts.deposit.key().as_ref(), - &[*ctx.bumps.get("mfi_pda_signer").unwrap()], - ]], - ), - 0, - Some(true), - )?; + let deposit_key = ctx.accounts.deposit.key().to_bytes(); + let signer_seeds: &[&[&[u8]]] = &[&[ + DEPOSIT_MFI_AUTH_SIGNER_SEED.as_bytes(), + deposit_key.as_ref(), + &[ctx.bumps.mfi_pda_signer], + ]]; + let mut cpi_ctx = CpiContext::new_with_signer( + ctx.accounts.marginfi_program.to_account_info(), + marginfi::cpi::accounts::LendingAccountWithdraw { + marginfi_group: ctx.accounts.marginfi_group.to_account_info(), + marginfi_account: ctx.accounts.marginfi_account.to_account_info(), + signer: ctx.accounts.mfi_pda_signer.to_account_info(), + bank: ctx.accounts.marginfi_bank.to_account_info(), + destination_token_account: ctx.accounts.temp_token_account.to_account_info(), + bank_liquidity_vault: ctx.accounts.marginfi_bank_vault.to_account_info(), + bank_liquidity_vault_authority: ctx + .accounts + .marginfi_bank_vault_authority + .to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + }, + signer_seeds, + ); + cpi_ctx.remaining_accounts = ctx.remaining_accounts.to_vec(); + marginfi::cpi::lending_account_withdraw(cpi_ctx, 0, Some(true))?; // Redeem the shares with marginfi ctx.accounts.temp_token_account.reload()?; @@ -101,24 +104,24 @@ pub fn process(ctx: Context) -> Result<()> { // Transfer any additional rewards to the ephemeral token account if additional_reward_amount > 0 { - transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.campaign_reward_vault.to_account_info(), - to: ctx.accounts.temp_token_account.to_account_info(), - authority: ctx - .accounts - .campaign_reward_vault_authority - .to_account_info(), - }, - &[&[ - CAMPAIGN_AUTH_SEED.as_bytes(), - ctx.accounts.campaign.key().as_ref(), - &[*ctx.bumps.get("campaign_reward_vault_authority").unwrap()], - ]], - ), + let campaign_key = ctx.accounts.campaign.key(); + let campaign_auth_seeds: &[&[&[u8]]] = &[&[ + CAMPAIGN_AUTH_SEED.as_bytes(), + campaign_key.as_ref(), + &[ctx.bumps.campaign_reward_vault_authority], + ]]; + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + ctx.accounts.token_program.key, + ctx.accounts.campaign_reward_vault.to_account_info(), + ctx.accounts.asset_mint.to_account_info(), + ctx.accounts.temp_token_account.to_account_info(), + ctx.accounts + .campaign_reward_vault_authority + .to_account_info(), + ctx.remaining_accounts, additional_reward_amount, + ctx.accounts.asset_mint.decimals, + campaign_auth_seeds, )?; ctx.accounts.temp_token_account.reload()?; @@ -129,28 +132,28 @@ pub fn process(ctx: Context) -> Result<()> { ctx.accounts.temp_token_account.amount ); - // Transfer the total amount to the user - transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.temp_token_account.to_account_info(), - to: ctx.accounts.destination_account.to_account_info(), - authority: ctx.accounts.temp_token_account_authority.to_account_info(), - }, - &[&[ - TEMP_TOKEN_ACCOUNT_AUTH_SEED.as_bytes(), - ctx.accounts.deposit.key().as_ref(), - &[*ctx.bumps.get("temp_token_account_authority").unwrap()], - ]], - ), + // Transfer the total:: amount to the user + let temp_token_seeds: &[&[&[u8]]] = &[&[ + TEMP_TOKEN_ACCOUNT_AUTH_SEED.as_bytes(), + deposit_key.as_ref(), + &[ctx.bumps.temp_token_account_authority], + ]]; + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + ctx.accounts.token_program.key, + ctx.accounts.temp_token_account.to_account_info(), + ctx.accounts.asset_mint.to_account_info(), + ctx.accounts.destination_account.to_account_info(), + ctx.accounts.temp_token_account_authority.to_account_info(), + ctx.remaining_accounts, ctx.accounts.temp_token_account.amount, + ctx.accounts.asset_mint.decimals, + temp_token_seeds, )?; // Close the temp token account close_account(CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), - anchor_spl::token::CloseAccount { + CloseAccount { account: ctx.accounts.temp_token_account.to_account_info(), destination: ctx.accounts.signer.to_account_info(), authority: ctx.accounts.temp_token_account_authority.to_account_info(), @@ -158,7 +161,7 @@ pub fn process(ctx: Context) -> Result<()> { &[&[ TEMP_TOKEN_ACCOUNT_AUTH_SEED.as_bytes(), ctx.accounts.deposit.key().as_ref(), - &[*ctx.bumps.get("temp_token_account_authority").unwrap()], + &[ctx.bumps.temp_token_account_authority], ]], ))?; @@ -178,7 +181,7 @@ pub struct EndDeposit<'info> { ], bump, )] - pub campaign_reward_vault: Box>, + pub campaign_reward_vault: Box>, #[account( seeds = [ @@ -215,7 +218,7 @@ pub struct EndDeposit<'info> { token::mint = asset_mint, token::authority = temp_token_account_authority, )] - pub temp_token_account: Box>, + pub temp_token_account: Box>, #[account( seeds = [ @@ -233,7 +236,7 @@ pub struct EndDeposit<'info> { #[account(address = marginfi_bank.load()?.mint)] /// CHECK: Asserted by constraint - pub asset_mint: AccountInfo<'info>, + pub asset_mint: InterfaceAccount<'info, Mint>, #[account( mut, @@ -259,12 +262,15 @@ pub struct EndDeposit<'info> { #[account(mut)] pub marginfi_bank_vault: AccountInfo<'info>, + // /// CHECK: Asserted by CPI call + // #[account()] + // pub bank_mint: InterfaceAccount<'info, Mint>, /// CHECK: Asserted by CPI call #[account(mut)] pub marginfi_bank_vault_authority: AccountInfo<'info>, /// CHECK: Asserted by CPI call pub marginfi_program: Program<'info, Marginfi>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } diff --git a/programs/liquidity-incentive-program/src/lib.rs b/programs/liquidity-incentive-program/src/lib.rs index 1b99dd8c4..d47a96b46 100644 --- a/programs/liquidity-incentive-program/src/lib.rs +++ b/programs/liquidity-incentive-program/src/lib.rs @@ -30,8 +30,8 @@ pub mod liquidity_incentive_program { /// /// # Returns /// * `Ok(())` if the campaign was successfully created, or an error otherwise. - pub fn create_campaign( - ctx: Context, + pub fn create_campaign<'info>( + ctx: Context<'_, '_, '_, 'info, CreateCampaign<'info>>, lockup_period: u64, max_deposits: u64, max_rewards: u64, @@ -51,7 +51,10 @@ pub mod liquidity_incentive_program { /// # Errors /// * `LIPError::CampaignNotActive` if the relevant campaign is not active. /// * `LIPError::DepositAmountTooLarge` is the deposit amount exceeds the amount of remaining deposits that can be made into the campaign. - pub fn create_deposit(ctx: Context, amount: u64) -> Result<()> { + pub fn create_deposit<'info>( + ctx: Context<'_, '_, '_, 'info, CreateDeposit<'info>>, + amount: u64, + ) -> Result<()> { instructions::create_deposit::process(ctx, amount) } @@ -71,7 +74,7 @@ pub mod liquidity_incentive_program { /// * Reloading ephemeral token account fails /// * Transferring additional reward to ephemeral token account fails /// * Reloading ephemeral token account after transfer fails - pub fn end_deposit(ctx: Context) -> Result<()> { + pub fn end_deposit<'info>(ctx: Context<'_, '_, '_, 'info, EndDeposit<'info>>) -> Result<()> { instructions::end_deposit::process(ctx) } } diff --git a/programs/liquidity-incentive-program/tests/lip.rs b/programs/liquidity-incentive-program/tests/lip.rs index 79b7298e7..f9be67201 100644 --- a/programs/liquidity-incentive-program/tests/lip.rs +++ b/programs/liquidity-incentive-program/tests/lip.rs @@ -67,7 +67,7 @@ async fn campaign_no_yield() -> Result<()> { assert_eq!(campaign.max_deposits, native!(1000, "USDC")); assert_eq!(campaign.remaining_capacity, 0); - let destination_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let destination_account = test_f.usdc_mint.create_empty_token_account().await; let res = campaign_f .try_end_deposit(deposit_key, destination_account.key) .await; @@ -76,7 +76,7 @@ async fn campaign_no_yield() -> Result<()> { test_f.advance_time(time!(1, "s")).await; - let destination_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let destination_account = test_f.usdc_mint.create_empty_token_account().await; let res = campaign_f .try_end_deposit(deposit_key, destination_account.key) .await; @@ -105,6 +105,8 @@ async fn campaign_mixed_yield() -> Result<()> { // Setup test executor with non-admin payer let test_f = TestFixture::new(None).await; + test_f.set_time(0); + // Setup sample bank let usdc_bank = test_f .marginfi_group @@ -166,7 +168,7 @@ async fn campaign_mixed_yield() -> Result<()> { .try_bank_repay(usdc_borrowing_account.key, &usdc_bank, 500, Some(true)) .await?; - let destination_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let destination_account = test_f.usdc_mint.create_empty_token_account().await; campaign_f .try_end_deposit(deposit_key, destination_account.key) .await?; @@ -196,6 +198,8 @@ async fn campaign_max_yield() -> Result<()> { // Setup test executor with non-admin payer let test_f = TestFixture::new(None).await; + test_f.set_time(0); + // Setup sample bank let usdc_bank = test_f .marginfi_group @@ -257,7 +261,7 @@ async fn campaign_max_yield() -> Result<()> { .try_bank_repay(usdc_borrowing_account.key, &usdc_bank, 500, Some(true)) .await?; - let destination_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let destination_account = test_f.usdc_mint.create_empty_token_account().await; campaign_f .try_end_deposit(deposit_key, destination_account.key) .await?; @@ -319,7 +323,7 @@ async fn campaign_neg_yield() -> Result<()> { .set_asset_share_value(I80F48::from(usdc_bank.load().await.asset_share_value) / 2) .await; - let destination_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let destination_account = test_f.usdc_mint.create_empty_token_account().await; campaign_f .try_end_deposit(deposit_key, destination_account.key) .await?; diff --git a/programs/marginfi/Cargo.toml b/programs/marginfi/Cargo.toml index 164d86468..f787301a5 100644 --- a/programs/marginfi/Cargo.toml +++ b/programs/marginfi/Cargo.toml @@ -13,22 +13,32 @@ no-entrypoint = [] no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] -default = [] +default = ["mainnet-beta"] +idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] test-bpf = ["test", "debug"] test = [] client = [] devnet = [] mainnet-beta = [] debug = [] +staging = [] [dependencies] solana-program = { workspace = true } +spl-transfer-hook-interface = { workspace = true } +spl-tlv-account-resolution = { workspace = true } anchor-lang = { workspace = true } -anchor-spl = { workspace = true } +anchor-spl = { workspace = true, features = ["metadata"] } +anchor-lang-29 = { workspace = true } pyth-sdk-solana = { workspace = true } -switchboard-v2 = { workspace = true } +pyth-solana-receiver-sdk = { workspace = true } +switchboard-solana = { workspace = true } +borsh = "0.10.3" + +mpl-token-metadata = "4" +spl-token = "4.0" bytemuck = "1.9.1" cfg-if = "1.0.0" @@ -55,5 +65,6 @@ fixtures = { path = "../../test-utils", package = "test-utilities" } futures = "0.3.25" pretty_assertions = "1.2.1" rust_decimal = "*" +test-case = "3.3.1" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" diff --git a/programs/marginfi/fuzz/Cargo.lock b/programs/marginfi/fuzz/Cargo.lock index 61b2bd44e..8ff842fe6 100644 --- a/programs/marginfi/fuzz/Cargo.lock +++ b/programs/marginfi/fuzz/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.4.3" @@ -44,19 +69,19 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.12", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -71,66 +96,135 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anchor-attribute-access-control" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa5be5b72abea167f87c868379ba3c2be356bfca9e6f474fd055fa0f7eeb4f2" +checksum = "e5f619f1d04f53621925ba8a2e633ba5a6081f2ae14758cbb67f38fd823e0a3e" dependencies = [ - "anchor-syn", - "anyhow", + "anchor-syn 0.29.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", "proc-macro2", "quote", - "regex", "syn 1.0.109", ] [[package]] name = "anchor-attribute-account" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f468970344c7c9f9d03b4da854fd7c54f21305059f53789d0045c1dd803f0018" +checksum = "e7f2a3e1df4685f18d12a943a9f2a7456305401af21a07c9fe076ef9ecd6e400" dependencies = [ - "anchor-syn", - "anyhow", + "anchor-syn 0.29.0", + "bs58 0.5.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", "bs58 0.5.1", "proc-macro2", "quote", - "rustversion", "syn 1.0.109", ] [[package]] name = "anchor-attribute-constant" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59948e7f9ef8144c2aefb3f32a40c5fce2798baeec765ba038389e82301017ef" +checksum = "9423945cb55627f0b30903288e78baf6f62c6c8ab28fb344b6b25f1ffee3dca7" dependencies = [ - "anchor-syn", - "proc-macro2", + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-error" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc753c9d1c7981cb8948cf7e162fb0f64558999c0413058e2d43df1df5448086" +checksum = "93ed12720033cc3c3bf3cfa293349c2275cd5ab99936e33dd4bf283aaad3e241" dependencies = [ - "anchor-syn", - "proc-macro2", + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-event" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38b4e172ba1b52078f53fdc9f11e3dc0668ad27997838a0aad2d148afac8c97" +checksum = "eef4dc0371eba2d8c8b54794b0b0eb786a234a559b77593d6f80825b6d2c77a2" dependencies = [ - "anchor-syn", - "anyhow", + "anchor-syn 0.29.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", "proc-macro2", "quote", "syn 1.0.109", @@ -138,25 +232,72 @@ dependencies = [ [[package]] name = "anchor-attribute-program" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eebd21543606ab61e2d83d9da37d24d3886a49f390f9c43a1964735e8c0f0d5" +checksum = "b18c4f191331e078d4a6a080954d1576241c29c56638783322a18d308ab27e4f" +dependencies = [ + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ - "anchor-syn", + "anchor-lang-idl", + "anchor-syn 0.30.1", "anyhow", + "bs58 0.5.1", + "heck 0.3.3", "proc-macro2", "quote", + "serde_json", "syn 1.0.109", ] [[package]] name = "anchor-derive-accounts" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4720d899b3686396cced9508f23dab420f1308344456ec78ef76f98fda42af" +checksum = "5de10d6e9620d3bcea56c56151cad83c5992f50d5960b3a9bebc4a50390ddc3c" dependencies = [ - "anchor-syn", - "anyhow", + "anchor-syn 0.29.0", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e2e5be518ec6053d90a2a7f26843dbee607583c779e6c8395951b9739bdfbe" +dependencies = [ + "anchor-syn 0.29.0", + "borsh-derive-internal 0.10.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-syn 0.30.1", + "borsh-derive-internal 0.10.3", "proc-macro2", "quote", "syn 1.0.109", @@ -164,9 +305,19 @@ dependencies = [ [[package]] name = "anchor-derive-space" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f495e85480bd96ddeb77b71d499247c7d4e8b501e75ecb234e9ef7ae7bd6552a" +checksum = "1ecc31d19fa54840e74b7a979d44bcea49d70459de846088a1d71e87ba53c419" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "proc-macro2", "quote", @@ -175,50 +326,128 @@ dependencies = [ [[package]] name = "anchor-lang" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d4b20100f1310a774aba3471ef268e5c4ba4d5c28c0bbe663c2658acbc414" -dependencies = [ - "anchor-attribute-access-control", - "anchor-attribute-account", - "anchor-attribute-constant", - "anchor-attribute-error", - "anchor-attribute-event", - "anchor-attribute-program", - "anchor-derive-accounts", - "anchor-derive-space", +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35da4785497388af0553586d55ebdc08054a8b1724720ef2749d313494f2b8ad" +dependencies = [ + "anchor-attribute-access-control 0.29.0", + "anchor-attribute-account 0.29.0", + "anchor-attribute-constant 0.29.0", + "anchor-attribute-error 0.29.0", + "anchor-attribute-event 0.29.0", + "anchor-attribute-program 0.29.0", + "anchor-derive-accounts 0.29.0", + "anchor-derive-serde 0.29.0", + "anchor-derive-space 0.29.0", "arrayref", "base64 0.13.1", "bincode", "borsh 0.10.3", "bytemuck", - "getrandom 0.2.12", + "getrandom 0.2.15", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-lang" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-attribute-access-control 0.30.1", + "anchor-attribute-account 0.30.1", + "anchor-attribute-constant 0.30.1", + "anchor-attribute-error 0.30.1", + "anchor-attribute-event 0.30.1", + "anchor-attribute-program 0.30.1", + "anchor-derive-accounts 0.30.1", + "anchor-derive-serde 0.30.1", + "anchor-derive-space 0.30.1", + "arrayref", + "base64 0.21.7", + "bincode", + "borsh 0.10.3", + "bytemuck", + "getrandom 0.2.15", "solana-program", "thiserror", ] +[[package]] +name = "anchor-lang-idl" +version = "0.1.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck 0.3.3", + "serde", + "serde_json", + "sha2 0.10.8", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anyhow", + "serde", +] + [[package]] name = "anchor-spl" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f860599da1c2354e7234c768783049eb42e2f54509ecfc942d2e0076a2da7b" +checksum = "6c4fd6e43b2ca6220d2ef1641539e678bfc31b6cc393cf892b373b5997b6a39a" dependencies = [ - "anchor-lang", + "anchor-lang 0.29.0", "solana-program", - "spl-associated-token-account", - "spl-token 3.5.0", - "spl-token-2022", + "spl-associated-token-account 2.3.0", + "spl-token", + "spl-token-2022 0.9.0", +] + +[[package]] +name = "anchor-spl" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" +dependencies = [ + "anchor-lang 0.30.1", + "spl-associated-token-account 3.0.2", + "spl-pod 0.2.2", + "spl-token", + "spl-token-2022 3.0.2", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", ] [[package]] name = "anchor-syn" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a125e4b0cc046cfec58f5aa25038e34cf440151d58f0db3afc55308251fe936d" +checksum = "d9101b84702fed2ea57bd22992f75065da5648017135b844283a2f6d74f27825" +dependencies = [ + "anyhow", + "bs58 0.5.1", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.8", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.30.1" +source = "git+https://github.com/mrgnlabs/anchor.git?rev=fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d#fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" dependencies = [ "anyhow", "bs58 0.5.1", - "heck", + "heck 0.3.3", "proc-macro2", "quote", "serde", @@ -243,11 +472,34 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "aquamarine" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] name = "arbitrary" @@ -287,7 +539,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.13.2", - "itertools", + "itertools 0.10.5", "num-traits", "zeroize", ] @@ -304,8 +556,8 @@ dependencies = [ "ark-std", "derivative", "digest 0.10.7", - "itertools", - "num-bigint", + "itertools 0.10.5", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version", @@ -328,7 +580,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -357,7 +609,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -381,12 +633,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" @@ -399,28 +645,118 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "assert_matches" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-compression" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "az" @@ -428,6 +764,21 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.12.3" @@ -446,6 +797,18 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "1.3.3" @@ -462,14 +825,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bitmaps" -version = "2.1.0" +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitmaps" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.5.1" @@ -529,6 +913,16 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive 1.5.1", + "cfg_aliases", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -555,6 +949,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -599,6 +1007,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.4.0" @@ -630,24 +1059,46 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -656,14 +1107,52 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror", +] + [[package]] name = "cc" -version = "1.0.90" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -672,16 +1161,34 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", - "windows-targets 0.52.4", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", ] [[package]] @@ -693,6 +1200,81 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.16.1", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -713,12 +1295,28 @@ dependencies = [ "web-sys", ] +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "constant_time_eq" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -734,6 +1332,24 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -755,9 +1371,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -810,9 +1426,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -820,27 +1436,79 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.55", + "strsim 0.11.1", + "syn 2.0.58", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.55", + "syn 2.0.58", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.6", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", ] [[package]] @@ -868,7 +1536,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -877,6 +1545,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -897,12 +1583,67 @@ dependencies = [ "subtle", ] +[[package]] +name = "dir-diff" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" +dependencies = [ + "walkdir", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + [[package]] name = "ed25519" version = "1.5.3" @@ -938,22 +1679,82 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "enum_dispatch" -version = "0.3.13" +name = "encode_unicode" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.55", + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -975,17 +1776,70 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fast-math" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2465292146cdfc2011350fe3b1c616ac83cf0faeedb33463ba1c332ed8948d66" +dependencies = [ + "ieee754", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "feature-probe" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "fixed" -version = "1.23.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79386fdcec5e0fde91b1a6a5bcd89677d1f9304f7f986b154a1b9109038854d9" +checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", @@ -1028,12 +1882,141 @@ dependencies = [ "fixed-macro-impl", ] +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1045,6 +2028,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1060,9 +2053,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1071,16 +2064,61 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "goblin" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util 0.7.11", + "tracing", +] + [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -1090,20 +2128,29 @@ dependencies = [ "ahash 0.7.8", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.11", ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -1114,6 +2161,18 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1123,6 +2182,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex" version = "0.4.3" @@ -1132,6 +2197,12 @@ dependencies = [ "serde", ] +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + [[package]] name = "hmac" version = "0.8.1" @@ -1162,12 +2233,84 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1197,6 +2340,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ieee754" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" + [[package]] name = "im" version = "15.1.0" @@ -1214,50 +2373,137 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "2.2.6" +name = "include_dir" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ - "equivalent", - "hashbrown 0.14.3", + "include_dir_macros", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "include_dir_macros" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ - "either", + "proc-macro2", + "quote", ] [[package]] -name = "itoa" -version = "1.0.11" +name = "index_list" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "2cb725b6505e51229de32027e0cfcd9db29da4d89156f9747b0a5195643fa3e1" [[package]] -name = "jobserver" -version = "0.1.28" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "libc", + "autocfg", + "hashbrown 0.12.3", ] [[package]] -name = "js-sys" -version = "0.3.69" +name = "indexmap" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ - "wasm-bindgen", + "equivalent", + "hashbrown 0.14.5", ] [[package]] -name = "keccak" +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "keccak" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" @@ -1265,17 +2511,26 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libfuzzer-sys" @@ -1298,15 +2553,34 @@ dependencies = [ "base64 0.12.3", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.2.2", + "libsecp256k1-gen-ecmult 0.2.1", + "libsecp256k1-gen-genmult 0.2.1", "rand 0.7.3", "serde", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core 0.3.0", + "libsecp256k1-gen-ecmult 0.3.0", + "libsecp256k1-gen-genmult 0.3.0", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + [[package]] name = "libsecp256k1-core" version = "0.2.2" @@ -1318,13 +2592,33 @@ dependencies = [ "subtle", ] +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + [[package]] name = "libsecp256k1-gen-ecmult" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0", ] [[package]] @@ -1333,14 +2627,41 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0", +] + +[[package]] +name = "light-poseidon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +dependencies = [ + "ark-bn254", + "ark-ff", + "num-bigint 0.4.6", + "thiserror", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1348,11 +2669,12 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "serde", + "value-bag", ] [[package]] @@ -1389,12 +2711,42 @@ dependencies = [ "winapi", ] +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lz4" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eab492fe7f8651add23237ea56dbf11b3c4ff762ab83d40a47f11433421f91" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "marginfi" version = "0.1.0" dependencies = [ - "anchor-lang", - "anchor-spl", + "anchor-lang 0.30.1", + "anchor-spl 0.30.1", + "borsh 0.10.3", "bytemuck", "cfg-if", "enum_dispatch", @@ -1402,10 +2754,13 @@ dependencies = [ "fixed-macro", "lazy_static", "pyth-sdk-solana", + "pyth-solana-receiver-sdk", "solana-program", "solana-security-txt", + "spl-tlv-account-resolution 0.6.3", + "spl-transfer-hook-interface 0.6.3", "static_assertions", - "switchboard-v2", + "switchboard-solana", "type-layout", ] @@ -1413,14 +2768,16 @@ dependencies = [ name = "marginfi-fuzz" version = "0.0.0" dependencies = [ - "ahash 0.8.8", - "anchor-lang", + "anchor-lang 0.30.1", + "anchor-spl 0.30.1", "anyhow", "arbitrary", + "base64 0.22.1", "bumpalo", "bytemuck", "fixed", "fixed-macro", + "itertools 0.12.1", "lazy_static", "libfuzzer-sys", "log", @@ -1428,18 +2785,21 @@ dependencies = [ "marginfi", "once_cell", "pyth-sdk-solana", + "quinn-proto", "rand 0.8.5", "safe-transmute", "solana-program", + "solana-program-test", "solana-sdk", - "spl-token 4.0.0", + "spl-token", + "strum 0.26.3", ] [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1459,6 +2819,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -1481,1104 +2850,3929 @@ dependencies = [ ] [[package]] -name = "num-bigint" -version = "0.4.4" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "num-derive" -version = "0.3.3" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "adler", ] [[package]] -name = "num-integer" -version = "0.1.46" +name = "mio" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ - "num-traits", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] -name = "num-traits" -version = "0.2.18" +name = "mockall" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ - "autocfg", + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", ] [[package]] -name = "num_enum" -version = "0.5.11" +name = "mockall_derive" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ - "num_enum_derive 0.5.11", + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "num_enum" -version = "0.6.1" +name = "modular-bitfield" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" dependencies = [ - "num_enum_derive 0.6.1", + "modular-bitfield-impl", + "static_assertions", ] [[package]] -name = "num_enum_derive" -version = "0.5.11" +name = "modular-bitfield-impl" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "num_enum_derive" -version = "0.6.1" +name = "nix" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 2.0.55", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] [[package]] -name = "opaque-debug" -version = "0.3.1" +name = "normalize-line-endings" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "ordered-float" -version = "2.10.1" +name = "num" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", "num-traits", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "num-bigint" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "lock_api", - "parking_lot_core", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "parking_lot_core" -version = "0.9.9" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", + "num-integer", + "num-traits", ] [[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" +name = "num-complex" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ - "crypto-mac", + "autocfg", + "num-traits", ] [[package]] -name = "pbkdf2" -version = "0.11.0" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.7", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "polyval" -version = "0.5.3" +name = "num-derive" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "toml", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "once_cell", - "toml_edit", + "num-traits", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "num-iter" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "num-rational" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", ] [[package]] -name = "proc-macro2" -version = "1.0.79" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "unicode-ident", + "autocfg", ] [[package]] -name = "pyth-sdk" -version = "0.8.0" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7aeef4d5f0a9c98ff5af2ddd84a8b89919c512188305b497a9eb9afa97a949" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "borsh 0.10.3", - "borsh-derive 0.10.3", - "getrandom 0.2.12", - "hex", - "schemars", - "serde", + "hermit-abi 0.3.9", + "libc", ] [[package]] -name = "pyth-sdk-solana" -version = "0.8.0" +name = "num_enum" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa571ea6ea51102b8fc03303d0e6fea4f788f77bb4e0d65ae2d3c5e384e3187" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ - "borsh 0.10.3", - "borsh-derive 0.10.3", - "bytemuck", - "num-derive", - "num-traits", - "pyth-sdk", - "serde", - "solana-program", - "thiserror", + "num_enum_derive 0.6.1", ] [[package]] -name = "qstring" +name = "num_enum" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "percent-encoding", + "num_enum_derive 0.7.2", ] [[package]] -name = "quote" -version = "1.0.35" +name = "num_enum_derive" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ + "proc-macro-crate 1.3.1", "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "rand" -version = "0.7.3" +name = "num_enum_derive" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "rand" -version = "0.8.5" +name = "number_prefix" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "object" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "memchr", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "oid-registry" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "asn1-rs", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "once_cell" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "rand_core" -version = "0.6.4" +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.12", -] +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] -name = "rand_hc" -version = "0.2.0" +name = "openssl-probe" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] -name = "rand_xoshiro" -version = "0.6.0" +name = "opentelemetry" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" dependencies = [ - "rand_core 0.6.4", + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "js-sys", + "lazy_static", + "percent-encoding", + "pin-project", + "rand 0.8.5", + "thiserror", ] [[package]] -name = "rayon" -version = "1.10.0" +name = "ordered-float" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ - "either", - "rayon-core", + "num-traits", ] [[package]] -name = "rayon-core" -version = "1.12.1" +name = "os_str_bytes" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "aliasable", + "ouroboros_macro", ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "ouroboros_macro" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ - "bitflags", + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "regex" -version = "1.10.4" +name = "parking_lot" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "lock_api", + "parking_lot_core", ] [[package]] -name = "regex-automata" -version = "0.4.6" +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "cfg-if", + "libc", + "redox_syscall 0.5.2", + "smallvec", + "windows-targets 0.52.6", ] [[package]] -name = "regex-syntax" -version = "0.8.3" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "rust_decimal" -version = "1.26.1" +name = "pbkdf2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" dependencies = [ - "arrayvec", - "num-traits", - "serde", + "crypto-mac", ] [[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" +name = "pbkdf2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "semver", + "digest 0.10.7", ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "pem" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] [[package]] -name = "ryu" -version = "1.0.17" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "safe-transmute" -version = "0.11.2" +name = "percentage" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a01dab6acf992653be49205bdd549f32f17cb2803e8eacf1560bf97259aae8" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num", +] [[package]] -name = "schemars" -version = "0.8.16" +name = "pin-project" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", + "pin-project-internal", ] [[package]] -name = "schemars_derive" -version = "0.8.16" +name = "pin-project-internal" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "semver" -version = "1.0.22" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "serde" -version = "1.0.197" +name = "pkcs8" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" dependencies = [ - "serde_derive", + "der", + "spki", + "zeroize", ] [[package]] -name = "serde-value" -version = "0.7.0" +name = "pkg-config" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "ordered-float", - "serde", + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", ] [[package]] -name = "serde_bytes" -version = "0.11.14" +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ - "serde", + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex", ] [[package]] -name = "serde_derive" -version = "1.0.197" +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", + "predicates-core", + "termtree", ] [[package]] -name = "serde_derive_internals" -version = "0.26.0" +name = "proc-macro-crate" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "toml", ] [[package]] -name = "serde_json" -version = "1.0.115" +name = "proc-macro-crate" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "itoa", - "ryu", - "serde", + "once_cell", + "toml_edit 0.19.15", ] [[package]] -name = "serde_with" -version = "2.3.3" +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "serde", - "serde_with_macros", + "toml_edit 0.21.1", ] [[package]] -name = "serde_with_macros" -version = "2.3.3" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "darling", + "proc-macro-error-attr", "proc-macro2", "quote", - "syn 2.0.55", + "syn 1.0.109", + "version_check", ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", + "proc-macro2", + "quote", + "version_check", ] [[package]] -name = "sha2" -version = "0.9.9" +name = "proc-macro2" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "unicode-ident", ] [[package]] -name = "sha2" -version = "0.10.8" +name = "ptr_meta" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", + "ptr_meta_derive", ] [[package]] -name = "sha3" -version = "0.9.1" +name = "ptr_meta_derive" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "sha3" -version = "0.10.8" +name = "pyth-sdk" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "1e7aeef4d5f0a9c98ff5af2ddd84a8b89919c512188305b497a9eb9afa97a949" dependencies = [ - "digest 0.10.7", - "keccak", + "borsh 0.10.3", + "borsh-derive 0.10.3", + "getrandom 0.2.15", + "hex", + "schemars", + "serde", ] [[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - -[[package]] -name = "sized-chunks" -version = "0.6.5" +name = "pyth-sdk-solana" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +checksum = "7f913de6eb29d8def199af3beaee645e84c5281327d58777eff3fdd9f1d37105" dependencies = [ - "bitmaps", - "typenum", + "borsh 0.10.3", + "borsh-derive 0.10.3", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "pyth-sdk", + "serde", + "solana-program", + "thiserror", ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "pyth-solana-receiver-sdk" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "91e6559643f0b377b6f293269251f6a804ae7332c37f7310371f50c833453cd0" +dependencies = [ + "anchor-lang 0.29.0", + "hex", + "pythnet-sdk", + "solana-program", +] [[package]] -name = "solana-frozen-abi" -version = "1.16.25" +name = "pythnet-sdk" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7077f6495ccc313dff49c3e3f3ed03e49058258bae7fee77ac29ba0a474ba82" +checksum = "3bbbc0456f9f27c9ad16b6c3bf1b2a7fea61eebf900f4d024a0468b9a84fe0c1" dependencies = [ - "ahash 0.8.8", - "blake3", - "block-buffer 0.10.4", - "bs58 0.4.0", - "bv", + "anchor-lang 0.29.0", + "bincode", + "borsh 0.10.3", + "bytemuck", "byteorder", - "cc", - "either", - "generic-array", - "getrandom 0.1.16", - "im", - "lazy_static", - "log", - "memmap2", - "once_cell", - "rand_core 0.6.4", + "fast-math", + "hex", + "proc-macro2", "rustc_version", "serde", - "serde_bytes", - "serde_derive", - "serde_json", - "sha2 0.10.8", - "solana-frozen-abi-macro", - "subtle", + "sha3 0.10.8", + "slow_primes", + "solana-program", "thiserror", ] [[package]] -name = "solana-frozen-abi-macro" -version = "1.16.25" +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "qualifier_attr" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f516f992211a2ab70de5c367190575c97e02d156f9f1d8b76886d673f30e88a2" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "rustc_version", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] -name = "solana-logger" -version = "1.16.25" +name = "quinn" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b64def674bfaa4a3f8be7ba19c03c9caec4ec028ba62b9a427ec1bf608a2486" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ - "env_logger", - "lazy_static", - "log", + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", ] [[package]] -name = "solana-program" -version = "1.16.25" +name = "quinn-proto" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e92350aa5b42564681655331e7e0b9d5c99a442de317ceeb4741efbbe9a6c05" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-serialize", - "array-bytes", - "base64 0.21.7", - "bincode", - "bitflags", - "blake3", - "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.2.12", - "itertools", - "js-sys", - "lazy_static", - "libc", - "libsecp256k1", - "log", - "memoffset 0.9.1", - "num-bigint", - "num-derive", - "num-traits", - "parking_lot", - "rand 0.7.3", - "rand_chacha 0.2.2", - "rustc_version", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "serde_json", - "sha2 0.10.8", - "sha3 0.10.8", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-sdk-macro", + "arbitrary", + "bytes", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls", + "rustls-native-certs", + "slab", "thiserror", - "tiny-bip39", - "wasm-bindgen", - "zeroize", + "tinyvec", + "tracing", ] [[package]] -name = "solana-sdk" -version = "1.16.25" +name = "quinn-udp" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2087e15c92d4d6b3f085dc12fbe9614141c811f90a54cc418240ac30b608133f" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ - "assert_matches", - "base64 0.21.7", + "bytes", + "libc", + "socket2", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util 0.7.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rust_decimal" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +dependencies = [ + "arrayvec", + "borsh 1.5.1", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "safe-transmute" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944826ff8fa8093089aba3acb4ef44b9446a99a16f3bf4e74af3f77d340ab7d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.58", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "seqlock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5c67b6f14ecc5b86c66fa63d76b5092352678545a8a3cdae80aef5128371910" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slow_primes" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58267dd2fbaa6dceecba9e3e106d2d90a2b02497c0e8b01b8759beccf5113938" +dependencies = [ + "num", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "solana-account-decoder" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4973213a11c2e1b924b36e0c6688682b5aa4623f8d4eeaa1204c32cee524e6d6" +dependencies = [ + "Inflector", + "base64 0.21.7", + "bincode", + "bs58 0.4.0", + "bv", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-config-program", + "solana-sdk", + "spl-token", + "spl-token-2022 1.0.0", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-accounts-db" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c06263320e399af20d46c8cebea7a1d5dc1bc56f31f8dfaacf7119576c48a7" +dependencies = [ + "arrayref", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "flate2", + "fnv", + "im", + "index_list", + "itertools 0.10.5", + "lazy_static", + "log", + "lz4", + "memmap2", + "modular-bitfield", + "num-derive 0.4.2", + "num-traits", + "num_cpus", + "num_enum 0.7.2", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "seqlock", + "serde", + "serde_derive", + "smallvec", + "solana-bucket-map", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-nohash-hasher", + "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.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e57cb8f2e90361280b246f70bb7f5f86f4e4ff1ad5bbdfe18a81bea141f03a" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive 0.4.2", + "num-traits", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-banks-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c65a9540370523f3ade7190526309337cc50f1d742b3341dfa7357da3f59a56" +dependencies = [ + "borsh 1.5.1", + "futures", + "solana-banks-interface", + "solana-program", + "solana-sdk", + "tarpc", + "thiserror", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-banks-interface" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b1dc20a7a71cf37bcbc2a3a5dfd73d7410a13850aa68d954a9c09e6a77e652" +dependencies = [ + "serde", + "solana-sdk", + "tarpc", +] + +[[package]] +name = "solana-banks-server" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d449d55d3c5c3fe4c9f0c9f790a9feabe294f8ff0b4c6b771a20b2313ad8974a" +dependencies = [ + "bincode", + "crossbeam-channel", + "futures", + "solana-accounts-db", + "solana-banks-interface", + "solana-client", + "solana-runtime", + "solana-sdk", + "solana-send-transaction-service", + "tarpc", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-bpf-loader-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1a55b8533f2dc716602e7c1b2bd555d5ac598ef6e80d28a517e6f31baf042e" +dependencies = [ + "bincode", + "byteorder", + "libsecp256k1 0.6.0", + "log", + "scopeguard", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-bucket-map" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda213af7ae26ce249120f211060d2a85d87fe367c6490ee19b70845cbd320fc" +dependencies = [ + "bv", + "bytemuck", + "log", + "memmap2", + "modular-bitfield", + "num_enum 0.7.2", + "rand 0.8.5", + "solana-measure", + "solana-sdk", + "tempfile", +] + +[[package]] +name = "solana-clap-utils" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909f4553d0b31bb5b97533a6b64cc321a4eace9112d6efbabcf4408ea1b3f1db" +dependencies = [ + "chrono", + "clap 2.34.0", + "rpassword", + "solana-remote-wallet", + "solana-sdk", + "thiserror", + "tiny-bip39", + "uriparse", + "url", +] + +[[package]] +name = "solana-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5cc431df6cc1dd964134fa4ec7df765d3af3fae9c2148f96a3c4fb500290633" +dependencies = [ + "async-trait", + "bincode", + "dashmap", + "futures", + "futures-util", + "indexmap 2.2.6", + "indicatif", + "log", + "quinn", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-quic-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-rpc-client-nonce-utils", + "solana-sdk", + "solana-streamer", + "solana-thin-client", + "solana-tpu-client", + "solana-udp-client", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-compute-budget-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb36ef3c3a1f38515c1ae0d255c4d6e5e635a856ac2aa1cd5f892b3db58e857" +dependencies = [ + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-config-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e38b040d3a42e8f7d80c4a86bb0d49d7aed663b56b0fe0ae135d2d145fb7ae3a" +dependencies = [ + "bincode", + "chrono", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-connection-cache" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae02622c63943485f0af3d0896626eaf6478e734f0b6bc61c7cc5320963c6e75" +dependencies = [ + "async-trait", + "bincode", + "crossbeam-channel", + "futures-util", + "indexmap 2.2.6", + "log", + "rand 0.8.5", + "rayon", + "rcgen", + "solana-measure", + "solana-metrics", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-cost-model" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "838532d8437d00958621d2589d6033e9c69ea95cd0936efa8964146e49dcff53" +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-frozen-abi" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4867f66e9527fa44451c861c1dc6d9b2a7c7a668d7c6a297cdefbe39f4395b33" +dependencies = [ + "block-buffer 0.10.4", + "bs58 0.4.0", + "bv", + "either", + "generic-array", + "im", + "lazy_static", + "log", + "memmap2", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "sha2 0.10.8", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168f24d97347b85f05192df58d6be3e3047a4aadc4001bc1b9e711a5ec878eea" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.58", +] + +[[package]] +name = "solana-loader-v4-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c426482234b7c267a5e0dfa8198442e1ffad2ad6c521f6b810949bc2719215" +dependencies = [ + "log", + "solana-measure", + "solana-program-runtime", + "solana-sdk", + "solana_rbpf", +] + +[[package]] +name = "solana-logger" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0511082fc62f2d086520fff5aa1917c389d8c840930c08ad255ae05952c08a2" +dependencies = [ + "env_logger", + "lazy_static", + "log", +] + +[[package]] +name = "solana-measure" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55a3df105431d25f86f2a7da0cbbde5f54c1f0782ca59367ea4a8037bc6797" +dependencies = [ + "log", + "solana-sdk", +] + +[[package]] +name = "solana-metrics" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddec097ed7572804389195128dbd57958b427829153c6cd8ec3343c86fe3cd22" +dependencies = [ + "crossbeam-channel", + "gethostname", + "lazy_static", + "log", + "reqwest", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-net-utils" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258fa7c29fb7605b8d2ed89aa0d43c640d14f4147ad1f5b3fdad19a1ac145ca5" +dependencies = [ + "bincode", + "clap 3.2.25", + "crossbeam-channel", + "log", + "nix", + "rand 0.8.5", + "serde", + "serde_derive", + "socket2", + "solana-logger", + "solana-sdk", + "solana-version", + "tokio", + "url", +] + +[[package]] +name = "solana-nohash-hasher" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8a731ed60e89177c8a7ab05fe0f1511cedd3e70e773f288f9de33a9cfdc21e" + +[[package]] +name = "solana-perf" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca422edcf16a6e64003ca118575ea641f7b750f14a0ad28c71dd84f33dcb912a" +dependencies = [ + "ahash 0.8.11", + "bincode", + "bv", + "caps", + "curve25519-dalek", + "dlopen2", + "fnv", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "rayon", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc5a636dc75e5c25651e34f7a36afc9ae60d38166687c5b0375abb580ac81a2" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "base64 0.21.7", + "bincode", + "bitflags 2.6.0", + "blake3", + "borsh 0.10.3", + "borsh 0.9.3", + "borsh 1.5.1", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.15", + "itertools 0.10.5", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1 0.6.0", + "light-poseidon", + "log", + "memoffset 0.9.1", + "num-bigint 0.4.6", + "num-derive 0.4.2", + "num-traits", + "parking_lot", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-program-runtime" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf373c3da0387f47fee4c5ed2465a9628b9db026a62211a692a9285aa9251544" +dependencies = [ + "base64 0.21.7", + "bincode", + "eager", + "enum-iterator", + "itertools 0.10.5", + "libc", + "log", + "num-derive 0.4.2", + "num-traits", + "percentage", + "rand 0.8.5", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-program-test" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9194b8744c5b135401ab4a2923a1072d3a67697bd50f7450a4ed5302f36a6999" +dependencies = [ + "assert_matches", + "async-trait", + "base64 0.21.7", + "bincode", + "chrono-humanize", + "crossbeam-channel", + "log", + "serde", + "solana-accounts-db", + "solana-banks-client", + "solana-banks-interface", + "solana-banks-server", + "solana-bpf-loader-program", + "solana-logger", + "solana-program-runtime", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "solana_rbpf", + "test-case", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-pubsub-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b9abc76168d19927561db6a3685b98752bd0961b4ce4f8b7f85ee12238c017" +dependencies = [ + "crossbeam-channel", + "futures-util", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-quic-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7952c5306a0be5f5276448cd20246b31265bfa884f29a077a24303c6a16aeb34" +dependencies = [ + "async-mutex", + "async-trait", + "futures", + "itertools 0.10.5", + "lazy_static", + "log", + "quinn", + "quinn-proto", + "rcgen", + "rustls", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-rpc-client-api", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4fa0cc66f8e73d769bca2ede3012ba2ef8ab67963e832808665369f2cf81743" +dependencies = [ + "lazy_static", + "num_cpus", +] + +[[package]] +name = "solana-remote-wallet" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "289803796d4ff7b4699504d3ab9e9d9c5205ea3892b2ebe397b377494dbd75d4" +dependencies = [ + "console", + "dialoguer", + "log", + "num-derive 0.4.2", + "num-traits", + "parking_lot", + "qstring", + "semver", + "solana-sdk", + "thiserror", + "uriparse", +] + +[[package]] +name = "solana-rpc-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb55a08018776a62ecff52139fbcdab1a7baa4e8f077202be58156e8dde4d5f" +dependencies = [ + "async-trait", + "base64 0.21.7", + "bincode", + "bs58 0.4.0", + "indicatif", + "log", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-rpc-client-api", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "solana-vote-program", + "tokio", +] + +[[package]] +name = "solana-rpc-client-api" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a8403038f4d6ab65bc7e7afb3afe8d9824c592232553c5cef55cf3de36025d" +dependencies = [ + "base64 0.21.7", + "bs58 0.4.0", + "jsonrpc-core", + "reqwest", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-sdk", + "solana-transaction-status", + "solana-version", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "solana-rpc-client-nonce-utils" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caca735caf76d51c074c3bacbfe38094bf7f92cfbe7b5b13f3bc4946e64f889" +dependencies = [ + "clap 2.34.0", + "solana-clap-utils", + "solana-rpc-client", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-runtime" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699943045665038bfa4e76dd2582b4c390f1aec6ab5edef36da43afe3469f1d" +dependencies = [ + "aquamarine", + "arrayref", + "base64 0.21.7", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "dir-diff", + "flate2", + "fnv", + "im", + "index_list", + "itertools 0.10.5", + "lazy_static", + "log", + "lru", + "lz4", + "memmap2", + "mockall", + "modular-bitfield", + "num-derive 0.4.2", + "num-traits", + "num_cpus", + "num_enum 0.7.2", + "ouroboros", + "percentage", + "qualifier_attr", + "rand 0.8.5", + "rayon", + "regex", + "rustc_version", + "serde", + "serde_derive", + "serde_json", + "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", + "solana-measure", + "solana-metrics", + "solana-perf", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-system-program", + "solana-version", + "solana-vote", + "solana-vote-program", + "solana-zk-token-proof-program", + "solana-zk-token-sdk", + "static_assertions", + "strum 0.24.1", + "strum_macros 0.24.3", + "symlink", + "tar", + "tempfile", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-sdk" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df43d3a1e1637397ab43cbc216a5a8f977ec8a3cc3f3ae8c3851c83a3255dbcf" +dependencies = [ + "assert_matches", + "base64 0.21.7", + "bincode", + "bitflags 2.6.0", + "borsh 1.5.1", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools 0.10.5", + "js-sys", + "lazy_static", + "libsecp256k1 0.6.0", + "log", + "memmap2", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.2", + "pbkdf2 0.11.0", + "qstring", + "qualifier_attr", + "rand 0.7.3", + "rand 0.8.5", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_with", + "sha2 0.10.8", + "sha3 0.10.8", + "siphasher", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86c76414183a325038ff020b22c07d1e9d2da0703ddc0244acfed37ee2921d96" +dependencies = [ + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-send-transaction-service" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e056d865d22548bb7228121e118aa632486fc1a33a100961e5e98b5663371384" +dependencies = [ + "crossbeam-channel", + "log", + "solana-client", + "solana-measure", + "solana-metrics", + "solana-runtime", + "solana-sdk", + "solana-tpu-client", +] + +[[package]] +name = "solana-stake-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5dd1bc07beb75da5df5e07301d3d0d6104872c9afade22b910af9061fb4bc15" +dependencies = [ + "bincode", + "log", + "rustc_version", + "solana-config-program", + "solana-program-runtime", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-streamer" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad1bdb955ec6d23a1dbf87e403ff3e610d68616275693125a893d7ed4b2d323" +dependencies = [ + "async-channel", + "bytes", + "crossbeam-channel", + "futures-util", + "histogram", + "indexmap 2.2.6", + "itertools 0.10.5", + "libc", + "log", + "nix", + "pem", + "percentage", + "pkcs8", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rcgen", + "rustls", + "smallvec", + "solana-metrics", + "solana-perf", + "solana-sdk", + "thiserror", + "tokio", + "x509-parser", +] + +[[package]] +name = "solana-system-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78733745268c96d5a29c09cde9f0a6c9d662abba43e661b75dd858da8e3d0b2e" +dependencies = [ + "bincode", + "log", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-thin-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc301310ba0755c449a8800136f67f8ad14419b366404629894cd10021495360" +dependencies = [ + "bincode", + "log", + "rayon", + "solana-connection-cache", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", +] + +[[package]] +name = "solana-tpu-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb887bd5078ff015e103e9ee54a6713380590efa8ff1804b3a653f07188928c6" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap 2.2.6", + "indicatif", + "log", + "rayon", + "solana-connection-cache", + "solana-measure", + "solana-metrics", + "solana-pubsub-client", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-sdk", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-transaction-status" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0cdfdf63192fb60de094fae8e81159e4e3e9aac9659fe3f9ef0e707023fb32" +dependencies = [ + "Inflector", + "base64 0.21.7", + "bincode", + "borsh 0.10.3", + "bs58 0.4.0", + "lazy_static", + "log", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-sdk", + "spl-associated-token-account 2.3.0", + "spl-memo", + "spl-token", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "solana-udp-client" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea0d6d8d66e36371577f51c4d1d6192a66f1fa4efe7161a36d94677640dcadb" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-net-utils", + "solana-sdk", + "solana-streamer", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-version" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4c2f531c22ce806b211118be8928a791425f97de4592371fb57b246ed33e34" +dependencies = [ + "log", + "rustc_version", + "semver", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", +] + +[[package]] +name = "solana-vote" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ab95a5d19ff0464def1777adaae5a74e1edc9e6818103064c18fdc2643f6cb" +dependencies = [ + "crossbeam-channel", + "itertools 0.10.5", + "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.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8a6486017e71a3714a8e1a635e17209135cc20535ba9808ccf106d80ff6e8b" +dependencies = [ + "bincode", + "log", + "num-derive 0.4.2", + "num-traits", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-zk-token-proof-program" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e3dfb2deb449f7eb1dbd0c7e66dd95ec7b1303a5788673f9fbc9b5a5ea59f2" +dependencies = [ + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "1.18.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513407f88394e437b4ff5aad892bc5bf51a655ae2401e6e63549734d3695c46f" +dependencies = [ + "aes-gcm-siv", + "base64 0.21.7", "bincode", - "bitflags", + "bytemuck", + "byteorder", + "curve25519-dalek", + "getrandom 0.1.16", + "itertools 0.10.5", + "lazy_static", + "merlin", + "num-derive 0.4.2", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "solana_rbpf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d457cc2ba742c120492a64b7fa60e22c575e891f6b55039f4d736568fb112a3" +dependencies = [ + "byteorder", + "combine", + "goblin", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "scroll", + "thiserror", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "spl-associated-token-account" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" +dependencies = [ + "assert_matches", "borsh 0.10.3", - "bs58 0.4.0", + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022 1.0.0", + "thiserror", +] + +[[package]] +name = "spl-associated-token-account" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e688554bac5838217ffd1fab7845c573ff106b6336bf7d290db7c98d5a8efd" +dependencies = [ + "assert_matches", + "borsh 1.5.1", + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022 3.0.2", + "thiserror", +] + +[[package]] +name = "spl-discriminator" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.1.2", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d1814406e98b08c5cd02c1126f83fd407ad084adce0b05fda5730677822eac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.2.0", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" +dependencies = [ + "quote", + "spl-discriminator-syn 0.1.2", + "syn 2.0.58", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn 0.2.0", + "syn 2.0.58", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", + "thiserror", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a" +dependencies = [ + "solana-program", +] + +[[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", +] + +[[package]] +name = "spl-pod" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ce669f48cf2eca1ec518916d8725596bfb655beb1c74374cf71dc6cb773c9" +dependencies = [ + "borsh 1.5.1", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.4.1", +] + +[[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.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.3.2", + "thiserror", +] + +[[package]] +name = "spl-program-error" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49065093ea91f57b9b2bd81493ff705e2ad4e64507a07dbc02b085778e02770e" +dependencies = [ + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.4.1", + "thiserror", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.58", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" +dependencies = [ + "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]] +name = "spl-tlv-account-resolution" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cace91ba08984a41556efe49cbf2edca4db2f577b649da7827d3621161784bf8" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", +] + +[[package]] +name = "spl-token" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "solana-program", + "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.2", + "num-traits", + "num_enum 0.7.2", + "solana-program", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "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 = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" +dependencies = [ + "arrayref", "bytemuck", - "byteorder", - "chrono", - "derivation-path", - "digest 0.10.7", - "ed25519-dalek", - "ed25519-dalek-bip32", - "generic-array", - "hmac 0.12.1", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1", - "log", - "memmap2", - "num-derive", + "num-derive 0.4.2", "num-traits", - "num_enum 0.6.1", - "pbkdf2 0.11.0", - "qstring", - "rand 0.7.3", - "rand_chacha 0.2.2", - "rustc_version", + "num_enum 0.7.2", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.4.1", + "spl-type-length-value 0.3.0", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5412f99ae7ee6e0afde00defaa354e6228e47e30c0e3adf553e2e01e6abb584" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.2", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.2.2", + "spl-token", + "spl-token-group-interface 0.2.3", + "spl-token-metadata-interface 0.3.3", + "spl-transfer-hook-interface 0.6.3", + "spl-type-length-value 0.4.3", + "thiserror", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d419b5cfa3ee8e0f2386fd7e02a33b3ec8a7db4a9c7064a2ea24849dc4a273b6" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", +] + +[[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 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.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30179c47e93625680dabb620c6e7931bd12d62af390f447bc7beb4a3a9b5feee" +dependencies = [ + "borsh 1.5.1", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-type-length-value 0.4.3", +] + +[[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", + "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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aabdb7c471566f6ddcee724beb8618449ea24b399e58d464d6b5bc7db550259" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.5.1", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a98359769cd988f7b35c02558daa56d496a7e3bd8626e61f90a7c757eedb9b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", + "spl-tlv-account-resolution 0.6.3", + "spl-type-length-value 0.4.3", +] + +[[package]] +name = "spl-type-length-value" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ce13429dbd41d2cee8a73931c05fda0b0c8ca156a8b0c19445642550bb61a" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.2", + "spl-pod 0.2.2", + "spl-program-error 0.4.1", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "superslice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" + +[[package]] +name = "sval" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eb957fbc79a55306d5d25d87daf3627bc3800681491cda0709eef36c748bfe" + +[[package]] +name = "sval_buffer" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e860aef60e9cbf37888d4953a13445abf523c534640d1f6174d310917c410d" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3f2b07929a1127d204ed7cb3905049381708245727680e9139dac317ed556f" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e188677497de274a1367c4bda15bd2296de4070d91729aac8f0a09c1abf64d" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f456c07dae652744781f2245d5e3b78e6a9ebad70790ac11eb15dbdbce5282" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886feb24709f0476baaebbf9ac10671a50163caa7e439d7a7beb7f6d81d0a6fb" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2e7fc517d778f44f8cb64140afa36010999565528d48985f55e64d45f369ce" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bf66549a997ff35cd2114a27ac4b0c2843280f2cfa84b240d169ecaa0add46" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + +[[package]] +name = "switchboard-common" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96fe58be35530580b729fa5d846661c89a007982527f4ff0ca6010168564159" +dependencies = [ + "base64 0.21.7", + "hex", + "log", "serde", - "serde_bytes", - "serde_derive", "serde_json", - "serde_with", "sha2 0.10.8", "sha3 0.10.8", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", +] + +[[package]] +name = "switchboard-solana" +version = "0.29.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb0cb2cd26bcd72a11fae679d07207bca093c303c9cc72bcdc7866bb7bf8a6b" +dependencies = [ + "anchor-lang 0.29.0", + "anchor-spl 0.29.0", + "bytemuck", + "hex", + "kv-log-macro", + "lazy_static", + "libsecp256k1 0.7.1", + "log", + "rust_decimal", + "solana-address-lookup-table-program", "solana-program", - "solana-sdk-macro", - "thiserror", - "uriparse", - "wasm-bindgen", + "superslice", + "switchboard-common", +] + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "solana-sdk-macro" -version = "1.16.25" +name = "syn" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0e0e7ee984b0f9179a1d4f4e9e67ce675de2324b5a98b61d2bdb61be3c19bb" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ - "bs58 0.4.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.55", + "unicode-ident", ] [[package]] -name = "solana-security-txt" -version = "1.1.1" +name = "syn_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", +] [[package]] -name = "solana-zk-token-sdk" -version = "1.16.25" +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1457c85ab70a518438b9ac2b0c56037b9f6693060dfb617bbb93c7116e4f0c22" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "aes-gcm-siv", - "base64 0.21.7", - "bincode", - "bytemuck", - "byteorder", - "curve25519-dalek", - "getrandom 0.1.16", - "itertools", - "lazy_static", - "merlin", - "num-derive", - "num-traits", - "rand 0.7.3", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[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 = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tarpc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" +dependencies = [ + "anyhow", + "fnv", + "futures", + "humantime", + "opentelemetry", + "pin-project", + "rand 0.8.5", "serde", - "serde_json", - "sha3 0.9.1", - "solana-program", - "solana-sdk", - "subtle", + "static_assertions", + "tarpc-plugins", "thiserror", - "zeroize", + "tokio", + "tokio-serde", + "tokio-util 0.6.10", + "tracing", + "tracing-opentelemetry", ] [[package]] -name = "spl-associated-token-account" -version = "1.1.3" +name = "tarpc-plugins" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" +checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "assert_matches", - "borsh 0.9.3", - "num-derive", - "num-traits", - "solana-program", - "spl-token 3.5.0", - "spl-token-2022", - "thiserror", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "spl-memo" -version = "3.0.1" +name = "tempfile" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ - "solana-program", + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", ] [[package]] -name = "spl-token" -version = "3.5.0" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "arrayref", - "bytemuck", - "num-derive", - "num-traits", - "num_enum 0.5.11", - "solana-program", - "thiserror", + "winapi-util", ] [[package]] -name = "spl-token" -version = "4.0.0" +name = "termtree" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" dependencies = [ - "arrayref", - "bytemuck", - "num-derive", - "num-traits", - "num_enum 0.6.1", - "solana-program", - "thiserror", + "test-case-macros", ] [[package]] -name = "spl-token-2022" -version = "0.6.1" +name = "test-case-core" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ - "arrayref", - "bytemuck", - "num-derive", - "num-traits", - "num_enum 0.5.11", - "solana-program", - "solana-zk-token-sdk", - "spl-memo", - "spl-token 3.5.0", + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "test-case-core", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "thread-id" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "tinyvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] [[package]] -name = "strsim" -version = "0.10.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "subtle" -version = "2.4.1" +name = "tokio" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] [[package]] -name = "superslice" -version = "1.0.0" +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] [[package]] -name = "switchboard-v2" -version = "0.4.0" +name = "tokio-tungstenite" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b81886169f446e22edc18ead7addd9ebd141c39bf2286cb37943c92cd3af724" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ - "anchor-lang", - "anchor-spl", - "bytemuck", - "rust_decimal", - "solana-program", - "superslice", + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.25.4", ] [[package]] -name = "syn" -version = "1.0.109" +name = "tokio-util" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", ] [[package]] -name = "syn" -version = "2.0.55" +name = "tokio-util" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] -name = "termcolor" -version = "1.4.1" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "winapi-util", + "serde", ] [[package]] -name = "thiserror" -version = "1.0.58" +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "thiserror-impl", + "indexmap 2.2.6", + "toml_datetime", + "winnow", ] [[package]] -name = "thiserror-impl" -version = "1.0.58" +name = "toml_edit" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", + "indexmap 2.2.6", + "toml_datetime", + "winnow", ] [[package]] -name = "thread-id" -version = "4.2.1" +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "libc", - "winapi", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "tiny-bip39" -version = "0.8.2" +name = "tracing-attributes" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "anyhow", - "hmac 0.8.1", - "once_cell", - "pbkdf2 0.4.0", - "rand 0.7.3", - "rustc-hash", - "sha2 0.9.9", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "tinyvec_macros", + "once_cell", + "valuable", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "tracing-opentelemetry" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] [[package]] -name = "toml" -version = "0.5.11" +name = "tracing-subscriber" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "serde", + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] -name = "toml_datetime" -version = "0.6.5" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "toml_edit" -version = "0.19.15" +name = "tungstenite" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "indexmap", - "toml_datetime", - "winnow", + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "sha1", + "thiserror", + "url", + "utf-8", + "webpki-roots 0.24.0", ] [[package]] @@ -2602,6 +6796,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typemap-ors" version = "1.0.0" @@ -2617,6 +6817,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -2638,6 +6844,18 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "universal-hash" version = "0.4.1" @@ -2648,6 +6866,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + [[package]] name = "unsafe-any-ors" version = "1.0.0" @@ -2663,6 +6890,18 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "uriparse" version = "0.6.4" @@ -2673,12 +6912,108 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2712,10 +7047,22 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2734,7 +7081,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2755,6 +7102,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -2773,11 +7135,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2792,7 +7154,25 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2812,17 +7192,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2833,9 +7214,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2845,9 +7226,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2857,9 +7238,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2869,9 +7256,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2881,9 +7268,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2893,9 +7280,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2905,9 +7292,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -2918,24 +7305,81 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", ] [[package]] @@ -2955,5 +7399,34 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.58", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.12+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +dependencies = [ + "cc", + "pkg-config", ] diff --git a/programs/marginfi/fuzz/Cargo.toml b/programs/marginfi/fuzz/Cargo.toml index 08cad8e58..7a2d82569 100644 --- a/programs/marginfi/fuzz/Cargo.toml +++ b/programs/marginfi/fuzz/Cargo.toml @@ -14,13 +14,15 @@ test = false doc = false [dependencies] -solana-sdk = "1.16.23" -solana-program = "1.16.23" +solana-sdk = "=1.18.17" +solana-program = "=1.18.17" +solana-program-test = "=1.18.17" spl-token = "4.0.0" -ahash = "=0.8.8" -anchor-lang = "0.28.0" -pyth-sdk-solana = "0.8.0" +anchor-lang = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" } +anchor-spl = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" } + +pyth-sdk-solana = "=0.10.1" anyhow = "1.0.68" arbitrary = { version = "1.2.2", features = ["derive"] } @@ -35,6 +37,11 @@ lazy_static = "1.4.0" log = { version = "0.4.17", optional = true } log4rs = { version = "1.2.0", optional = true } once_cell = "1.17.1" +base64 = "0.22.1" +itertools = "0.12.1" +strum = { version = "0.26.3", features = ["derive"], default-features = false } + +quinn-proto = { version = "0.10.6", features = ["arbitrary"] } [features] capture_log = ["log", "log4rs"] diff --git a/programs/marginfi/fuzz/fuzz_targets/lend.rs b/programs/marginfi/fuzz/fuzz_targets/lend.rs index bd370ec1a..c0f41d469 100644 --- a/programs/marginfi/fuzz/fuzz_targets/lend.rs +++ b/programs/marginfi/fuzz/fuzz_targets/lend.rs @@ -51,18 +51,28 @@ enum Action { #[derive(Debug)] pub struct ActionSequence(Vec); +impl ActionSequence { + pub const N_ACTIONS: usize = 400; +} + impl<'a> Arbitrary<'a> for ActionSequence { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let n_actions = 100; - let mut actions = Vec::with_capacity(n_actions); + let mut actions = Vec::with_capacity(Self::N_ACTIONS); - for _ in 0..n_actions { + for _ in 0..Self::N_ACTIONS { + if u.is_empty() { + panic!("Byte exhaustion detected, stopping early"); + } let action = Action::arbitrary(u)?; actions.push(action); } Ok(ActionSequence(actions)) } + + fn size_hint(_: usize) -> (usize, Option) { + (Self::N_ACTIONS * 10, Some(Self::N_ACTIONS * 10)) + } } #[derive(Debug, Arbitrary)] @@ -92,7 +102,7 @@ fn process_actions(ctx: FuzzerContext) -> Result<()> { context.metrics.read().unwrap().print(); context.metrics.read().unwrap().log(); - verify_end_state(&context)?; + verify_end_state(&context).unwrap(); accounst_state.reset(); @@ -134,10 +144,10 @@ fn setup_logging() -> anyhow::Result<()> { Ok(()) } -fn verify_end_state(mga: &MarginfiFuzzContext) -> anyhow::Result<()> { +fn verify_end_state<'a>(mga: &'a MarginfiFuzzContext<'a>) -> anyhow::Result<()> { mga.banks.iter().try_for_each(|bank| { - let bank_loader = AccountLoader::::try_from(&bank.bank)?; - let mut bank_data = bank_loader.load_mut()?; + let bank_loader = AccountLoader::::try_from(&bank.bank).unwrap(); + let mut bank_data = bank_loader.load_mut().unwrap(); let latest_timestamp = *mga.last_sysvar_current_timestamp.read().unwrap(); @@ -158,7 +168,7 @@ fn verify_end_state(mga: &MarginfiFuzzContext) -> anyhow::Result<()> { let net_accounted_balance = total_deposits - total_liabilities; let liquidity_vault_token_account = - spl_token::state::Account::unpack(&bank.liquidity_vault.data.borrow())?; + spl_token::state::Account::unpack(&bank.liquidity_vault.data.borrow()[..spl_token::state::Account::LEN]).unwrap(); marginfi_fuzz::log!("Accounted Deposits: {}, Liabs: {}, Net {}, Outstanding Fees: {}, Net with Fees {}, Value Token Balance {}, Net Without Fees {}", total_deposits, diff --git a/programs/marginfi/fuzz/generate_corpus.py b/programs/marginfi/fuzz/generate_corpus.py new file mode 100644 index 000000000..ca7ac0780 --- /dev/null +++ b/programs/marginfi/fuzz/generate_corpus.py @@ -0,0 +1,13 @@ +import os +import random +import json + +def generate_binary_corpus(num_files=100, max_size=4096): + corpus_dir = os.path.join(os.path.dirname(__file__), "corpus/lend") + os.makedirs(corpus_dir, exist_ok=True) + + for i in range(num_files): + with open(f"{corpus_dir}/input_{i}.bin", "wb") as f: + f.write(os.urandom(random.randint(1, max_size))) + +generate_binary_corpus() diff --git a/programs/marginfi/fuzz/src/account_state.rs b/programs/marginfi/fuzz/src/account_state.rs index 7f5822116..a3ff6519e 100644 --- a/programs/marginfi/fuzz/src/account_state.rs +++ b/programs/marginfi/fuzz/src/account_state.rs @@ -1,19 +1,27 @@ -use crate::SplAccount; +use crate::arbitrary_helpers::TokenType; use anchor_lang::{ prelude::{AccountInfo, Pubkey, Rent, SolanaSysvar}, Discriminator, }; +use anchor_spl::token_2022::spl_token_2022::{ + self, + extension::{ + transfer_fee::{TransferFee, TransferFeeConfig}, + BaseStateWithExtensions, BaseStateWithExtensionsMut, ExtensionType, StateWithExtensions, + StateWithExtensionsMut, + }, + state::Mint, +}; use bumpalo::Bump; use marginfi::{constants::PYTH_ID, state::marginfi_group::BankVaultType}; use pyth_sdk_solana::state::{ - AccountType, PriceAccount, PriceInfo, PriceStatus, Rational, MAGIC, VERSION_2, + AccountType, PriceInfo, PriceStatus, Rational, SolanaPriceAccount, MAGIC, VERSION_2, }; use safe_transmute::{transmute_to_bytes, transmute_to_bytes_mut}; use solana_program::{ bpf_loader, program_pack::Pack, stake_history::Epoch, system_program, sysvar, }; use solana_sdk::{signature::Keypair, signer::Signer}; -use spl_token::state::Mint; use std::mem::size_of; pub struct AccountsState { @@ -26,6 +34,7 @@ impl AccountsState { } fn random_pubkey<'bump>(&'bump self) -> &Pubkey { + #[allow(deprecated)] self.bump .alloc(Pubkey::new(transmute_to_bytes(&rand::random::<[u64; 4]>()))) } @@ -51,73 +60,182 @@ impl AccountsState { ) } - pub fn new_token_mint<'bump>(&'bump self, rent: Rent, decimals: u8) -> AccountInfo<'bump> { - let data = self.bump.alloc_slice_fill_copy(Mint::LEN, 0u8); - let mut mint = Mint::default(); - mint.is_initialized = true; - mint.decimals = decimals; - Mint::pack(mint, data).unwrap(); - AccountInfo::new( - self.random_pubkey(), - false, - true, - self.bump.alloc(rent.minimum_balance(data.len())), - data, - &spl_token::ID, - false, - Epoch::default(), - ) + pub fn new_token_mint<'bump>( + &'bump self, + rent: Rent, + decimals: u8, + token_type: TokenType, + ) -> AccountInfo<'bump> { + match token_type { + TokenType::Tokenkeg => { + let data = self.bump.alloc_slice_fill_copy(Mint::LEN, 0u8); + let mut mint = Mint::default(); + mint.is_initialized = true; + mint.decimals = decimals; + Mint::pack(mint, data).unwrap(); + AccountInfo::new( + self.random_pubkey(), + false, + true, + self.bump.alloc(rent.minimum_balance(data.len())), + data, + &spl_token::ID, + false, + Epoch::default(), + ) + } + TokenType::Token22 => { + let data = self.bump.alloc_slice_fill_copy(Mint::LEN, 0u8); + let mut mint = Mint::default(); + mint.is_initialized = true; + mint.decimals = decimals; + Mint::pack(mint, data).unwrap(); + AccountInfo::new( + self.random_pubkey(), + false, + true, + self.bump.alloc(rent.minimum_balance(data.len())), + data, + &spl_token_2022::ID, + false, + Epoch::default(), + ) + } + TokenType::Token22WithFee { + transfer_fee_basis_points, + maximum_fee, + } => { + // Calculate mint size for t22 mint with transfer fee + let data_len = ExtensionType::try_calculate_account_len::< + spl_token_2022::state::Mint, + >(&[ExtensionType::TransferFeeConfig]) + .unwrap(); + + let mut data = self.bump.alloc_slice_fill_copy(data_len, 0u8); + let mut mint_state = + StateWithExtensionsMut::::unpack_uninitialized( + &mut data, + ) + .unwrap(); + mint_state.init_account_type().unwrap(); + + let transfer_fee_config = mint_state + .init_extension::(false) + .unwrap(); + *transfer_fee_config = TransferFeeConfig { + transfer_fee_config_authority: Default::default(), + withdraw_withheld_authority: Default::default(), + withheld_amount: 0.into(), + older_transfer_fee: TransferFee { + epoch: 0.into(), + maximum_fee: maximum_fee.into(), + transfer_fee_basis_points: transfer_fee_basis_points.into(), + }, + newer_transfer_fee: TransferFee { + epoch: 0.into(), + maximum_fee: maximum_fee.into(), + transfer_fee_basis_points: transfer_fee_basis_points.into(), + }, + }; + + let mut mint = spl_token_2022::state::Mint::default(); + mint.decimals = decimals; + mint.is_initialized = true; + + mint_state.base = mint; + mint_state.pack_base(); + + AccountInfo::new( + self.random_pubkey(), + false, + true, + self.bump.alloc(rent.minimum_balance(data.len())), + data, + &spl_token_2022::ID, + false, + Epoch::default(), + ) + } + } } - pub fn new_token_account<'bump, 'a, 'b>( - &'bump self, - mint_pubkey: &'a Pubkey, - owner_pubkey: &'b Pubkey, + pub fn new_token_account<'a>( + &'a self, + mint_ai: AccountInfo<'a>, + owner_pubkey: &'a Pubkey, balance: u64, rent: Rent, - ) -> AccountInfo<'bump> { + ) -> AccountInfo<'a> { self.new_token_account_with_pubkey( Keypair::new().pubkey(), - mint_pubkey, + mint_ai, owner_pubkey, balance, rent, ) } - pub fn new_token_account_with_pubkey<'bump, 'a, 'b>( - &'bump self, + pub fn new_token_account_with_pubkey<'a>( + &'a self, account_pubkey: Pubkey, - mint_pubkey: &'a Pubkey, - owner_pubkey: &'b Pubkey, + mint_ai: AccountInfo<'a>, + owner_pubkey: &'a Pubkey, balance: u64, rent: Rent, - ) -> AccountInfo<'bump> { - let data = self.bump.alloc_slice_fill_copy(SplAccount::LEN, 0u8); - let mut account = SplAccount::default(); - account.state = spl_token::state::AccountState::Initialized; - account.mint = *mint_pubkey; - account.owner = *owner_pubkey; - account.amount = balance; - SplAccount::pack(account, data).unwrap(); + ) -> AccountInfo<'a> { + // Load mint + let mint_data = mint_ai.try_borrow_data().unwrap(); + let mint_state = + StateWithExtensions::::unpack(&mint_data).unwrap(); + let mint_extensions = mint_state.get_extension_types().unwrap(); + let required_extensions = + ExtensionType::get_required_init_account_extensions(&mint_extensions); + let space = ExtensionType::try_calculate_account_len::( + &required_extensions, + ) + .unwrap(); + + let data = self.bump.alloc_slice_fill_copy(space, 0u8); + + let mut token_account_state = + StateWithExtensionsMut::::unpack_uninitialized(data) + .unwrap(); + + if required_extensions.contains(&ExtensionType::TransferFeeAmount) { + token_account_state + .init_account_extension_from_type(ExtensionType::TransferFeeAmount) + .unwrap(); + } + + token_account_state.base = spl_token_2022::state::Account::default(); + token_account_state.base.state = spl_token_2022::state::AccountState::Initialized; + token_account_state.base.mint = *mint_ai.key; + token_account_state.base.owner = *owner_pubkey; + token_account_state.base.amount = balance; + + token_account_state.pack_base(); + token_account_state.init_account_type().unwrap(); + drop(token_account_state); + assert!(StateWithExtensionsMut::::unpack(data).is_ok()); + AccountInfo::new( self.bump.alloc(account_pubkey), false, true, self.bump.alloc(rent.minimum_balance(data.len())), data, - &spl_token::ID, + mint_ai.owner, false, Epoch::default(), ) } - pub fn new_owned_account<'bump>( - &'bump self, + pub fn new_owned_account( + &self, unpadded_len: usize, owner_pubkey: Pubkey, rent: Rent, - ) -> AccountInfo<'bump> { + ) -> AccountInfo { let data_len = unpadded_len + 12; self.new_dex_owned_account_with_lamports( unpadded_len, @@ -184,7 +302,7 @@ impl AccountsState { mint: Pubkey, mint_decimals: i32, ) -> AccountInfo { - let price_account = PriceAccount { + let price_account = SolanaPriceAccount { prod: mint, agg: PriceInfo { conf: 0, @@ -244,26 +362,26 @@ impl AccountsState { account_info } - pub fn new_vault_account<'bump>( - &'bump self, + pub fn new_vault_account<'a>( + &'a self, vault_type: BankVaultType, - mint_pubkey: &'bump Pubkey, - owner: &'bump Pubkey, - bank: &'bump Pubkey, - ) -> (AccountInfo<'bump>, u8) { + mint_ai: AccountInfo<'a>, + owner: &'a Pubkey, + bank: &'a Pubkey, + ) -> (AccountInfo<'a>, u8) { let (vault_address, seed_bump) = get_vault_address(bank, vault_type); ( - self.new_token_account_with_pubkey(vault_address, mint_pubkey, owner, 0, Rent::free()), + self.new_token_account_with_pubkey(vault_address, mint_ai, owner, 0, Rent::free()), seed_bump, ) } - pub fn new_vault_authority<'bump>( - &'bump self, + pub fn new_vault_authority<'a>( + &'a self, vault_type: BankVaultType, - bank: &'bump Pubkey, - ) -> (AccountInfo<'bump>, u8) { + bank: &'a Pubkey, + ) -> (AccountInfo<'a>, u8) { let (vault_address, seed_bump) = get_vault_authority(bank, vault_type); ( diff --git a/programs/marginfi/fuzz/src/arbitrary_helpers.rs b/programs/marginfi/fuzz/src/arbitrary_helpers.rs index 7d7e67fbf..b0b079dc4 100644 --- a/programs/marginfi/fuzz/src/arbitrary_helpers.rs +++ b/programs/marginfi/fuzz/src/arbitrary_helpers.rs @@ -1,21 +1,28 @@ use arbitrary::Arbitrary; use fixed_macro::types::I80F48; use marginfi::state::marginfi_group::WrappedI80F48; +use strum::{EnumDiscriminants, VariantArray}; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PriceChange(pub i64); impl<'a> Arbitrary<'a> for PriceChange { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.int_in_range(-10..=10)? * 1_000_000)) + if u.is_empty() { + panic!("Byte exhaustion detected, stopping early"); + } + Ok(Self(u.int_in_range(0..=1_000_000_000_000)?)) } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AccountIdx(pub u8); -pub const N_USERS: usize = 4; +pub const N_USERS: usize = 2; impl<'a> Arbitrary<'a> for AccountIdx { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + if u.is_empty() { + panic!("Byte exhaustion detected, stopping early"); + } let i: u8 = u.int_in_range(0..=N_USERS as u8 - 1)?; Ok(AccountIdx(i)) } @@ -31,9 +38,12 @@ impl<'a> Arbitrary<'a> for AccountIdx { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BankIdx(pub u8); -pub const N_BANKS: usize = 4; +pub const N_BANKS: usize = 16; impl<'a> Arbitrary<'a> for BankIdx { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + if u.is_empty() { + panic!("Byte exhaustion detected, stopping early"); + } Ok(BankIdx(u.int_in_range(0..=N_BANKS - 1)? as u8)) } @@ -52,6 +62,9 @@ pub struct AssetAmount(pub u64); pub const ASSET_UNIT: u64 = 1_000_000_000; impl<'a> Arbitrary<'a> for AssetAmount { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + if u.is_empty() { + panic!("Byte exhaustion detected, stopping early"); + } Ok(AssetAmount(u.int_in_range(1..=10)? * ASSET_UNIT)) } @@ -64,7 +77,7 @@ impl<'a> Arbitrary<'a> for AssetAmount { } } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy)] pub struct BankAndOracleConfig { pub oracle_native_price: u64, pub mint_decimals: u8, @@ -77,6 +90,7 @@ pub struct BankAndOracleConfig { pub deposit_limit: u64, pub borrow_limit: u64, + pub token_type: TokenType, pub risk_tier_isolated: bool, } @@ -92,6 +106,8 @@ impl<'a> Arbitrary<'a> for BankAndOracleConfig { let risk_tier_isolated: bool = u.arbitrary()?; + let token_type = u.arbitrary()?; + Ok(Self { oracle_native_price: u.int_in_range(1..=10)? * max_price, mint_decimals, @@ -105,6 +121,7 @@ impl<'a> Arbitrary<'a> for BankAndOracleConfig { } else { I80F48!(0).into() }, + token_type, liability_weight_init: I80F48!(1.5).into(), liability_weight_maint: I80F48!(1.25).into(), deposit_limit, @@ -125,7 +142,42 @@ impl BankAndOracleConfig { liability_weight_maint: I80F48!(1.1).into(), deposit_limit: 1_000_000_000_000 * 10u64.pow(6), borrow_limit: 1_000_000_000_000 * 10u64.pow(6), + token_type: TokenType::Tokenkeg, risk_tier_isolated: false, } } } + +#[derive(Debug, Clone, Copy, EnumDiscriminants)] +#[strum_discriminants(derive(VariantArray))] +pub enum TokenType { + Tokenkeg, + Token22, + Token22WithFee { + transfer_fee_basis_points: u16, + maximum_fee: u64, + }, +} + +impl<'a> Arbitrary<'a> for TokenType { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let discriminant = u.choose(&TokenTypeDiscriminants::VARIANTS)?; + + match discriminant { + TokenTypeDiscriminants::Tokenkeg => Ok(TokenType::Tokenkeg), + TokenTypeDiscriminants::Token22 => Ok(TokenType::Token22), + TokenTypeDiscriminants::Token22WithFee => { + // Get fee + let fee_bps: u16 = u.int_in_range(0..=10_000)?; + + // Get max fee + let max_fee: u64 = u.int_in_range(0..=1_000_000)?; + + Ok(TokenType::Token22WithFee { + transfer_fee_basis_points: fee_bps, + maximum_fee: max_fee, + }) + } + } + } +} diff --git a/programs/marginfi/fuzz/src/bank_accounts.rs b/programs/marginfi/fuzz/src/bank_accounts.rs index 7bb98ba87..d321aaca4 100644 --- a/programs/marginfi/fuzz/src/bank_accounts.rs +++ b/programs/marginfi/fuzz/src/bank_accounts.rs @@ -3,7 +3,7 @@ use crate::log; use anchor_lang::prelude::{AccountInfo, ProgramError, Pubkey}; use anchor_lang::Key; -use pyth_sdk_solana::state::PriceAccount; +use pyth_sdk_solana::state::SolanaPriceAccount; use std::cmp::max; use std::collections::HashMap; @@ -18,42 +18,45 @@ pub struct BankAccounts<'info> { pub fee_vault: AccountInfo<'info>, pub fee_vault_authority: AccountInfo<'info>, pub mint: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, pub mint_decimals: u8, } impl<'bump> BankAccounts<'bump> { pub fn refresh_oracle(&self, timestamp: i64) -> Result<(), ProgramError> { let mut data = self.oracle.try_borrow_mut_data()?; - let data = bytemuck::from_bytes_mut::(&mut data); + let data = bytemuck::from_bytes_mut::(&mut data); data.timestamp = timestamp; Ok(()) } - pub fn update_oracle(&self, price_change: i64) -> Result<(), ProgramError> { + pub fn update_oracle(&self, updated_price: i64) -> Result<(), ProgramError> { let mut data = self.oracle.try_borrow_mut_data()?; - let data = bytemuck::from_bytes_mut::(&mut data); + let data = bytemuck::from_bytes_mut::(&mut data); - data.agg.price = max(data.agg.price + price_change, 0); - data.ema_price.val = max(data.ema_price.val + price_change, 0); - data.ema_price.numer = max(data.ema_price.numer + price_change, 0); + data.agg.price = max(updated_price, 0); + data.ema_price.val = max(updated_price, 0); + data.ema_price.numer = max(updated_price, 0); Ok(()) } pub fn log_oracle_price(&self) -> Result<(), ProgramError> { - let data = self.oracle.try_borrow_data()?; - let data = bytemuck::from_bytes::(&data); - - log!("Oracle price: {}", data.ema_price.val); + log!( + "Oracle price: {}", + bytemuck::from_bytes::(&self.oracle.try_borrow_data()?) + .ema_price + .val + ); Ok(()) } } -pub fn get_bank_map<'bump>( - banks: &'bump [BankAccounts<'bump>], -) -> HashMap> { - HashMap::from_iter(banks.iter().map(|bank| (bank.bank.key(), bank.clone()))) +pub fn get_bank_map<'a, 'bump>( + banks: &'a [BankAccounts<'bump>], +) -> HashMap> { + HashMap::from_iter(banks.iter().map(|bank| (bank.bank.key(), bank))) } diff --git a/programs/marginfi/fuzz/src/lib.rs b/programs/marginfi/fuzz/src/lib.rs index f418460db..1ccc86e48 100644 --- a/programs/marginfi/fuzz/src/lib.rs +++ b/programs/marginfi/fuzz/src/lib.rs @@ -1,17 +1,26 @@ +use std::{ + collections::HashMap, + mem::size_of, + ops::AddAssign, + sync::{Arc, RwLock}, + time::{SystemTime, UNIX_EPOCH}, +}; + use account_state::{AccountInfoCache, AccountsState}; use anchor_lang::{ - prelude::{ - Account, AccountInfo, AccountLoader, Context, Program, Pubkey, Rent, Signer, SolanaSysvar, - Sysvar, - }, + accounts::{interface::Interface, interface_account::InterfaceAccount}, + prelude::{AccountInfo, AccountLoader, Context, Program, Pubkey, Rent, Signer, Sysvar}, Discriminator, Key, }; -use arbitrary_helpers::{AccountIdx, AssetAmount, BankAndOracleConfig, BankIdx, PriceChange}; +use anchor_spl::token_2022::spl_token_2022; +use arbitrary_helpers::{ + AccountIdx, AssetAmount, BankAndOracleConfig, BankIdx, PriceChange, TokenType, +}; use bank_accounts::{get_bank_map, BankAccounts}; - use fixed_macro::types::I80F48; - use marginfi::{ + errors::MarginfiError, + instructions::LendingPoolAddBankBumps, prelude::MarginfiGroup, state::{ marginfi_account::MarginfiAccount, @@ -19,18 +28,13 @@ use marginfi::{ }, }; use metrics::{MetricAction, Metrics}; - use solana_program::system_program; - -use std::{ - collections::{BTreeMap, HashMap}, - mem::size_of, - ops::AddAssign, - sync::{Arc, RwLock}, - time::{SystemTime, UNIX_EPOCH}, -}; use stubs::test_syscall_stubs; use user_accounts::UserAccount; +use utils::{ + account_info_lifetime_shortener as ails, account_info_ref_lifetime_shortener as airls, + account_info_slice_lifetime_shortener as aisls, +}; pub mod account_state; pub mod arbitrary_helpers; @@ -38,8 +42,7 @@ pub mod bank_accounts; pub mod metrics; pub mod stubs; pub mod user_accounts; - -type SplAccount = spl_token::state::Account; +pub mod utils; pub struct MarginfiFuzzContext<'info> { pub marginfi_group: AccountInfo<'info>, @@ -48,20 +51,18 @@ pub struct MarginfiFuzzContext<'info> { pub owner: AccountInfo<'info>, pub system_program: AccountInfo<'info>, pub rent_sysvar: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, pub last_sysvar_current_timestamp: RwLock, pub metrics: Arc>, pub state: &'info AccountsState, } -impl<'bump> MarginfiFuzzContext<'bump> { +impl<'state> MarginfiFuzzContext<'state> { pub fn setup( - state: &'bump AccountsState, + state: &'state AccountsState, bank_configs: &[BankAndOracleConfig], n_users: u8, ) -> Self { let system_program = state.new_program(system_program::id()); - let token_program = state.new_program(spl_token::id()); let admin = state.new_sol_account(1_000_000); let rent_sysvar = state.new_rent_sysvar_account(Rent::free()); let marginfi_group = @@ -73,7 +74,6 @@ impl<'bump> MarginfiFuzzContext<'bump> { owner: admin, system_program, rent_sysvar, - token_program, marginfi_accounts: vec![], last_sysvar_current_timestamp: RwLock::new( SystemTime::now() @@ -84,32 +84,29 @@ impl<'bump> MarginfiFuzzContext<'bump> { metrics: Arc::new(RwLock::new(Metrics::default())), state, }; + marginfi_state.advance_time(0); - let banks = bank_configs + bank_configs .iter() - .map(|config| marginfi_state.setup_bank(state, Rent::free(), config)) - .collect(); + .for_each(|config| marginfi_state.setup_bank(state, Rent::free(), config)); - marginfi_state.banks = banks; - - let token_vec = marginfi_state.banks.iter().map(|b| *b.mint.key).collect(); + let token_vec = marginfi_state + .banks + .iter() + .map(|b| b.mint.clone()) + .collect(); - marginfi_state.marginfi_accounts = (0..n_users) - .into_iter() - .map(|_| { - marginfi_state - .create_marginfi_account(state, Rent::free(), &token_vec) - .unwrap() - }) - .collect::>(); + (0..n_users).into_iter().for_each(|_| { + marginfi_state + .create_marginfi_account(state, Rent::free(), &token_vec) + .unwrap() + }); // Create an extra account for seeding the banks - let funding_account = marginfi_state + marginfi_state .create_marginfi_account(state, Rent::free(), &token_vec) .unwrap(); - marginfi_state.marginfi_accounts.push(funding_account); - // Seed the banks for bank_idx in 0..marginfi_state.banks.len() { marginfi_state @@ -125,12 +122,10 @@ impl<'bump> MarginfiFuzzContext<'bump> { .unwrap(); } - marginfi_state.advance_time(0); - marginfi_state } - fn get_bank_map(&'bump self) -> HashMap> { + fn get_bank_map<'a>(&'a self) -> HashMap> { get_bank_map(&self.banks) } @@ -157,20 +152,25 @@ impl<'bump> MarginfiFuzzContext<'bump> { )); } - pub fn setup_bank( - &self, - state: &'bump AccountsState, + pub fn setup_bank<'a>( + &'a mut self, + state: &'state AccountsState, rent: Rent, initial_bank_config: &BankAndOracleConfig, - ) -> BankAccounts<'bump> { - let bank = state.new_owned_account(size_of::(), marginfi::id(), rent); - - let mint = state.new_token_mint(rent, initial_bank_config.mint_decimals); + ) { + log!("Setting up bank with config {:#?}", initial_bank_config); + let bank = state.new_owned_account(size_of::(), marginfi::id(), rent.clone()); + + let mint = state.new_token_mint( + rent.clone(), + initial_bank_config.mint_decimals, + initial_bank_config.token_type, + ); let (liquidity_vault_authority, liquidity_vault_authority_bump) = state.new_vault_authority(BankVaultType::Liquidity, bank.key); let (liquidity_vault, liquidity_vault_bump) = state.new_vault_account( BankVaultType::Liquidity, - mint.key, + mint.clone(), liquidity_vault_authority.key, bank.key, ); @@ -179,7 +179,7 @@ impl<'bump> MarginfiFuzzContext<'bump> { state.new_vault_authority(BankVaultType::Insurance, bank.key); let (insurance_vault, insurance_vault_bump) = state.new_vault_account( BankVaultType::Insurance, - mint.key, + mint.clone(), insurance_vault_authority.key, bank.key, ); @@ -188,99 +188,104 @@ impl<'bump> MarginfiFuzzContext<'bump> { state.new_vault_authority(BankVaultType::Fee, bank.key); let (fee_vault, fee_vault_bump) = state.new_vault_account( BankVaultType::Fee, - mint.key, + mint.clone(), fee_vault_authority.key, bank.key, ); let oracle = state.new_oracle_account( - rent, + rent.clone(), initial_bank_config.oracle_native_price as i64, *mint.key, initial_bank_config.mint_decimals as i32, ); - let mut seed_bump_map = BTreeMap::new(); - - seed_bump_map.insert("liquidity_vault".to_owned(), liquidity_vault_bump); - seed_bump_map.insert( - "liquidity_vault_authority".to_owned(), - liquidity_vault_authority_bump, - ); - seed_bump_map.insert("insurance_vault".to_owned(), insurance_vault_bump); - seed_bump_map.insert( - "insurance_vault_authority".to_owned(), - insurance_vault_authority_bump, - ); - seed_bump_map.insert("fee_vault".to_owned(), fee_vault_bump); - seed_bump_map.insert("fee_vault_authority".to_owned(), fee_vault_authority_bump); + let add_bank_bumps = LendingPoolAddBankBumps { + liquidity_vault_authority: liquidity_vault_authority_bump, + liquidity_vault: liquidity_vault_bump, + insurance_vault_authority: insurance_vault_authority_bump, + insurance_vault: insurance_vault_bump, + fee_vault_authority: fee_vault_authority_bump, + fee_vault: fee_vault_bump, + }; - test_syscall_stubs(Some( - *self.last_sysvar_current_timestamp.read().unwrap() as i64 - )); + let token_program = match initial_bank_config.token_type { + TokenType::Tokenkeg => state.new_program(spl_token::id()), + TokenType::Token22 | TokenType::Token22WithFee { .. } => { + state.new_program(spl_token_2022::id()) + } + }; - marginfi::instructions::marginfi_group::lending_pool_add_bank( - Context::new( - &marginfi::id(), - &mut marginfi::instructions::LendingPoolAddBank { - marginfi_group: AccountLoader::try_from(&self.marginfi_group).unwrap(), - admin: Signer::try_from(&self.owner).unwrap(), - fee_payer: Signer::try_from(&self.owner).unwrap(), - bank_mint: Box::new(Account::try_from(&mint).unwrap()), - bank: AccountLoader::try_from_unchecked(&marginfi::ID, &bank).unwrap(), - liquidity_vault_authority: liquidity_vault_authority.clone(), - liquidity_vault: Box::new(Account::try_from(&liquidity_vault).unwrap()), - insurance_vault_authority: insurance_vault_authority.clone(), - insurance_vault: Box::new(Account::try_from(&insurance_vault).unwrap()), - fee_vault_authority: fee_vault_authority.clone(), - fee_vault: Box::new(Account::try_from(&fee_vault).unwrap()), - rent: Sysvar::from_account_info(&self.rent_sysvar).unwrap(), - token_program: Program::try_from(&self.token_program).unwrap(), - system_program: Program::try_from(&self.system_program).unwrap(), - }, - &[oracle.clone()], - seed_bump_map, - ), - BankConfig { - asset_weight_init: initial_bank_config.asset_weight_init, - asset_weight_maint: initial_bank_config.asset_weight_maint, - liability_weight_init: initial_bank_config.liability_weight_init, - liability_weight_maint: initial_bank_config.liability_weight_maint, - deposit_limit: initial_bank_config.deposit_limit, - borrow_limit: initial_bank_config.borrow_limit, - interest_rate_config: InterestRateConfig { - optimal_utilization_rate: I80F48!(0.5).into(), - plateau_interest_rate: I80F48!(0.5).into(), - max_interest_rate: I80F48!(4).into(), - insurance_fee_fixed_apr: I80F48!(0.01).into(), - insurance_ir_fee: I80F48!(0.05).into(), - protocol_fixed_fee_apr: I80F48!(0.01).into(), - protocol_ir_fee: I80F48!(0.1).into(), + { + marginfi::instructions::marginfi_group::lending_pool_add_bank( + Context::new( + &marginfi::ID, + &mut marginfi::instructions::LendingPoolAddBank { + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group)) + .unwrap(), + admin: Signer::try_from(airls(&self.owner)).unwrap(), + fee_payer: Signer::try_from(airls(&self.owner)).unwrap(), + bank_mint: Box::new(InterfaceAccount::try_from(airls(&mint)).unwrap()), + bank: AccountLoader::try_from_unchecked(&marginfi::ID, airls(&bank)) + .unwrap(), + liquidity_vault_authority: ails(liquidity_vault_authority.clone()), + liquidity_vault: Box::new( + InterfaceAccount::try_from(airls(&liquidity_vault)).unwrap(), + ), + insurance_vault_authority: ails(insurance_vault_authority.clone()), + insurance_vault: Box::new( + InterfaceAccount::try_from(airls(&insurance_vault)).unwrap(), + ), + fee_vault_authority: ails(fee_vault_authority.clone()), + fee_vault: Box::new(InterfaceAccount::try_from(airls(&fee_vault)).unwrap()), + rent: Sysvar::from_account_info(airls(&self.rent_sysvar)).unwrap(), + token_program: Interface::try_from(airls(&token_program)).unwrap(), + system_program: Program::try_from(airls(&self.system_program)).unwrap(), + }, + &[ails(oracle.clone())], + add_bank_bumps, + ), + BankConfig { + asset_weight_init: initial_bank_config.asset_weight_init, + asset_weight_maint: initial_bank_config.asset_weight_maint, + liability_weight_init: initial_bank_config.liability_weight_init, + liability_weight_maint: initial_bank_config.liability_weight_maint, + deposit_limit: initial_bank_config.deposit_limit, + borrow_limit: initial_bank_config.borrow_limit, + interest_rate_config: InterestRateConfig { + optimal_utilization_rate: I80F48!(0.5).into(), + plateau_interest_rate: I80F48!(0.5).into(), + max_interest_rate: I80F48!(4).into(), + insurance_fee_fixed_apr: I80F48!(0.01).into(), + insurance_ir_fee: I80F48!(0.05).into(), + protocol_fixed_fee_apr: I80F48!(0.01).into(), + protocol_ir_fee: I80F48!(0.1).into(), + ..Default::default() + }, + oracle_setup: marginfi::state::price::OracleSetup::PythLegacy, + oracle_keys: [ + oracle.key(), + Pubkey::default(), + Pubkey::default(), + Pubkey::default(), + Pubkey::default(), + ], + operational_state: + marginfi::state::marginfi_group::BankOperationalState::Operational, + risk_tier: if !initial_bank_config.risk_tier_isolated { + marginfi::state::marginfi_group::RiskTier::Collateral + } else { + marginfi::state::marginfi_group::RiskTier::Isolated + }, ..Default::default() }, - oracle_setup: marginfi::state::price::OracleSetup::PythEma, - oracle_keys: [ - oracle.key(), - Pubkey::default(), - Pubkey::default(), - Pubkey::default(), - Pubkey::default(), - ], - operational_state: - marginfi::state::marginfi_group::BankOperationalState::Operational, - risk_tier: if !initial_bank_config.risk_tier_isolated { - marginfi::state::marginfi_group::RiskTier::Collateral - } else { - marginfi::state::marginfi_group::RiskTier::Isolated - }, - ..Default::default() - }, - ) - .unwrap(); + ) + .unwrap(); + } set_discriminator::(bank.clone()); - BankAccounts { + self.banks.push(BankAccounts { bank, oracle, liquidity_vault, @@ -291,44 +296,53 @@ impl<'bump> MarginfiFuzzContext<'bump> { insurance_vault_authority, fee_vault_authority, mint_decimals: initial_bank_config.mint_decimals, - } + token_program, + }); } - fn create_marginfi_account( - &self, - state: &'bump AccountsState, + fn create_marginfi_account<'a>( + &'a mut self, + state: &'state AccountsState, rent: Rent, - token_mints: &Vec, - ) -> anyhow::Result> { + token_mints: &Vec>, + ) -> anyhow::Result<()> { let marginfi_account = - state.new_owned_account(size_of::(), marginfi::id(), rent); + state.new_owned_account(size_of::(), marginfi::id(), rent.clone()); marginfi::instructions::marginfi_account::initialize_account(Context::new( &marginfi::id(), &mut marginfi::instructions::marginfi_account::MarginfiAccountInitialize { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, marginfi_account: AccountLoader::try_from_unchecked( &marginfi::ID, - &marginfi_account, + airls(&marginfi_account), )?, - authority: Signer::try_from(&self.owner)?, - fee_payer: Signer::try_from(&self.owner)?, - system_program: Program::try_from(&self.system_program)?, + authority: Signer::try_from(airls(&self.owner))?, + fee_payer: Signer::try_from(airls(&self.owner))?, + system_program: Program::try_from(airls(&self.system_program))?, }, &[], - BTreeMap::new(), + Default::default(), ))?; let token_accounts = token_mints .iter() .map(|token| { - state.new_token_account(token, self.owner.key, 100_000_000_000_000_000, rent) + state.new_token_account( + token.clone(), + self.owner.key, + 100_000_000_000_000_000, + rent.clone(), + ) }) .collect(); set_discriminator::(marginfi_account.clone()); - Ok(UserAccount::new(marginfi_account, token_accounts)) + self.marginfi_accounts + .push(UserAccount::new(marginfi_account, token_accounts)); + + Ok(()) } pub fn process_action_deposit( @@ -338,6 +352,7 @@ impl<'bump> MarginfiFuzzContext<'bump> { asset_amount: &AssetAmount, ) -> anyhow::Result<()> { let marginfi_account = &self.marginfi_accounts[account_idx.0 as usize]; + sort_balances(airls(&marginfi_account.margin_account)); let bank = &self.banks[bank_idx.0 as usize]; @@ -348,33 +363,55 @@ impl<'bump> MarginfiFuzzContext<'bump> { bank.liquidity_vault.clone(), ]); + let mut remaining_accounts: Vec = vec![]; + if bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(bank.mint.clone())); + } + let res = marginfi::instructions::marginfi_account::lending_account_deposit( Context::new( &marginfi::ID, &mut marginfi::instructions::LendingAccountDeposit { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - marginfi_account: AccountLoader::try_from(&marginfi_account.margin_account)?, - signer: Signer::try_from(&self.owner)?, - bank: AccountLoader::try_from(&bank.bank)?, - signer_token_account: marginfi_account.token_accounts[bank_idx.0 as usize] - .clone(), - bank_liquidity_vault: bank.liquidity_vault.clone(), - token_program: Program::try_from(&self.token_program)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + marginfi_account: AccountLoader::try_from(airls( + &marginfi_account.margin_account, + ))?, + signer: Signer::try_from(airls(&self.owner))?, + bank: AccountLoader::try_from(airls(&bank.bank))?, + signer_token_account: ails( + marginfi_account.token_accounts[bank_idx.0 as usize].clone(), + ), + bank_liquidity_vault: ails(bank.liquidity_vault.clone()), + token_program: Interface::try_from(airls(&bank.token_program))?, }, - &[], - BTreeMap::new(), + &remaining_accounts, + Default::default(), ), asset_amount.0, ); - if res.is_err() { + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + [MarginfiError::AccountDisabled.into(),].contains(&error), + "Unexpected deposit error: {:?}", + error + ); + cache.revert(); - } + + false + } else { + true + }; self.metrics .write() .unwrap() - .update_metric(MetricAction::Deposit, res.is_ok()); + .update_metric(MetricAction::Deposit, success); Ok(()) } @@ -388,6 +425,7 @@ impl<'bump> MarginfiFuzzContext<'bump> { ) -> anyhow::Result<()> { let marginfi_account = &self.marginfi_accounts[account_idx.0 as usize]; let bank = &self.banks[bank_idx.0 as usize]; + sort_balances(airls(&marginfi_account.margin_account)); let cache = AccountInfoCache::new(&[ marginfi_account.margin_account.clone(), @@ -396,40 +434,69 @@ impl<'bump> MarginfiFuzzContext<'bump> { bank.liquidity_vault.clone(), ]); + let mut remaining_accounts = vec![]; + if bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(bank.mint.clone())); + } + let res = marginfi::instructions::marginfi_account::lending_account_repay( Context::new( &marginfi::ID, &mut marginfi::instructions::LendingAccountRepay { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - marginfi_account: AccountLoader::try_from(&marginfi_account.margin_account)?, - signer: Signer::try_from(&self.owner)?, - bank: AccountLoader::try_from(&bank.bank)?, - signer_token_account: marginfi_account.token_accounts[bank_idx.0 as usize] - .clone(), - bank_liquidity_vault: bank.liquidity_vault.clone(), - token_program: Program::try_from(&self.token_program)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + marginfi_account: AccountLoader::try_from(airls( + &marginfi_account.margin_account, + ))?, + signer: Signer::try_from(airls(&self.owner))?, + bank: AccountLoader::try_from(airls(&bank.bank))?, + signer_token_account: ails( + marginfi_account.token_accounts[bank_idx.0 as usize].clone(), + ), + bank_liquidity_vault: ails(bank.liquidity_vault.clone()), + token_program: Interface::try_from(airls(&bank.token_program))?, }, - &[], - BTreeMap::new(), + &remaining_accounts, + Default::default(), ), asset_amount.0, Some(repay_all), ); - if res.is_err() { + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + vec![ + MarginfiError::NoLiabilityFound.into(), + MarginfiError::OperationRepayOnly.into(), + // TODO: maybe change + MarginfiError::BankAccountNotFound.into(), + MarginfiError::AccountDisabled.into(), + ] + .contains(&error), + "Unexpected repay error: {:?}", + error + ); + cache.revert(); - } + + false + } else { + true + }; self.metrics .write() .unwrap() - .update_metric(MetricAction::Repay, res.is_ok()); + .update_metric(MetricAction::Repay, success); Ok(()) } pub fn process_action_withdraw( - &'bump self, + &'state self, account_idx: &AccountIdx, bank_idx: &BankIdx, asset_amount: &AssetAmount, @@ -437,6 +504,7 @@ impl<'bump> MarginfiFuzzContext<'bump> { ) -> anyhow::Result<()> { self.refresh_oracle_accounts(); let marginfi_account = &self.marginfi_accounts[account_idx.0 as usize]; + sort_balances(airls(&marginfi_account.margin_account)); let bank = &self.banks[bank_idx.0 as usize]; @@ -457,46 +525,75 @@ impl<'bump> MarginfiFuzzContext<'bump> { vec![] }; + let mut remaining_accounts = vec![]; + if bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(bank.mint.clone())); + } + remaining_accounts.extend(marginfi_account.get_remaining_accounts( + &self.get_bank_map(), + vec![], + remove_all_bank, + )); let res = marginfi::instructions::marginfi_account::lending_account_withdraw( Context::new( &marginfi::ID, &mut marginfi::instructions::LendingAccountWithdraw { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - marginfi_account: AccountLoader::try_from(&marginfi_account.margin_account)?, - signer: Signer::try_from(&self.owner)?, - bank: AccountLoader::try_from(&bank.bank)?, - token_program: Program::try_from(&self.token_program)?, - destination_token_account: Account::try_from( - &marginfi_account.token_accounts[bank_idx.0 as usize].clone(), - )?, - bank_liquidity_vault_authority: bank.liquidity_vault_authority.clone(), - bank_liquidity_vault: Account::try_from(&bank.liquidity_vault)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + marginfi_account: AccountLoader::try_from(airls( + &marginfi_account.margin_account, + ))?, + signer: Signer::try_from(airls(&self.owner))?, + bank: AccountLoader::try_from(airls(&bank.bank))?, + token_program: Interface::try_from(airls(&bank.token_program))?, + destination_token_account: InterfaceAccount::try_from(airls( + &marginfi_account.token_accounts[bank_idx.0 as usize], + ))?, + bank_liquidity_vault_authority: ails(bank.liquidity_vault_authority.clone()), + bank_liquidity_vault: InterfaceAccount::try_from(airls(&bank.liquidity_vault))?, }, - &marginfi_account.get_remaining_accounts( - &self.get_bank_map(), - vec![], - remove_all_bank, - ), - BTreeMap::new(), + aisls(&remaining_accounts), + Default::default(), ), asset_amount.0, withdraw_all, ); - if res.is_err() { + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + [ + MarginfiError::OperationWithdrawOnly.into(), + MarginfiError::IllegalUtilizationRatio.into(), + MarginfiError::RiskEngineInitRejected.into(), + MarginfiError::NoAssetFound.into(), + MarginfiError::BankAccountNotFound.into(), + MarginfiError::AccountDisabled.into(), + ] + .contains(&error), + "Unexpected withdraw error: {:?}", + error + ); + cache.revert(); - } + + false + } else { + true + }; self.metrics .write() .unwrap() - .update_metric(MetricAction::Withdraw, res.is_ok()); + .update_metric(MetricAction::Withdraw, success); Ok(()) } pub fn process_action_borrow( - &'bump self, + &'state self, account_idx: &AccountIdx, bank_idx: &BankIdx, asset_amount: &AssetAmount, @@ -511,49 +608,74 @@ impl<'bump> MarginfiFuzzContext<'bump> { marginfi_account.token_accounts[bank_idx.0 as usize].clone(), bank.liquidity_vault.clone(), ]); + sort_balances(airls(&marginfi_account.margin_account)); + let mut remaining_accounts = vec![]; + if bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(bank.mint.clone())); + } + remaining_accounts.extend(marginfi_account.get_remaining_accounts( + &self.get_bank_map(), + vec![bank.bank.key()], + vec![], + )); let res = marginfi::instructions::marginfi_account::lending_account_borrow( Context::new( &marginfi::ID, &mut marginfi::instructions::LendingAccountBorrow { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - marginfi_account: AccountLoader::try_from(&marginfi_account.margin_account)?, - signer: Signer::try_from(&self.owner)?, - bank: AccountLoader::try_from(&bank.bank)?, - token_program: Program::try_from(&self.token_program)?, - destination_token_account: Account::try_from( - &marginfi_account.token_accounts[bank_idx.0 as usize].clone(), - )?, - bank_liquidity_vault_authority: bank.liquidity_vault_authority.clone(), - bank_liquidity_vault: Account::try_from(&bank.liquidity_vault)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + marginfi_account: AccountLoader::try_from(airls( + &marginfi_account.margin_account, + ))?, + signer: Signer::try_from(airls(&self.owner))?, + bank: AccountLoader::try_from(airls(&bank.bank))?, + token_program: Interface::try_from(airls(&bank.token_program))?, + destination_token_account: InterfaceAccount::try_from(airls( + &marginfi_account.token_accounts[bank_idx.0 as usize], + ))?, + bank_liquidity_vault_authority: ails(bank.liquidity_vault_authority.clone()), + bank_liquidity_vault: InterfaceAccount::try_from(airls(&bank.liquidity_vault))?, }, - &marginfi_account.get_remaining_accounts( - &self.get_bank_map(), - vec![bank.bank.key()], - vec![], - ), - BTreeMap::new(), + aisls(&remaining_accounts), + Default::default(), ), asset_amount.0, ); - let is_ok = res.is_ok(); + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + vec![ + MarginfiError::RiskEngineInitRejected.into(), + MarginfiError::IsolatedAccountIllegalState.into(), + MarginfiError::IllegalUtilizationRatio.into(), + MarginfiError::AccountDisabled.into(), + ] + .contains(&error), + "Unexpected borrow error: {:?}", + error + ); - if !is_ok { - log!("{}", res.unwrap_err()); cache.revert(); - } + + false + } else { + true + }; self.metrics .write() .unwrap() - .update_metric(MetricAction::Borrow, is_ok); + .update_metric(MetricAction::Borrow, success); Ok(()) } pub fn process_liquidate_account( - &'bump self, + &'state self, liquidator_idx: &AccountIdx, liquidatee_idx: &AccountIdx, asset_amount: &AssetAmount, @@ -561,10 +683,30 @@ impl<'bump> MarginfiFuzzContext<'bump> { self.refresh_oracle_accounts(); let liquidator_account = &self.marginfi_accounts[liquidator_idx.0 as usize]; let liquidatee_account = &self.marginfi_accounts[liquidatee_idx.0 as usize]; + sort_balances(airls(&liquidator_account.margin_account)); + sort_balances(airls(&liquidatee_account.margin_account)); + + if liquidator_account.margin_account.key() == liquidatee_account.margin_account.key() { + self.metrics + .write() + .unwrap() + .update_metric(MetricAction::Liquidate, false); + + return Ok(()); + } let (asset_bank_idx, liab_bank_idx) = if let Some(a) = liquidatee_account.get_liquidation_banks(&self.banks) { - a + if a.0 == a.1 { + self.metrics + .write() + .unwrap() + .update_metric(MetricAction::Liquidate, false); + + return Ok(()); + } else { + a + } } else { self.metrics .write() @@ -587,11 +729,15 @@ impl<'bump> MarginfiFuzzContext<'bump> { liab_bank.insurance_vault.clone(), ]); - let mut remaining_accounts = vec![asset_bank.oracle.clone(), liab_bank.oracle.clone()]; + let mut remaining_accounts = vec![]; + if liab_bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(liab_bank.mint.clone())); + } + remaining_accounts.extend(vec![asset_bank.oracle.clone(), liab_bank.oracle.clone()]); let mut liquidator_remaining_accounts = liquidator_account.get_remaining_accounts( &self.get_bank_map(), - vec![asset_bank.bank.key(), liab_bank.bank.key()], + vec![liab_bank.bank.key(), asset_bank.bank.key()], vec![], ); let mut liquidatee_remaining_accounts = @@ -604,48 +750,69 @@ impl<'bump> MarginfiFuzzContext<'bump> { Context::new( &marginfi::id(), &mut marginfi::instructions::LendingAccountLiquidate { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - asset_bank: AccountLoader::try_from(&asset_bank.bank.clone())?, - liab_bank: AccountLoader::try_from(&liab_bank.bank.clone())?, - liquidator_marginfi_account: AccountLoader::try_from( - &liquidator_account.margin_account.clone(), - )?, - signer: Signer::try_from(&self.owner)?, - liquidatee_marginfi_account: AccountLoader::try_from( - &liquidatee_account.margin_account.clone(), - )?, - bank_liquidity_vault_authority: liab_bank.liquidity_vault_authority.clone(), - bank_liquidity_vault: Box::new(Account::try_from( - &liab_bank.liquidity_vault.clone(), - )?), - bank_insurance_vault: liab_bank.insurance_vault.clone(), - token_program: Program::try_from(&self.token_program)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + asset_bank: AccountLoader::try_from(airls(&asset_bank.bank))?, + liab_bank: AccountLoader::try_from(airls(&liab_bank.bank))?, + liquidator_marginfi_account: AccountLoader::try_from(airls( + &liquidator_account.margin_account, + ))?, + signer: Signer::try_from(airls(&self.owner))?, + liquidatee_marginfi_account: AccountLoader::try_from(airls( + &liquidatee_account.margin_account, + ))?, + bank_liquidity_vault_authority: ails( + liab_bank.liquidity_vault_authority.clone(), + ), + bank_liquidity_vault: Box::new(InterfaceAccount::try_from(airls( + &liab_bank.liquidity_vault, + ))?), + bank_insurance_vault: ails(liab_bank.insurance_vault.clone()), + token_program: Interface::try_from(airls(&liab_bank.token_program))?, }, - &remaining_accounts, - BTreeMap::new(), + aisls(&remaining_accounts), + Default::default(), ), asset_amount.0, ); - let is_ok = res.is_ok(); - - self.metrics - .write() - .unwrap() - .update_metric(MetricAction::Liquidate, is_ok); + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + vec![ + MarginfiError::RiskEngineInitRejected.into(), + MarginfiError::IsolatedAccountIllegalState.into(), + MarginfiError::IllegalUtilizationRatio.into(), + MarginfiError::IllegalLiquidation.into(), + MarginfiError::AccountDisabled.into(), + MarginfiError::MathError.into(), // TODO: would be best to avoid this one + ] + .contains(&error), + "Unexpected liquidate error: {:?}", + error + ); - if !is_ok { account_cache.revert(); - log!("Error Liquidate {:?}", res.unwrap_err()); + + false } else { self.process_handle_bankruptcy(liquidatee_idx, &liab_bank_idx)?; - } + + true + }; + + self.metrics + .write() + .unwrap() + .update_metric(MetricAction::Liquidate, success); Ok(()) } pub fn process_handle_bankruptcy( - &'bump self, + &'state self, account_idx: &AccountIdx, bank_idx: &BankIdx, ) -> anyhow::Result<()> { @@ -661,32 +828,59 @@ impl<'bump> MarginfiFuzzContext<'bump> { bank.insurance_vault.clone(), ]); + let mut remaining_accounts = vec![]; + if bank.token_program.key() == spl_token_2022::ID { + remaining_accounts.push(ails(bank.mint.clone())); + } + remaining_accounts.extend(marginfi_account.get_remaining_accounts( + &self.get_bank_map(), + vec![], + vec![], + )); let res = marginfi::instructions::lending_pool_handle_bankruptcy(Context::new( &marginfi::ID, &mut marginfi::instructions::LendingPoolHandleBankruptcy { - marginfi_group: AccountLoader::try_from(&self.marginfi_group.clone())?, - signer: Signer::try_from(&self.owner)?, - bank: AccountLoader::try_from(&bank.bank.clone())?, - marginfi_account: AccountLoader::try_from( - &marginfi_account.margin_account.clone(), - )?, - liquidity_vault: bank.liquidity_vault.clone(), - insurance_vault: Box::new(Account::try_from(&bank.insurance_vault.clone())?), - insurance_vault_authority: bank.insurance_vault_authority.clone(), - token_program: Program::try_from(&self.token_program)?, + marginfi_group: AccountLoader::try_from(airls(&self.marginfi_group))?, + signer: Signer::try_from(airls(&self.owner))?, + bank: AccountLoader::try_from(airls(&bank.bank))?, + marginfi_account: AccountLoader::try_from(airls(&marginfi_account.margin_account))?, + liquidity_vault: ails(bank.liquidity_vault.clone()), + insurance_vault: Box::new(InterfaceAccount::try_from(airls( + &bank.insurance_vault, + ))?), + insurance_vault_authority: ails(bank.insurance_vault_authority.clone()), + token_program: Interface::try_from(airls(&bank.token_program))?, }, - &marginfi_account.get_remaining_accounts(&self.get_bank_map(), vec![], vec![]), - BTreeMap::new(), + aisls(&remaining_accounts), + Default::default(), )); - if res.is_err() { + let success = if res.is_err() { + let error = res.unwrap_err(); + + self.metrics.write().unwrap().update_error(&error); + + assert!( + vec![ + MarginfiError::AccountDisabled.into(), + MarginfiError::AccountNotBankrupt.into(), + ] + .contains(&error), + "Unexpected handle bankruptcy error: {:?}", + error + ); + cache.revert(); - } + + false + } else { + true + }; self.metrics .write() .unwrap() - .update_metric(MetricAction::Bankruptcy, res.is_ok()); + .update_metric(MetricAction::Bankruptcy, success); Ok(()) } @@ -707,6 +901,16 @@ impl<'bump> MarginfiFuzzContext<'bump> { } } +fn sort_balances<'a>(marginfi_account_ai: &'a AccountInfo<'a>) { + let marginfi_account_loader = + AccountLoader::::try_from(marginfi_account_ai).unwrap(); + let mut marginfi_account = marginfi_account_loader.load_mut().unwrap(); + marginfi_account + .lending_account + .balances + .sort_by_key(|a| !a.active); +} + pub fn set_discriminator(ai: AccountInfo) { let mut data = ai.try_borrow_mut_data().unwrap(); @@ -717,11 +921,11 @@ pub fn set_discriminator(ai: AccountInfo) { data[..8].copy_from_slice(&T::DISCRIMINATOR); } -fn initialize_marginfi_group<'bump>( - state: &'bump AccountsState, - admin: AccountInfo<'bump>, - system_program: AccountInfo<'bump>, -) -> AccountInfo<'bump> { +fn initialize_marginfi_group<'a>( + state: &'a AccountsState, + admin: AccountInfo<'a>, + system_program: AccountInfo<'a>, +) -> AccountInfo<'a> { let program_id = marginfi::id(); let marginfi_group = state.new_owned_account(size_of::(), program_id, Rent::free()); @@ -730,13 +934,13 @@ fn initialize_marginfi_group<'bump>( &marginfi::id(), &mut marginfi::instructions::MarginfiGroupInitialize { // Unchecked because we are initializing the account. - marginfi_group: AccountLoader::try_from_unchecked(&program_id, &marginfi_group) + marginfi_group: AccountLoader::try_from_unchecked(&program_id, airls(&marginfi_group)) .unwrap(), - admin: Signer::try_from(&admin).unwrap(), - system_program: Program::try_from(&system_program).unwrap(), + admin: Signer::try_from(airls(&admin)).unwrap(), + system_program: Program::try_from(airls(&system_program)).unwrap(), }, &[], - BTreeMap::new(), + Default::default(), )) .unwrap(); @@ -852,9 +1056,9 @@ mod tests { let margin_account = &a.marginfi_accounts[0]; let bank_map = a.get_bank_map(); let remaining_accounts = - &margin_account.get_remaining_accounts(&bank_map, vec![], vec![]); + margin_account.get_remaining_accounts(&bank_map, vec![], vec![]); - let re = RiskEngine::new(&marginfi_account, remaining_accounts).unwrap(); + let re = RiskEngine::new(&marginfi_account, aisls(&remaining_accounts)).unwrap(); let health = re .get_account_health( @@ -912,9 +1116,9 @@ mod tests { let margin_account = &a.marginfi_accounts[0]; let bank_map = a.get_bank_map(); let remaining_accounts = - &margin_account.get_remaining_accounts(&bank_map, vec![], vec![]); + margin_account.get_remaining_accounts(&bank_map, vec![], vec![]); - let re = RiskEngine::new(&marginfi_account, remaining_accounts).unwrap(); + let re = RiskEngine::new(&marginfi_account, aisls(&remaining_accounts)).unwrap(); let health = re .get_account_health( @@ -955,22 +1159,39 @@ mod tests { let a = MarginfiFuzzContext::setup(&account_state, &[BankAndOracleConfig::dummy(); 2], 3); - let price = { + a.process_update_oracle(&BankIdx(0), &PriceChange(1100)) + .unwrap(); + + let new_price = { let data = a.banks[0].oracle.try_borrow_data().unwrap(); let data = bytemuck::from_bytes::(&data); - data.ema_price.val }; - a.process_update_oracle(&BankIdx(0), &PriceChange(1100)) - .unwrap(); + assert_eq!(new_price, 1100); + } - let new_price = { + #[test] + fn pyth_timestamp_update() { + let account_state = AccountsState::new(); + + let a = MarginfiFuzzContext::setup(&account_state, &[BankAndOracleConfig::dummy(); 2], 3); + + let initial_timestamp = { let data = a.banks[0].oracle.try_borrow_data().unwrap(); let data = bytemuck::from_bytes::(&data); - data.ema_price.val + data.timestamp }; + assert_eq!(initial_timestamp, 0); + + a.banks[0].refresh_oracle(123_456).unwrap(); - assert_eq!(price, new_price - 1100); + let updated_timestamp_via_0_10 = { + let pf = + pyth_sdk_solana::load_price_feed_from_account_info(&a.banks[0].oracle).unwrap(); + + pf.get_ema_price_unchecked().publish_time + }; + assert_eq!(updated_timestamp_via_0_10, 123_456); } } diff --git a/programs/marginfi/fuzz/src/metrics.rs b/programs/marginfi/fuzz/src/metrics.rs index b81c25668..14e1c3a95 100644 --- a/programs/marginfi/fuzz/src/metrics.rs +++ b/programs/marginfi/fuzz/src/metrics.rs @@ -1,5 +1,7 @@ +use anchor_lang::error::Error; +use itertools::Itertools; use lazy_static::lazy_static; -use std::sync::atomic::AtomicU64; +use std::{collections::HashMap, sync::atomic::AtomicU64}; lazy_static! { pub static ref LOG_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -9,6 +11,7 @@ lazy_static! { macro_rules! log { ($($arg:tt)*) => { #[cfg(feature = "capture_log")] { + use base64::Engine; let mut ct = $crate::metrics::LOG_COUNTER.load(std::sync::atomic::Ordering::Acquire); let header = format!("{} -", ct); @@ -36,6 +39,7 @@ pub struct Metrics { handle_bankruptcy_s: u64, handle_bankruptcy_e: u64, pub price_update: u64, + error_counts: HashMap, } #[derive(Debug)] @@ -70,15 +74,38 @@ impl Metrics { *metric += 1; } + pub fn update_error(&mut self, error: &Error) { + let error_name = match error { + Error::AnchorError(e) => { + e.error_name.clone() + } + Error::ProgramError(e) => { + e.program_error.to_string() + } + }; + + let error_count = self.error_counts.entry(error_name).or_insert(0); + *error_count += 1; + } + pub fn print(&self) { - print!("\r"); - print!("{}", self.get_print_string()); + println!("{}", self.get_print_string()); + println!("{:?}", self.get_error_counts_string()); + } + + pub fn get_error_counts_string(&self) -> String { + let top_5_by_count = self.error_counts.iter().sorted_by_key(|(_, count)| *count).rev().take(5).collect::>(); + let top_5_by_count_str = top_5_by_count.iter().map(|(error, count)| { + format!("{}: {}", error, count) + }).collect::>().join(", "); + + format!("Top 5 errors: {}", top_5_by_count_str) } pub fn get_print_string(&self) -> String { format!("Deposit\t{}\t{}\tWithd\t{}\t{}\tBorrow\t{}\t{}\tRepay\t{}\t{}\tLiq\t{}\t{}\tBank\t{}\t{}\tUpdate\t{}", self.deposit_s, - self.deposit_e, + self.deposit_e, self.withdraw_s, self.withdraw_e, self.borrow_s, diff --git a/programs/marginfi/fuzz/src/stubs.rs b/programs/marginfi/fuzz/src/stubs.rs index 5ffd38989..d666d2b60 100644 --- a/programs/marginfi/fuzz/src/stubs.rs +++ b/programs/marginfi/fuzz/src/stubs.rs @@ -1,9 +1,13 @@ use anchor_lang::prelude::{AccountInfo, Clock, Pubkey}; +use anchor_spl::token_2022::spl_token_2022; use lazy_static::lazy_static; use solana_program::{entrypoint::ProgramResult, instruction::Instruction, program_stubs}; use crate::log; +#[cfg(feature = "capture_log")] +use itertools::Itertools; + lazy_static! { static ref VERBOSE: u32 = std::env::var("FUZZ_VERBOSE") .map(|s| s.parse()) @@ -19,18 +23,24 @@ pub struct TestSyscallStubs { } impl program_stubs::SyscallStubs for TestSyscallStubs { - fn sol_log(&self, message: &str) { + fn sol_log(&self, _message: &str) { if *VERBOSE == 0 { return; } - log!("Program Log: {}", message); + log!("Program Log: {}", _message); } - fn sol_log_data(&self, fields: &[&[u8]]) { + fn sol_log_data(&self, _fields: &[&[u8]]) { if *VERBOSE == 0 { return; } - log!("data: {}", fields.iter().map(base64::encode).join(" ")); + log!( + "data: {}", + _fields + .iter() + .map(|field| base64::engine::general_purpose::STANDARD.encode(field)) + .join(" ") + ); } fn sol_invoke_signed( @@ -57,11 +67,19 @@ impl program_stubs::SyscallStubs for TestSyscallStubs { } } - spl_token::processor::Processor::process( - &instruction.program_id, - &new_account_infos, - &instruction.data, - ) + if instruction.program_id == spl_token::ID { + spl_token::processor::Processor::process( + &instruction.program_id, + &new_account_infos, + &instruction.data, + ) + } else { + spl_token_2022::processor::Processor::process( + &instruction.program_id, + &new_account_infos, + &instruction.data, + ) + } } fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 { diff --git a/programs/marginfi/fuzz/src/user_accounts.rs b/programs/marginfi/fuzz/src/user_accounts.rs index 011493014..05958137a 100644 --- a/programs/marginfi/fuzz/src/user_accounts.rs +++ b/programs/marginfi/fuzz/src/user_accounts.rs @@ -26,7 +26,10 @@ impl<'info> UserAccount<'info> { } } - pub fn get_liquidation_banks(&self, banks: &[BankAccounts]) -> Option<(BankIdx, BankIdx)> { + pub fn get_liquidation_banks( + &'info self, + banks: &[BankAccounts], + ) -> Option<(BankIdx, BankIdx)> { let marginfi_account_al = AccountLoader::::try_from(&self.margin_account).ok()?; let marginfi_account = marginfi_account_al.load().ok()?; @@ -68,7 +71,7 @@ impl<'info> UserAccount<'info> { } pub fn get_remaining_accounts( - &self, + &'info self, bank_map: &HashMap>, include_banks: Vec, exclude_banks: Vec, @@ -85,13 +88,12 @@ impl<'info> UserAccount<'info> { .iter() .filter(|a| a.active && !exclude_banks.contains(&a.bank_pk)) .flat_map(|balance| { - let _bank_accounts = bank_map.get(&balance.bank_pk).unwrap(); - let bank_accounts = bank_map.get(&balance.bank_pk).unwrap(); + assert_eq!(balance.bank_pk, bank_accounts.bank.key()); already_included_banks.insert(bank_accounts.bank.key()); - vec![bank_accounts.bank.clone(), bank_accounts.oracle.clone()] + [bank_accounts.bank.clone(), bank_accounts.oracle.clone()] }) .collect::>(); @@ -104,7 +106,8 @@ impl<'info> UserAccount<'info> { .iter() .flat_map(|key| { let bank_accounts = bank_map.get(key).unwrap(); - vec![bank_accounts.bank.clone(), bank_accounts.oracle.clone()] + + [bank_accounts.bank.clone(), bank_accounts.oracle.clone()] }) .collect::>(); diff --git a/programs/marginfi/fuzz/src/utils.rs b/programs/marginfi/fuzz/src/utils.rs index e69de29bb..4b4955a9c 100644 --- a/programs/marginfi/fuzz/src/utils.rs +++ b/programs/marginfi/fuzz/src/utils.rs @@ -0,0 +1,20 @@ +use solana_sdk::account_info::AccountInfo; + +/// This is safe because it shortens lifetimes 'info: 'o and 'a: 'o to that of 'o +pub fn account_info_ref_lifetime_shortener<'info: 'a + 'o, 'a: 'o, 'o>( + ai: &'a AccountInfo<'info>, +) -> &'o AccountInfo<'o> { + unsafe { core::mem::transmute(ai) } +} + +/// This is safe because it shortens lifetimes 'info: 'o to that of 'o +pub fn account_info_lifetime_shortener<'info: 'o, 'o>(ai: AccountInfo<'info>) -> AccountInfo<'o> { + unsafe { core::mem::transmute(ai) } +} + +/// This is safe because it shortens lifetimes 'info: 'o and 'a: 'o to that of 'o +pub fn account_info_slice_lifetime_shortener<'info: 'a + 'o, 'a: 'o, 'o>( + ai: &'a [AccountInfo<'info>], +) -> &'o [AccountInfo<'o>] { + unsafe { core::mem::transmute(ai) } +} diff --git a/programs/marginfi/src/constants.rs b/programs/marginfi/src/constants.rs index a7c48e4d9..874a2905b 100644 --- a/programs/marginfi/src/constants.rs +++ b/programs/marginfi/src/constants.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; use fixed::types::I80F48; use fixed_macro::types::I80F48; +use pyth_solana_receiver_sdk::price_update::VerificationLevel; use solana_program::pubkey; pub const LIQUIDITY_VAULT_AUTHORITY_SEED: &str = "liquidity_vault_auth"; @@ -11,13 +12,16 @@ pub const LIQUIDITY_VAULT_SEED: &str = "liquidity_vault"; pub const INSURANCE_VAULT_SEED: &str = "insurance_vault"; pub const FEE_VAULT_SEED: &str = "fee_vault"; +pub const LIQUID_INSURANCE_SEED: &str = "liquid_insurance_seed"; +pub const LIQUID_INSURANCE_USER_SEED: &str = "liquid_insurance_user"; + pub const EMISSIONS_AUTH_SEED: &str = "emissions_auth_seed"; pub const EMISSIONS_TOKEN_ACCOUNT_SEED: &str = "emissions_token_account_seed"; cfg_if::cfg_if! { if #[cfg(feature = "devnet")] { pub const PYTH_ID: Pubkey = pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"); - } else if #[cfg(feature = "mainnet-beta")] { + } else if #[cfg(any(feature = "mainnet-beta", feature = "staging"))] { pub const PYTH_ID: Pubkey = pubkey!("FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH"); } else { pub const PYTH_ID: Pubkey = pubkey!("5rYvdyWAunZgD2EC1aKo7hQbutUUnkt7bBFM6xNq2z7Z"); @@ -125,3 +129,7 @@ pub const EXP_10: [i128; MAX_EXP_10] = [ /// Value where total_asset_value_init_limit is considered inactive pub const TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE: u64 = 0; + +pub const MIN_PYTH_PUSH_VERIFICATION_LEVEL: VerificationLevel = VerificationLevel::Full; +pub const PYTH_PUSH_PYTH_SPONSORED_SHARD_ID: u16 = 0; +pub const PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID: u16 = 3301; diff --git a/programs/marginfi/src/errors.rs b/programs/marginfi/src/errors.rs index 81fcd1ffd..c6091c0cc 100644 --- a/programs/marginfi/src/errors.rs +++ b/programs/marginfi/src/errors.rs @@ -6,7 +6,6 @@ pub enum MarginfiError { MathError, #[msg("Invalid bank index")] // 6001 BankNotFound, - #[msg("Lending account balance not found")] // 6002 LendingAccountBalanceNotFound, #[msg("Bank deposit capacity exceeded")] // 6003 @@ -44,7 +43,7 @@ pub enum MarginfiError { #[msg("Bank is ReduceOnly mode")] // 6019 BankReduceOnly, #[msg("Bank is missing")] // 6020 - BankAccoutNotFound, + BankAccountNotFound, #[msg("Operation is deposit-only")] // 6021 OperationDepositOnly, #[msg("Operation is withdraw-only")] // 6022 @@ -65,13 +64,13 @@ pub enum MarginfiError { BankLiabilityCapacityExceeded, #[msg("Invalid Price")] // 6030 InvalidPrice, - #[msg("Account can have only one liablity when account is under isolated risk")] // 6031 + #[msg("Account can have only one liability when account is under isolated risk")] // 6031 IsolatedAccountIllegalState, // 6032 #[msg("Emissions already setup")] EmissionsAlreadySetup, #[msg("Oracle is not set")] // 6033 OracleNotSetup, - #[msg("Invalid swithcboard decimal conversion")] // 6034 + #[msg("Invalid switchboard decimal conversion")] // 6034 InvalidSwitchboardDecimalConversion, #[msg("Cannot close balance because of outstanding emissions")] // 6035 CannotCloseOutstandingEmissions, @@ -93,6 +92,18 @@ pub enum MarginfiError { IllegalAccountAuthorityTransfer, #[msg("Unauthorized")] // 6045 Unauthorized, + #[msg("Invalid account authority")] // 6046 + IllegalAction, + #[msg("Token22 Banks require mint account as first remaining account")] // 6047 + T22MintRequired, + #[msg("InvalidWithdrawal")] // 6048 + InvalidWithdrawal, + #[msg("Insurance fund account balance slots are full")] // 6049 + InsuranceFundAccountBalanceSlotsFull, + #[msg("Insurance fund account withdraw slots are full")] // 6050 + InsuranceFundAccountWithdrawSlotsFull, + #[msg("Insurance fund withdraw period must be greater than zero")] // 6050 + InsuranceFundInvalidWithdrawPeriod, } impl From for ProgramError { diff --git a/programs/marginfi/src/events.rs b/programs/marginfi/src/events.rs index 35518c6f6..93d6bd937 100644 --- a/programs/marginfi/src/events.rs +++ b/programs/marginfi/src/events.rs @@ -17,6 +17,11 @@ pub struct AccountEventHeader { pub marginfi_group: Pubkey, } +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct LiquidInsuranceFundEventHeader { + pub bank: Pubkey, +} + // marginfi group events #[event] @@ -146,3 +151,35 @@ pub struct MarginfiAccountTransferAccountAuthorityEvent { pub old_account_authority: Pubkey, pub new_account_authority: Pubkey, } + +// liquid insurance fund events + +#[event] +pub struct MarginfiCreateNewLiquidInsuranceFundEvent { + pub header: LiquidInsuranceFundEventHeader, +} + +#[event] +pub struct MarginfiCreateNewLiquidInsuranceFundAccountEvent { + pub user: Pubkey, +} + +#[event] +pub struct MarginfiDepositIntoLiquidInsuranceFundEvent { + pub header: LiquidInsuranceFundEventHeader, + pub amount: u64, + pub signer_token_address: Pubkey, +} + +#[event] +pub struct MarginfiWithdrawClaimLiquidInsuranceFundEvent { + pub header: LiquidInsuranceFundEventHeader, + pub amount: u64, + pub success: bool, +} + +#[event] +pub struct MarginfiWithdrawRequestLiquidInsuranceFundEvent { + pub header: LiquidInsuranceFundEventHeader, + pub shares: f64, +} diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/create_account.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/create_account.rs new file mode 100644 index 000000000..43604f821 --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/create_account.rs @@ -0,0 +1,45 @@ +use crate::{ + constants::LIQUID_INSURANCE_USER_SEED, + events::MarginfiCreateNewLiquidInsuranceFundAccountEvent, prelude::*, + state::liquid_insurance_fund::LiquidInsuranceFundAccount, +}; +use anchor_lang::prelude::*; +use solana_program::sysvar::Sysvar; + +pub fn create_liquid_insurance_fund_account( + ctx: Context, +) -> MarginfiResult { + let CreateLiquidInsuranceFundAccount { + user_insurance_fund_account, + signer, + .. + } = ctx.accounts; + + let mut user_insurance_fund_account = user_insurance_fund_account.load_init()?; + + user_insurance_fund_account.initialize(signer.key()); + + emit!(MarginfiCreateNewLiquidInsuranceFundAccountEvent { user: signer.key() }); + + Ok(()) +} + +#[derive(Accounts)] +pub struct CreateLiquidInsuranceFundAccount<'info> { + #[account( + init, + payer = signer, + space = 8 + std::mem::size_of::(), + seeds = [ + LIQUID_INSURANCE_USER_SEED.as_bytes(), + signer.key().as_ref(), + ], + bump + )] + pub user_insurance_fund_account: AccountLoader<'info, LiquidInsuranceFundAccount>, + + #[account(mut)] + pub signer: Signer<'info>, + + pub system_program: Program<'info, System>, +} diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/create_fund.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/create_fund.rs new file mode 100644 index 000000000..3b4f1ed91 --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/create_fund.rs @@ -0,0 +1,94 @@ +use crate::{ + constants::{INSURANCE_VAULT_AUTHORITY_SEED, LIQUID_INSURANCE_SEED}, + events::{LiquidInsuranceFundEventHeader, MarginfiCreateNewLiquidInsuranceFundEvent}, + state::{liquid_insurance_fund::LiquidInsuranceFund, marginfi_group::Bank}, + MarginfiGroup, MarginfiResult, +}; +use anchor_lang::prelude::*; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; + +#[derive(Accounts)] +pub struct CreateLiquidInsuranceFund<'info> { + pub marginfi_group: AccountLoader<'info, MarginfiGroup>, + + #[account( + mut, + address = marginfi_group.load()?.admin, + )] + pub signer: Signer<'info>, + + #[account( + init, + space = 8 + std::mem::size_of::(), + payer = signer, + seeds = [ + LIQUID_INSURANCE_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump, + )] + pub liquid_insurance_fund: AccountLoader<'info, LiquidInsuranceFund>, + + #[account( + constraint = bank.load()?.group == marginfi_group.key(), + )] + pub bank: AccountLoader<'info, Bank>, + + /// The corresponding insurance vault that the liquid insurance fund deposits into. + /// This is the insurance vault of the underlying bank + #[account( + mut, + address = bank.load()?.insurance_vault, + )] + pub lif_vault: Box>, + + /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ + #[account( + seeds = [ + INSURANCE_VAULT_AUTHORITY_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump = bank.load()?.insurance_vault_authority_bump + )] + pub lif_authority: AccountInfo<'info>, + + pub token_program: Interface<'info, TokenInterface>, + + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, +} + +pub fn create_liquid_insurance_fund( + ctx: Context, + min_withdraw_period: i64, +) -> MarginfiResult { + let CreateLiquidInsuranceFund { + bank, + lif_authority, + liquid_insurance_fund, + lif_vault, + .. + } = ctx.accounts; + + let bank_ = bank.load()?; + let lif_vault_bump = bank_.insurance_vault_bump; + let lif_authority_bump = bank_.insurance_vault_authority_bump; + + let mut lif = liquid_insurance_fund.load_init()?; + + lif.initialize( + bank.key(), + bank.load()?.mint, + lif_authority.key(), + min_withdraw_period, + lif_vault_bump, + lif_authority_bump, + lif_vault.amount, + )?; + + emit!(MarginfiCreateNewLiquidInsuranceFundEvent { + header: LiquidInsuranceFundEventHeader { bank: lif.bank }, + }); + + Ok(()) +} diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/deposit.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/deposit.rs new file mode 100644 index 000000000..f2bb3b593 --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/deposit.rs @@ -0,0 +1,127 @@ +use crate::{ + constants::{INSURANCE_VAULT_SEED, LIQUID_INSURANCE_USER_SEED}, + events::{LiquidInsuranceFundEventHeader, MarginfiDepositIntoLiquidInsuranceFundEvent}, + state::liquid_insurance_fund::{LiquidInsuranceFund, LiquidInsuranceFundAccount}, + utils::calculate_post_fee_spl_deposit_amount, + MarginfiError, MarginfiResult, +}; +use anchor_lang::prelude::*; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; +use fixed::types::I80F48; + +#[derive(Accounts)] +pub struct DepositIntoLiquidInsuranceFund<'info> { + #[account(mut)] + pub liquid_insurance_fund: AccountLoader<'info, LiquidInsuranceFund>, + + #[account(mut)] + pub signer: Signer<'info>, + + /// CHECK: Account to move tokens into + #[account(mut)] + pub signer_token_account: AccountInfo<'info>, + + /// The corresponding insurance vault that the liquid insurance fund deposits into. + /// This is the insurance vault of the underlying bank + #[account( + mut, + seeds = [ + INSURANCE_VAULT_SEED.as_bytes(), + liquid_insurance_fund.load()?.bank.as_ref(), + ], + bump = liquid_insurance_fund.load()?.lif_vault_bump, + )] + pub bank_insurance_vault: Box>, + + #[account( + mut, + seeds = [ + LIQUID_INSURANCE_USER_SEED.as_bytes(), + signer.key().as_ref(), + ], + bump + )] + pub user_insurance_fund_account: AccountLoader<'info, LiquidInsuranceFundAccount>, + + pub token_program: Interface<'info, TokenInterface>, + + pub system_program: Program<'info, System>, +} + +/// 1) Check for existing deposit, or try to find free slot if non-existent +/// 2) Calculate deposit_num_shares(deposit_amount) +/// 3) SPL transfer to deposit +/// 4) Update user shares, total shares +pub fn deposit_into_liquid_insurance_fund<'info>( + mut ctx: Context<'_, '_, 'info, 'info, DepositIntoLiquidInsuranceFund<'info>>, + deposit_amount: u64, +) -> MarginfiResult { + if deposit_amount == 0 { + return Ok(()); + } + + let DepositIntoLiquidInsuranceFund { + liquid_insurance_fund: liquid_insurance_fund_loader, + signer, + signer_token_account, + bank_insurance_vault, + token_program, + user_insurance_fund_account, + .. + } = ctx.accounts; + let mut liquid_insurance_fund = liquid_insurance_fund_loader.load_mut()?; + let maybe_bank_mint = crate::utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &liquid_insurance_fund.bank_mint, + token_program.key, + )?; + let clock = Clock::get()?; + + let mut user_insurance_fund_account = user_insurance_fund_account.load_mut()?; + + // 1) Check for existing deposit, or try to find free slot if non-existent + let deposit = user_insurance_fund_account + .get_or_init_deposit(&liquid_insurance_fund_loader.key()) + .ok_or(MarginfiError::InsuranceFundAccountBalanceSlotsFull)?; + + liquid_insurance_fund.update_share_price_internal(bank_insurance_vault.amount.into())?; + + // 2) Calculate deposit_num_shares(deposit_amount) + let postfee_deposit_amount = maybe_bank_mint + .as_ref() + .map(|mint_ai| { + calculate_post_fee_spl_deposit_amount( + mint_ai.to_account_info(), + deposit_amount, + clock.epoch, + ) + }) + .unwrap_or(Ok(deposit_amount))?; + let deposit_num_shares: I80F48 = + liquid_insurance_fund.get_shares(I80F48::from_num(postfee_deposit_amount))?; + + // SPL transfer to deposit + liquid_insurance_fund.deposit_spl_transfer( + deposit_amount, + signer_token_account.to_account_info(), + bank_insurance_vault.to_account_info(), + signer.to_account_info(), + token_program.to_account_info(), + maybe_bank_mint.as_ref(), + ctx.remaining_accounts, + )?; + + // 3) Update user shares, total shares + deposit.add_shares(deposit_num_shares); + liquid_insurance_fund.deposit_shares(deposit_num_shares)?; + + emit!(MarginfiDepositIntoLiquidInsuranceFundEvent { + header: LiquidInsuranceFundEventHeader { + bank: liquid_insurance_fund.bank, + }, + amount: postfee_deposit_amount, + signer_token_address: signer_token_account.key(), + }); + + Ok(()) +} diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/mod.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/mod.rs new file mode 100644 index 000000000..d04470c20 --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/mod.rs @@ -0,0 +1,18 @@ +//! Module for liquid insurance pool instructions +//! Includes: +//! Create account +//! Create liquid insurance fund +//! Deposit into the bank's insurance vault +//! Request a withdrawal +//! Claim withdrawal +mod create_account; +mod create_fund; +mod deposit; +mod withdraw_claim; +mod withdraw_request; + +pub use create_account::*; +pub use create_fund::*; +pub use deposit::*; +pub use withdraw_claim::*; +pub use withdraw_request::*; diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_claim.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_claim.rs new file mode 100644 index 000000000..c3f6b868f --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_claim.rs @@ -0,0 +1,130 @@ +use crate::{ + bank_signer, check, + constants::{INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUID_INSURANCE_USER_SEED}, + events::{LiquidInsuranceFundEventHeader, MarginfiWithdrawClaimLiquidInsuranceFundEvent}, + math_error, + state::{ + liquid_insurance_fund::{LiquidInsuranceFund, LiquidInsuranceFundAccount}, + marginfi_group::BankVaultType, + }, + MarginfiError, MarginfiResult, +}; +use anchor_lang::prelude::*; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; + +#[derive(Accounts)] +pub struct SettleWithdrawClaimInLiquidInsuranceFund<'info> { + #[account(mut)] + pub signer: Signer<'info>, + + #[account(mut)] + pub signer_token_account: InterfaceAccount<'info, TokenAccount>, + + /// The corresponding insurance vault that the liquid insurance fund deposits into. + /// This is the insurance vault of the underlying bank + #[account( + mut, + seeds = [ + INSURANCE_VAULT_SEED.as_bytes(), + liquid_insurance_fund.load()?.bank.key().as_ref(), + ], + bump = liquid_insurance_fund.load()?.lif_vault_bump, + )] + pub bank_insurance_vault: Box>, + + #[account( + seeds = [ + INSURANCE_VAULT_AUTHORITY_SEED.as_bytes(), + liquid_insurance_fund.load()?.bank.key().as_ref(), + ], + bump = liquid_insurance_fund.load()?.lif_authority_bump, + )] + pub bank_insurance_vault_authority: AccountInfo<'info>, + + #[account(mut)] + pub liquid_insurance_fund: AccountLoader<'info, LiquidInsuranceFund>, + + #[account( + mut, + seeds = [ + LIQUID_INSURANCE_USER_SEED.as_bytes(), + signer.key().as_ref(), + ], + bump, + )] + pub user_insurance_fund_account: AccountLoader<'info, LiquidInsuranceFundAccount>, + + pub token_program: Interface<'info, TokenInterface>, +} + +/// 1) Retrieve earliest withdraw request for this user, for this liquid insurance fund +/// 2) Check whether enough time has passed +/// 3) Process withdrawal +/// 4) Transfer tokens +pub fn settle_withdraw_claim_in_liquid_insurance_fund<'info>( + mut ctx: Context<'_, '_, 'info, 'info, SettleWithdrawClaimInLiquidInsuranceFund<'info>>, +) -> MarginfiResult { + let SettleWithdrawClaimInLiquidInsuranceFund { + signer_token_account, + bank_insurance_vault, + bank_insurance_vault_authority, + liquid_insurance_fund: liquid_insurance_fund_loader, + user_insurance_fund_account, + token_program, + signer: _, + } = ctx.accounts; + let mut liquid_insurance_fund = liquid_insurance_fund_loader.load_mut()?; + let maybe_bank_mint = crate::utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &liquid_insurance_fund.bank_mint, + token_program.key, + )?; + let clock = Clock::get()?; + + // 1) Retrieve earliest withdraw request for this user, for this liquid insurance fund + let mut user_account = user_insurance_fund_account.load_mut()?; + liquid_insurance_fund.update_share_price_internal(bank_insurance_vault.amount.into())?; + let withdrawal = user_account + .get_earliest_withdrawal(&liquid_insurance_fund_loader.key()) + .ok_or(MarginfiError::InvalidWithdrawal)?; + + // 2) Check whether enough time has passed + check!( + clock.unix_timestamp + >= withdrawal + .withdraw_request_timestamp + .checked_add(liquid_insurance_fund.min_withdraw_period) + .ok_or_else(math_error!())?, + // TODO: more informative error + MarginfiError::InvalidWithdrawal + ); + + // 3) Calculate share value in tokens + let user_withdraw_amount: u64 = liquid_insurance_fund.process_withdrawal(withdrawal)?; + + // Withdraw user funds from the relevant insurance vault + liquid_insurance_fund.withdraw_spl_transfer( + user_withdraw_amount, + bank_insurance_vault.to_account_info(), + signer_token_account.to_account_info(), + bank_insurance_vault_authority.to_account_info(), + token_program.to_account_info(), + maybe_bank_mint.as_ref(), + ctx.remaining_accounts, + bank_signer!( + BankVaultType::Insurance, + liquid_insurance_fund.bank, + liquid_insurance_fund.lif_authority_bump + ), + )?; + + emit!(MarginfiWithdrawClaimLiquidInsuranceFundEvent { + header: LiquidInsuranceFundEventHeader { + bank: liquid_insurance_fund.bank, + }, + amount: user_withdraw_amount, + success: true, + }); + + Ok(()) +} diff --git a/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_request.rs b/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_request.rs new file mode 100644 index 000000000..571e789e7 --- /dev/null +++ b/programs/marginfi/src/instructions/liquid_insurance_fund/withdraw_request.rs @@ -0,0 +1,65 @@ +use crate::{ + constants::LIQUID_INSURANCE_USER_SEED, + events::{LiquidInsuranceFundEventHeader, MarginfiWithdrawRequestLiquidInsuranceFundEvent}, + state::liquid_insurance_fund::{LiquidInsuranceFund, LiquidInsuranceFundAccount}, + MarginfiResult, +}; +use anchor_lang::prelude::*; +use fixed::types::I80F48; + +#[derive(Accounts)] +#[instruction( + signer_bump: u8, +)] +pub struct WithdrawRequestLiquidInsuranceFund<'info> { + #[account(mut)] + pub liquid_insurance_fund: AccountLoader<'info, LiquidInsuranceFund>, + + #[account(mut)] + pub signer: Signer<'info>, + + #[account( + mut, + seeds = [ + LIQUID_INSURANCE_USER_SEED.as_bytes(), + signer.key().as_ref(), + ], + bump + )] + pub user_insurance_fund_account: AccountLoader<'info, LiquidInsuranceFundAccount>, +} + +pub fn create_withdraw_request_from_liquid_token_fund( + ctx: Context, + shares: Option, +) -> MarginfiResult { + // Note: I80F48::ZERO is not positive -> returns early + if shares.map(|s| !s.is_positive()) == Some(true) { + return Ok(()); + } + + let WithdrawRequestLiquidInsuranceFund { + liquid_insurance_fund, + user_insurance_fund_account, + .. + } = ctx.accounts; + let clock = Clock::get()?; + + let mut user_insurance_fund_account = user_insurance_fund_account.load_mut()?; + let liquid_insurance_fund_account = liquid_insurance_fund.load()?; + + let shares = user_insurance_fund_account.create_withdrawal( + &liquid_insurance_fund.key(), + shares, + clock.unix_timestamp, + )?; + + emit!(MarginfiWithdrawRequestLiquidInsuranceFundEvent { + header: LiquidInsuranceFundEventHeader { + bank: liquid_insurance_fund_account.bank, + }, + shares: shares.to_num::(), + }); + + Ok(()) +} diff --git a/programs/marginfi/src/instructions/marginfi_account/borrow.rs b/programs/marginfi/src/instructions/marginfi_account/borrow.rs index 5f9b773ad..e6c4f875d 100644 --- a/programs/marginfi/src/instructions/marginfi_account/borrow.rs +++ b/programs/marginfi/src/instructions/marginfi_account/borrow.rs @@ -7,9 +7,10 @@ use crate::{ marginfi_account::{BankAccountWrapper, MarginfiAccount, RiskEngine, DISABLED_FLAG}, marginfi_group::{Bank, BankVaultType}, }, + utils, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; use fixed::types::I80F48; use solana_program::{clock::Clock, sysvar::Sysvar}; @@ -20,7 +21,10 @@ use solana_program::{clock::Clock, sysvar::Sysvar}; /// 5. Verify that the user account is in a healthy state /// /// Will error if there is an existing asset <=> withdrawing is not allowed. -pub fn lending_account_borrow(ctx: Context, amount: u64) -> MarginfiResult { +pub fn lending_account_borrow<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingAccountBorrow<'info>>, + amount: u64, +) -> MarginfiResult { let LendingAccountBorrow { marginfi_account: marginfi_account_loader, destination_token_account, @@ -30,6 +34,12 @@ pub fn lending_account_borrow(ctx: Context, amount: u64) - bank: bank_loader, .. } = ctx.accounts; + let clock = Clock::get()?; + let maybe_bank_mint = utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &bank_loader.load()?.mint, + token_program.key, + )?; let mut marginfi_account = marginfi_account_loader.load_mut()?; @@ -39,13 +49,14 @@ pub fn lending_account_borrow(ctx: Context, amount: u64) - ); bank_loader.load_mut()?.accrue_interest( - Clock::get()?.unix_timestamp, + clock.unix_timestamp, #[cfg(not(feature = "client"))] bank_loader.key(), )?; { let mut bank = bank_loader.load_mut()?; + let liquidity_vault_authority_bump = bank.liquidity_vault_authority_bump; let mut bank_account = BankAccountWrapper::find_or_create( @@ -54,20 +65,33 @@ pub fn lending_account_borrow(ctx: Context, amount: u64) - &mut marginfi_account.lending_account, )?; - bank_account.borrow(I80F48::from_num(amount))?; + // User needs to borrow amount + fee to receive amount + let amount_pre_fee = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_pre_fee_spl_deposit_amount( + mint.to_account_info(), + amount, + clock.epoch, + ) + }) + .transpose()? + .unwrap_or(amount); + + bank_account.borrow(I80F48::from_num(amount_pre_fee))?; bank_account.withdraw_spl_transfer( - amount, - Transfer { - from: bank_liquidity_vault.to_account_info(), - to: destination_token_account.to_account_info(), - authority: bank_liquidity_vault_authority.to_account_info(), - }, + amount_pre_fee, + bank_liquidity_vault.to_account_info(), + destination_token_account.to_account_info(), + bank_liquidity_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Liquidity, bank_loader.key(), liquidity_vault_authority_bump ), + ctx.remaining_accounts, )?; emit!(LendingAccountBorrowEvent { @@ -79,7 +103,7 @@ pub fn lending_account_borrow(ctx: Context, amount: u64) - }, bank: bank_loader.key(), mint: bank.mint, - amount, + amount: amount_pre_fee, }); } @@ -112,7 +136,7 @@ pub struct LendingAccountBorrow<'info> { pub bank: AccountLoader<'info, Bank>, #[account(mut)] - pub destination_token_account: Account<'info, TokenAccount>, + pub destination_token_account: InterfaceAccount<'info, TokenAccount>, /// CHECK: Seed constraint check #[account( @@ -133,7 +157,7 @@ pub struct LendingAccountBorrow<'info> { ], bump = bank.load() ?.liquidity_vault_bump, )] - pub bank_liquidity_vault: Account<'info, TokenAccount>, + pub bank_liquidity_vault: InterfaceAccount<'info, TokenAccount>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_account/close.rs b/programs/marginfi/src/instructions/marginfi_account/close.rs new file mode 100644 index 000000000..817ed0121 --- /dev/null +++ b/programs/marginfi/src/instructions/marginfi_account/close.rs @@ -0,0 +1,25 @@ +use anchor_lang::prelude::*; + +use crate::{check, state::marginfi_account::MarginfiAccount, MarginfiError, MarginfiResult}; + +pub fn close_account(ctx: Context) -> MarginfiResult { + let marginfi_account = &ctx.accounts.marginfi_account.load()?; + + check!( + marginfi_account.can_be_closed(), + MarginfiError::IllegalAction, + "Account cannot be closed" + ); + + Ok(()) +} + +#[derive(Accounts)] +pub struct MarginfiAccountClose<'info> { + #[account(mut, close = fee_payer)] + pub marginfi_account: AccountLoader<'info, MarginfiAccount>, + #[account(address = marginfi_account.load()?.authority)] + pub authority: Signer<'info>, + #[account(mut)] + pub fee_payer: Signer<'info>, +} diff --git a/programs/marginfi/src/instructions/marginfi_account/deposit.rs b/programs/marginfi/src/instructions/marginfi_account/deposit.rs index da720b325..13ccb5e99 100644 --- a/programs/marginfi/src/instructions/marginfi_account/deposit.rs +++ b/programs/marginfi/src/instructions/marginfi_account/deposit.rs @@ -7,9 +7,10 @@ use crate::{ marginfi_account::{BankAccountWrapper, MarginfiAccount, DISABLED_FLAG}, marginfi_group::Bank, }, + utils, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, Transfer}; +use anchor_spl::token_interface::TokenInterface; use fixed::types::I80F48; use solana_program::clock::Clock; use solana_program::sysvar::Sysvar; @@ -20,7 +21,10 @@ use solana_program::sysvar::Sysvar; /// 4. Transfer funds from the signer's token account to the bank's liquidity vault /// /// Will error if there is an existing liability <=> repaying is not allowed. -pub fn lending_account_deposit(ctx: Context, amount: u64) -> MarginfiResult { +pub fn lending_account_deposit<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingAccountDeposit<'info>>, + amount: u64, +) -> MarginfiResult { let LendingAccountDeposit { marginfi_account: marginfi_account_loader, signer, @@ -30,6 +34,12 @@ pub fn lending_account_deposit(ctx: Context, amount: u64) bank: bank_loader, .. } = ctx.accounts; + let clock = Clock::get()?; + let maybe_bank_mint = utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &bank_loader.load()?.mint, + token_program.key, + )?; let mut bank = bank_loader.load_mut()?; let mut marginfi_account = marginfi_account_loader.load_mut()?; @@ -40,7 +50,7 @@ pub fn lending_account_deposit(ctx: Context, amount: u64) ); bank.accrue_interest( - Clock::get()?.unix_timestamp, + clock.unix_timestamp, #[cfg(not(feature = "client"))] bank_loader.key(), )?; @@ -52,14 +62,23 @@ pub fn lending_account_deposit(ctx: Context, amount: u64) )?; bank_account.deposit(I80F48::from_num(amount))?; + + let amount_pre_fee = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_pre_fee_spl_deposit_amount(mint.to_account_info(), amount, clock.epoch) + }) + .transpose()? + .unwrap_or(amount); + bank_account.deposit_spl_transfer( - amount, - Transfer { - from: signer_token_account.to_account_info(), - to: bank_liquidity_vault.to_account_info(), - authority: signer.to_account_info(), - }, + amount_pre_fee, + signer_token_account.to_account_info(), + bank_liquidity_vault.to_account_info(), + signer.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), + ctx.remaining_accounts, )?; emit!(LendingAccountDepositEvent { @@ -113,5 +132,5 @@ pub struct LendingAccountDeposit<'info> { )] pub bank_liquidity_vault: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_account/emissions.rs b/programs/marginfi/src/instructions/marginfi_account/emissions.rs index b55cae58e..a83c071be 100644 --- a/programs/marginfi/src/instructions/marginfi_account/emissions.rs +++ b/programs/marginfi/src/instructions/marginfi_account/emissions.rs @@ -1,9 +1,12 @@ use anchor_lang::{prelude::*, Accounts, ToAccountInfo}; -use anchor_spl::token::{transfer, Mint, Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{ + transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked, +}; use crate::{ check, constants::{EMISSIONS_AUTH_SEED, EMISSIONS_TOKEN_ACCOUNT_SEED}, + debug, prelude::{MarginfiError, MarginfiResult}, state::{ marginfi_account::{BankAccountWrapper, MarginfiAccount, DISABLED_FLAG}, @@ -11,8 +14,8 @@ use crate::{ }, }; -pub fn lending_account_withdraw_emissions( - ctx: Context, +pub fn lending_account_withdraw_emissions<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountWithdrawEmissions<'info>>, ) -> MarginfiResult { let mut marginfi_account = ctx.accounts.marginfi_account.load_mut()?; @@ -33,24 +36,28 @@ pub fn lending_account_withdraw_emissions( let emissions_settle_amount = balance.settle_emissions_and_get_transfer_amount()?; if emissions_settle_amount > 0 { + debug!("Transferring {} emissions to user", emissions_settle_amount); + let signer_seeds: &[&[&[u8]]] = &[&[ EMISSIONS_AUTH_SEED.as_bytes(), &ctx.accounts.bank.key().to_bytes(), &ctx.accounts.emissions_mint.key().to_bytes(), - &[*ctx.bumps.get("emissions_auth").unwrap()], + &[ctx.bumps.emissions_auth], ]]; - transfer( + transfer_checked( CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), - Transfer { + TransferChecked { from: ctx.accounts.emissions_vault.to_account_info(), to: ctx.accounts.destination_account.to_account_info(), authority: ctx.accounts.emissions_auth.to_account_info(), + mint: ctx.accounts.emissions_mint.to_account_info(), }, signer_seeds, ), emissions_settle_amount, + ctx.accounts.emissions_mint.decimals, )?; } @@ -81,7 +88,7 @@ pub struct LendingAccountWithdrawEmissions<'info> { #[account( address = bank.load()?.emissions_mint )] - pub emissions_mint: Account<'info, Mint>, + pub emissions_mint: InterfaceAccount<'info, Mint>, #[account( seeds = [ @@ -103,11 +110,11 @@ pub struct LendingAccountWithdrawEmissions<'info> { ], bump, )] - pub emissions_vault: Box>, + pub emissions_vault: Box>, #[account(mut)] - pub destination_account: Box>, - pub token_program: Program<'info, Token>, + pub destination_account: Box>, + pub token_program: Interface<'info, TokenInterface>, } /// Permissionlessly settle unclaimed emissions to a users account. diff --git a/programs/marginfi/src/instructions/marginfi_account/flashloan.rs b/programs/marginfi/src/instructions/marginfi_account/flashloan.rs index 4dae3aeac..b5deb83ce 100644 --- a/programs/marginfi/src/instructions/marginfi_account/flashloan.rs +++ b/programs/marginfi/src/instructions/marginfi_account/flashloan.rs @@ -120,8 +120,8 @@ pub fn check_flashloan_can_start( Ok(()) } -pub fn lending_account_end_flashloan( - ctx: Context, +pub fn lending_account_end_flashloan<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountEndFlashloan<'info>>, ) -> MarginfiResult<()> { check!( get_stack_height() == TRANSACTION_LEVEL_STACK_HEIGHT, diff --git a/programs/marginfi/src/instructions/marginfi_account/liquidate.rs b/programs/marginfi/src/instructions/marginfi_account/liquidate.rs index 91362d8d0..36b7bd26e 100644 --- a/programs/marginfi/src/instructions/marginfi_account/liquidate.rs +++ b/programs/marginfi/src/instructions/marginfi_account/liquidate.rs @@ -10,9 +10,9 @@ use crate::{ constants::{LIQUIDITY_VAULT_AUTHORITY_SEED, LIQUIDITY_VAULT_SEED}, state::marginfi_account::{BankAccountWrapper, MarginfiAccount}, }; -use crate::{check, debug, prelude::*}; +use crate::{check, debug, prelude::*, utils}; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; use fixed::types::I80F48; use solana_program::clock::Clock; use solana_program::sysvar::Sysvar; @@ -65,8 +65,18 @@ use solana_program::sysvar::Sysvar; /// assuming that the liquidatee liability token balance doesn't become positive (doesn't become counted as collateral), /// and that the liquidatee collateral token balance doesn't become negative (doesn't become counted as liability). /// -pub fn lending_account_liquidate( - ctx: Context, +/// +/// Expected remaining account schema +/// [ +/// liab_mint_ai (if token2022 mint), +/// asset_oracle_ai, +/// liab_oracle_ai, +/// liquidator_observation_ais..., +/// liquidatee_observation_ais..., +/// ] + +pub fn lending_account_liquidate<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingAccountLiquidate<'info>>, asset_amount: u64, ) -> MarginfiResult { check!( @@ -89,8 +99,14 @@ pub fn lending_account_liquidate( let mut liquidator_marginfi_account = liquidator_marginfi_account_loader.load_mut()?; let mut liquidatee_marginfi_account = liquidatee_marginfi_account_loader.load_mut()?; - let current_timestamp = Clock::get()?.unix_timestamp; + let clock = Clock::get()?; + let current_timestamp = clock.unix_timestamp; + let maybe_liab_bank_mint = utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &ctx.accounts.liab_bank.load()?.mint, + ctx.accounts.token_program.key, + )?; { ctx.accounts.asset_bank.load_mut()?.accrue_interest( current_timestamp, @@ -103,10 +119,10 @@ pub fn lending_account_liquidate( ctx.accounts.liab_bank.key(), )?; } - + let init_liquidatee_remaining_len = liquidatee_marginfi_account.get_remaining_accounts_len(); let pre_liquidation_health = { let liquidatee_accounts_starting_pos = - ctx.remaining_accounts.len() - liquidatee_marginfi_account.get_remaining_accounts_len(); + ctx.remaining_accounts.len() - init_liquidatee_remaining_len; let liquidatee_remaining_accounts = &ctx.remaining_accounts[liquidatee_accounts_starting_pos..]; @@ -125,7 +141,7 @@ pub fn lending_account_liquidate( let asset_pf = OraclePriceFeedAdapter::try_from_bank_config( &asset_bank.config, oracle_ais, - current_timestamp, + &clock, )?; asset_pf.get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low))? }; @@ -136,7 +152,7 @@ pub fn lending_account_liquidate( let liab_pf = OraclePriceFeedAdapter::try_from_bank_config( &liab_bank.config, oracle_ais, - current_timestamp, + &clock, )?; liab_pf.get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::High))? }; @@ -279,20 +295,19 @@ pub fn lending_account_liquidate( // Insurance fund receives fee liquidatee_liab_bank_account.withdraw_spl_transfer( insurance_fee_to_transfer, - Transfer { - from: ctx.accounts.bank_liquidity_vault.to_account_info(), - to: ctx.accounts.bank_insurance_vault.to_account_info(), - authority: ctx - .accounts - .bank_liquidity_vault_authority - .to_account_info(), - }, + ctx.accounts.bank_liquidity_vault.to_account_info(), + ctx.accounts.bank_insurance_vault.to_account_info(), + ctx.accounts + .bank_liquidity_vault_authority + .to_account_info(), + maybe_liab_bank_mint.as_ref(), ctx.accounts.token_program.to_account_info(), bank_signer!( BankVaultType::Liquidity, ctx.accounts.liab_bank.key(), liab_bank_liquidity_authority_bump ), + ctx.remaining_accounts, )?; ( @@ -325,9 +340,14 @@ pub fn lending_account_liquidate( // ## Risk checks ## - let (liquidator_remaining_accounts, liquidatee_remaining_accounts) = ctx.remaining_accounts - [2..] - .split_at(liquidator_marginfi_account.get_remaining_accounts_len()); + let liquidatee_accounts_starting_pos = + ctx.remaining_accounts.len() - init_liquidatee_remaining_len; + let liquidator_accounts_starting_pos = + liquidatee_accounts_starting_pos - liquidator_marginfi_account.get_remaining_accounts_len(); + + let liquidatee_remaining_accounts = &ctx.remaining_accounts[liquidatee_accounts_starting_pos..]; + let liquidator_remaining_accounts = + &ctx.remaining_accounts[liquidator_accounts_starting_pos..liquidatee_accounts_starting_pos]; // Verify liquidatee liquidation post health let post_liquidation_health = @@ -418,7 +438,7 @@ pub struct LendingAccountLiquidate<'info> { ], bump = liab_bank.load()?.liquidity_vault_bump )] - pub bank_liquidity_vault: Box>, + pub bank_liquidity_vault: Box>, /// CHECK: Seed constraint #[account( @@ -431,5 +451,5 @@ pub struct LendingAccountLiquidate<'info> { )] pub bank_insurance_vault: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_account/mod.rs b/programs/marginfi/src/instructions/marginfi_account/mod.rs index 35d1eef3b..41b669784 100644 --- a/programs/marginfi/src/instructions/marginfi_account/mod.rs +++ b/programs/marginfi/src/instructions/marginfi_account/mod.rs @@ -1,4 +1,5 @@ mod borrow; +mod close; mod close_balance; mod deposit; mod emissions; @@ -10,6 +11,7 @@ mod transfer_authority; mod withdraw; pub use borrow::*; +pub use close::*; pub use close_balance::*; pub use deposit::*; pub use emissions::*; diff --git a/programs/marginfi/src/instructions/marginfi_account/repay.rs b/programs/marginfi/src/instructions/marginfi_account/repay.rs index 3d3b9e6fa..65f794410 100644 --- a/programs/marginfi/src/instructions/marginfi_account/repay.rs +++ b/programs/marginfi/src/instructions/marginfi_account/repay.rs @@ -7,9 +7,10 @@ use crate::{ marginfi_account::{BankAccountWrapper, MarginfiAccount, DISABLED_FLAG}, marginfi_group::Bank, }, + utils, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, Transfer}; +use anchor_spl::token_interface::TokenInterface; use fixed::types::I80F48; use solana_program::{clock::Clock, sysvar::Sysvar}; @@ -19,8 +20,8 @@ use solana_program::{clock::Clock, sysvar::Sysvar}; /// 4. Transfer funds from the signer's token account to the bank's liquidity vault /// /// Will error if there is no existing liability <=> depositing is not allowed. -pub fn lending_account_repay( - ctx: Context, +pub fn lending_account_repay<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingAccountRepay<'info>>, amount: u64, repay_all: Option, ) -> MarginfiResult { @@ -33,6 +34,12 @@ pub fn lending_account_repay( bank: bank_loader, .. } = ctx.accounts; + let clock = Clock::get()?; + let maybe_bank_mint = utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &bank_loader.load()?.mint, + token_program.key, + )?; let repay_all = repay_all.unwrap_or(false); let mut bank = bank_loader.load_mut()?; @@ -44,7 +51,7 @@ pub fn lending_account_repay( ); bank.accrue_interest( - Clock::get()?.unix_timestamp, + clock.unix_timestamp, #[cfg(not(feature = "client"))] bank_loader.key(), )?; @@ -55,7 +62,7 @@ pub fn lending_account_repay( &mut marginfi_account.lending_account, )?; - let spl_deposit_amount = if repay_all { + let repay_amount_post_fee = if repay_all { bank_account.repay_all()? } else { bank_account.repay(I80F48::from_num(amount))?; @@ -63,14 +70,26 @@ pub fn lending_account_repay( amount }; + let repay_amount_pre_fee = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_pre_fee_spl_deposit_amount( + mint.to_account_info(), + repay_amount_post_fee, + clock.epoch, + ) + }) + .transpose()? + .unwrap_or(repay_amount_post_fee); + bank_account.deposit_spl_transfer( - spl_deposit_amount, - Transfer { - from: signer_token_account.to_account_info(), - to: bank_liquidity_vault.to_account_info(), - authority: signer.to_account_info(), - }, + repay_amount_pre_fee, + signer_token_account.to_account_info(), + bank_liquidity_vault.to_account_info(), + signer.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), + ctx.remaining_accounts, )?; emit!(LendingAccountRepayEvent { @@ -82,7 +101,7 @@ pub fn lending_account_repay( }, bank: bank_loader.key(), mint: bank.mint, - amount: spl_deposit_amount, + amount: repay_amount_post_fee, close_balance: repay_all, }); @@ -125,5 +144,5 @@ pub struct LendingAccountRepay<'info> { )] pub bank_liquidity_vault: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_account/withdraw.rs b/programs/marginfi/src/instructions/marginfi_account/withdraw.rs index 08bbdb68e..077fae613 100644 --- a/programs/marginfi/src/instructions/marginfi_account/withdraw.rs +++ b/programs/marginfi/src/instructions/marginfi_account/withdraw.rs @@ -7,9 +7,10 @@ use crate::{ marginfi_account::{BankAccountWrapper, MarginfiAccount, RiskEngine, DISABLED_FLAG}, marginfi_group::{Bank, BankVaultType}, }, + utils, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; use fixed::types::I80F48; use solana_program::{clock::Clock, sysvar::Sysvar}; @@ -20,8 +21,8 @@ use solana_program::{clock::Clock, sysvar::Sysvar}; /// 5. Verify that the user account is in a healthy state /// /// Will error if there is no existing asset <=> borrowing is not allowed. -pub fn lending_account_withdraw( - ctx: Context, +pub fn lending_account_withdraw<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingAccountWithdraw<'info>>, amount: u64, withdraw_all: Option, ) -> MarginfiResult { @@ -34,6 +35,7 @@ pub fn lending_account_withdraw( bank: bank_loader, .. } = ctx.accounts; + let clock = Clock::get()?; let withdraw_all = withdraw_all.unwrap_or(false); let mut marginfi_account = marginfi_account_loader.load_mut()?; @@ -43,14 +45,21 @@ pub fn lending_account_withdraw( MarginfiError::AccountDisabled ); + let maybe_bank_mint = utils::maybe_take_bank_mint( + &mut ctx.remaining_accounts, + &bank_loader.load()?.mint, + token_program.key, + )?; + bank_loader.load_mut()?.accrue_interest( - Clock::get()?.unix_timestamp, + clock.unix_timestamp, #[cfg(not(feature = "client"))] bank_loader.key(), )?; { let mut bank = bank_loader.load_mut()?; + let liquidity_vault_authority_bump = bank.liquidity_vault_authority_bump; let mut bank_account = BankAccountWrapper::find( @@ -59,27 +68,39 @@ pub fn lending_account_withdraw( &mut marginfi_account.lending_account, )?; - let spl_withdraw_amount = if withdraw_all { + let amount_pre_fee = if withdraw_all { bank_account.withdraw_all()? } else { - bank_account.withdraw(I80F48::from_num(amount))?; - - amount + let amount_pre_fee = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_pre_fee_spl_deposit_amount( + mint.to_account_info(), + amount, + clock.epoch, + ) + }) + .transpose()? + .unwrap_or(amount); + + bank_account.withdraw(I80F48::from_num(amount_pre_fee))?; + + amount_pre_fee }; bank_account.withdraw_spl_transfer( - spl_withdraw_amount, - Transfer { - from: bank_liquidity_vault.to_account_info(), - to: destination_token_account.to_account_info(), - authority: bank_liquidity_vault_authority.to_account_info(), - }, + amount_pre_fee, + bank_liquidity_vault.to_account_info(), + destination_token_account.to_account_info(), + bank_liquidity_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Liquidity, bank_loader.key(), liquidity_vault_authority_bump ), + ctx.remaining_accounts, )?; emit!(LendingAccountWithdrawEvent { @@ -91,7 +112,7 @@ pub fn lending_account_withdraw( }, bank: bank_loader.key(), mint: bank.mint, - amount: spl_withdraw_amount, + amount: amount_pre_fee, close_balance: withdraw_all, }); } @@ -125,7 +146,7 @@ pub struct LendingAccountWithdraw<'info> { pub bank: AccountLoader<'info, Bank>, #[account(mut)] - pub destination_token_account: Account<'info, TokenAccount>, + pub destination_token_account: InterfaceAccount<'info, TokenAccount>, /// CHECK: Seed constraint check #[account( @@ -146,7 +167,7 @@ pub struct LendingAccountWithdraw<'info> { ], bump = bank.load()?.liquidity_vault_bump, )] - pub bank_liquidity_vault: Account<'info, TokenAccount>, + pub bank_liquidity_vault: InterfaceAccount<'info, TokenAccount>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_group/add_pool.rs b/programs/marginfi/src/instructions/marginfi_group/add_pool.rs index e3f82e7a3..35895a1f2 100644 --- a/programs/marginfi/src/instructions/marginfi_group/add_pool.rs +++ b/programs/marginfi/src/instructions/marginfi_group/add_pool.rs @@ -8,7 +8,7 @@ use crate::{ MarginfiResult, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Mint, Token, TokenAccount}; +use anchor_spl::token_interface::*; /// Add a bank to the lending pool /// @@ -30,12 +30,12 @@ pub fn lending_pool_add_bank( let mut bank = bank_loader.load_init()?; - let liquidity_vault_bump = *ctx.bumps.get("liquidity_vault").unwrap(); - let liquidity_vault_authority_bump = *ctx.bumps.get("liquidity_vault_authority").unwrap(); - let insurance_vault_bump = *ctx.bumps.get("insurance_vault").unwrap(); - let insurance_vault_authority_bump = *ctx.bumps.get("insurance_vault_authority").unwrap(); - let fee_vault_bump = *ctx.bumps.get("fee_vault").unwrap(); - let fee_vault_authority_bump = *ctx.bumps.get("fee_vault_authority").unwrap(); + let liquidity_vault_bump = ctx.bumps.liquidity_vault; + let liquidity_vault_authority_bump = ctx.bumps.liquidity_vault_authority; + let insurance_vault_bump = ctx.bumps.insurance_vault; + let insurance_vault_authority_bump = ctx.bumps.insurance_vault_authority; + let fee_vault_bump = ctx.bumps.fee_vault; + let fee_vault_authority_bump = ctx.bumps.fee_vault_authority; *bank = Bank::new( ctx.accounts.marginfi_group.key(), @@ -83,7 +83,7 @@ pub struct LendingPoolAddBank<'info> { #[account(mut)] pub fee_payer: Signer<'info>, - pub bank_mint: Box>, + pub bank_mint: Box>, #[account( init, @@ -113,7 +113,7 @@ pub struct LendingPoolAddBank<'info> { ], bump, )] - pub liquidity_vault: Box>, + pub liquidity_vault: Box>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -136,7 +136,7 @@ pub struct LendingPoolAddBank<'info> { ], bump, )] - pub insurance_vault: Box>, + pub insurance_vault: Box>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -159,10 +159,10 @@ pub struct LendingPoolAddBank<'info> { ], bump, )] - pub fee_vault: Box>, + pub fee_vault: Box>, pub rent: Sysvar<'info, Rent>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } @@ -186,12 +186,12 @@ pub fn lending_pool_add_bank_with_seed( let mut bank = bank_loader.load_init()?; - let liquidity_vault_bump = *ctx.bumps.get("liquidity_vault").unwrap(); - let liquidity_vault_authority_bump = *ctx.bumps.get("liquidity_vault_authority").unwrap(); - let insurance_vault_bump = *ctx.bumps.get("insurance_vault").unwrap(); - let insurance_vault_authority_bump = *ctx.bumps.get("insurance_vault_authority").unwrap(); - let fee_vault_bump = *ctx.bumps.get("fee_vault").unwrap(); - let fee_vault_authority_bump = *ctx.bumps.get("fee_vault_authority").unwrap(); + let liquidity_vault_bump = ctx.bumps.liquidity_vault; + let liquidity_vault_authority_bump = ctx.bumps.liquidity_vault_authority; + let insurance_vault_bump = ctx.bumps.insurance_vault; + let insurance_vault_authority_bump = ctx.bumps.insurance_vault_authority; + let fee_vault_bump = ctx.bumps.fee_vault; + let fee_vault_authority_bump = ctx.bumps.fee_vault_authority; *bank = Bank::new( ctx.accounts.marginfi_group.key(), @@ -243,7 +243,7 @@ pub struct LendingPoolAddBankWithSeed<'info> { #[account(mut)] pub fee_payer: Signer<'info>, - pub bank_mint: Box>, + pub bank_mint: Box>, #[account( init, @@ -279,7 +279,7 @@ pub struct LendingPoolAddBankWithSeed<'info> { ], bump, )] - pub liquidity_vault: Box>, + pub liquidity_vault: Box>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -302,7 +302,7 @@ pub struct LendingPoolAddBankWithSeed<'info> { ], bump, )] - pub insurance_vault: Box>, + pub insurance_vault: Box>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -325,9 +325,9 @@ pub struct LendingPoolAddBankWithSeed<'info> { ], bump, )] - pub fee_vault: Box>, + pub fee_vault: Box>, pub rent: Sysvar<'info, Rent>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } diff --git a/programs/marginfi/src/instructions/marginfi_group/collect_bank_fees.rs b/programs/marginfi/src/instructions/marginfi_group/collect_bank_fees.rs index f99876539..6b3cae705 100644 --- a/programs/marginfi/src/instructions/marginfi_group/collect_bank_fees.rs +++ b/programs/marginfi/src/instructions/marginfi_group/collect_bank_fees.rs @@ -1,5 +1,9 @@ -use crate::constants::{FEE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_AUTHORITY_SEED}; +use crate::constants::{ + FEE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_AUTHORITY_SEED, LIQUID_INSURANCE_SEED, +}; use crate::events::{GroupEventHeader, LendingPoolBankCollectFeesEvent}; +use crate::state::liquid_insurance_fund::LiquidInsuranceFund; +use crate::utils; use crate::{ bank_signer, constants::{ @@ -10,11 +14,13 @@ use crate::{ MarginfiResult, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; use fixed::types::I80F48; use std::cmp::min; -pub fn lending_pool_collect_bank_fees(ctx: Context) -> MarginfiResult { +pub fn lending_pool_collect_bank_fees<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingPoolCollectBankFees<'info>>, +) -> MarginfiResult { let LendingPoolCollectBankFees { liquidity_vault_authority, insurance_vault, @@ -25,6 +31,8 @@ pub fn lending_pool_collect_bank_fees(ctx: Context) } = ctx.accounts; let mut bank = ctx.accounts.bank.load_mut()?; + let maybe_bank_mint = + utils::maybe_take_bank_mint(&mut ctx.remaining_accounts, &bank.mint, token_program.key)?; let mut available_liquidity = I80F48::from_num(liquidity_vault.amount); @@ -70,34 +78,34 @@ pub fn lending_pool_collect_bank_fees(ctx: Context) group_fee_transfer_amount .checked_to_num() .ok_or_else(math_error!())?, - Transfer { - from: liquidity_vault.to_account_info(), - to: fee_vault.to_account_info(), - authority: liquidity_vault_authority.to_account_info(), - }, + liquidity_vault.to_account_info(), + fee_vault.to_account_info(), + liquidity_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Liquidity, ctx.accounts.bank.key(), bank.liquidity_vault_authority_bump ), + ctx.remaining_accounts, )?; bank.withdraw_spl_transfer( insurance_fee_transfer_amount .checked_to_num() .ok_or_else(math_error!())?, - Transfer { - from: liquidity_vault.to_account_info(), - to: insurance_vault.to_account_info(), - authority: liquidity_vault_authority.to_account_info(), - }, + liquidity_vault.to_account_info(), + insurance_vault.to_account_info(), + liquidity_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Liquidity, ctx.accounts.bank.key(), bank.liquidity_vault_authority_bump ), + ctx.remaining_accounts, )?; emit!(LendingPoolBankCollectFeesEvent { @@ -145,7 +153,7 @@ pub struct LendingPoolCollectBankFees<'info> { ], bump = bank.load()?.liquidity_vault_bump )] - pub liquidity_vault: Account<'info, TokenAccount>, + pub liquidity_vault: InterfaceAccount<'info, TokenAccount>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -169,11 +177,11 @@ pub struct LendingPoolCollectBankFees<'info> { )] pub fee_vault: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } -pub fn lending_pool_withdraw_fees( - ctx: Context, +pub fn lending_pool_withdraw_fees<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingPoolWithdrawFees<'info>>, amount: u64, ) -> MarginfiResult { let LendingPoolWithdrawFees { @@ -186,20 +194,22 @@ pub fn lending_pool_withdraw_fees( } = ctx.accounts; let bank = bank_loader.load()?; + let maybe_bank_mint = + utils::maybe_take_bank_mint(&mut ctx.remaining_accounts, &bank.mint, token_program.key)?; bank.withdraw_spl_transfer( amount, - Transfer { - from: fee_vault.to_account_info(), - to: dst_token_account.to_account_info(), - authority: fee_vault_authority.to_account_info(), - }, + fee_vault.to_account_info(), + dst_token_account.to_account_info(), + fee_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Fee, bank_loader.key(), bank.fee_vault_authority_bump ), + ctx.remaining_accounts, )?; Ok(()) @@ -244,44 +254,105 @@ pub struct LendingPoolWithdrawFees<'info> { #[account(mut)] pub dst_token_account: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } -pub fn lending_pool_withdraw_insurance( - ctx: Context, - amount: u64, +pub fn lending_pool_withdraw_insurance<'a, 'b: 'info, 'info>( + mut ctx: Context<'a, 'b, 'info, 'info, LendingPoolAdminDepositWithdrawInsurance<'info>>, + amount: I80F48, ) -> MarginfiResult { - let LendingPoolWithdrawInsurance { + let LendingPoolAdminDepositWithdrawInsurance { bank: bank_loader, insurance_vault, insurance_vault_authority, - dst_token_account, + admin_token_account, token_program, + liquid_insurance_fund, .. } = ctx.accounts; let bank = bank_loader.load()?; + let maybe_bank_mint = + utils::maybe_take_bank_mint(&mut ctx.remaining_accounts, &bank.mint, token_program.key)?; - bank.withdraw_spl_transfer( + // If there exist a liquid insurance fund, admin should be limited to admin shares + let tokens = LiquidInsuranceFund::maybe_process_admin_withdraw( + liquid_insurance_fund, + insurance_vault.amount, amount, - Transfer { - from: insurance_vault.to_account_info(), - to: dst_token_account.to_account_info(), - authority: insurance_vault_authority.to_account_info(), - }, + )?; + + bank.withdraw_spl_transfer( + tokens, + insurance_vault.to_account_info(), + admin_token_account.to_account_info(), + insurance_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Insurance, bank_loader.key(), bank.insurance_vault_authority_bump ), + ctx.remaining_accounts, + )?; + + Ok(()) +} + +pub fn lending_pool_deposit_insurance<'a, 'info>( + mut ctx: Context<'a, 'info, 'info, 'info, LendingPoolAdminDepositWithdrawInsurance<'info>>, + amount: u64, +) -> MarginfiResult { + let LendingPoolAdminDepositWithdrawInsurance { + bank: bank_loader, + insurance_vault, + admin_token_account, + token_program, + liquid_insurance_fund, + admin, + .. + } = ctx.accounts; + + let bank = bank_loader.load()?; + let maybe_bank_mint = + utils::maybe_take_bank_mint(&mut ctx.remaining_accounts, &bank.mint, token_program.key)?; + + // Calculate post-fee amount + let post_fee_amount = maybe_bank_mint + .as_ref() + .map(|mint_ai| { + let clock = Clock::get().unwrap(); + utils::calculate_post_fee_spl_deposit_amount( + mint_ai.to_account_info(), + amount, + clock.epoch, + ) + }) + .unwrap_or(Ok(amount))?; + + // If there exist a liquid insurance fund, need to update shares + LiquidInsuranceFund::maybe_process_admin_deposit( + liquid_insurance_fund, + insurance_vault.amount, + post_fee_amount, + )?; + + bank.deposit_insurance_spl_transfer( + amount, + admin_token_account.to_account_info(), + insurance_vault.to_account_info(), + admin.to_account_info(), + maybe_bank_mint.as_ref(), + token_program.to_account_info(), + ctx.remaining_accounts, )?; Ok(()) } #[derive(Accounts)] -pub struct LendingPoolWithdrawInsurance<'info> { +pub struct LendingPoolAdminDepositWithdrawInsurance<'info> { pub marginfi_group: AccountLoader<'info, MarginfiGroup>, #[account( @@ -303,7 +374,7 @@ pub struct LendingPoolWithdrawInsurance<'info> { ], bump = bank.load()?.insurance_vault_bump )] - pub insurance_vault: AccountInfo<'info>, + pub insurance_vault: InterfaceAccount<'info, TokenAccount>, /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account( @@ -317,7 +388,17 @@ pub struct LendingPoolWithdrawInsurance<'info> { /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ #[account(mut)] - pub dst_token_account: AccountInfo<'info>, + pub admin_token_account: AccountInfo<'info>, + + pub token_program: Interface<'info, TokenInterface>, - pub token_program: Program<'info, Token>, + #[account( + mut, + seeds = [ + LIQUID_INSURANCE_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump, + )] + pub liquid_insurance_fund: AccountInfo<'info>, } diff --git a/programs/marginfi/src/instructions/marginfi_group/configure_bank.rs b/programs/marginfi/src/instructions/marginfi_group/configure_bank.rs index 097e58999..92a834409 100644 --- a/programs/marginfi/src/instructions/marginfi_group/configure_bank.rs +++ b/programs/marginfi/src/instructions/marginfi_group/configure_bank.rs @@ -1,13 +1,14 @@ use crate::constants::{EMISSIONS_AUTH_SEED, EMISSIONS_TOKEN_ACCOUNT_SEED}; use crate::events::{GroupEventHeader, LendingPoolBankConfigureEvent}; use crate::prelude::MarginfiError; -use crate::{check, math_error}; +use crate::{check, math_error, utils}; use crate::{ state::marginfi_group::{Bank, BankConfigOpt, MarginfiGroup}, MarginfiResult, }; use anchor_lang::prelude::*; -use anchor_spl::token::{transfer, Mint, Token, TokenAccount, Transfer}; +use anchor_spl::token_2022::{transfer_checked, TransferChecked}; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use fixed::types::I80F48; pub fn lending_pool_configure_bank( @@ -71,16 +72,24 @@ pub fn lending_pool_setup_emissions( bank.emissions_rate = emissions_rate; bank.emissions_remaining = I80F48::from_num(total_emissions).into(); - transfer( + let initial_emissions_amount_pre_fee = utils::calculate_pre_fee_spl_deposit_amount( + ctx.accounts.emissions_mint.to_account_info(), + total_emissions, + Clock::get()?.epoch, + )?; + + transfer_checked( CpiContext::new( ctx.accounts.token_program.to_account_info(), - Transfer { + TransferChecked { from: ctx.accounts.emissions_funding_account.to_account_info(), to: ctx.accounts.emissions_token_account.to_account_info(), authority: ctx.accounts.admin.to_account_info(), + mint: ctx.accounts.emissions_mint.to_account_info(), }, ), - total_emissions, + initial_emissions_amount_pre_fee, + ctx.accounts.emissions_mint.decimals, )?; Ok(()) @@ -102,7 +111,7 @@ pub struct LendingPoolSetupEmissions<'info> { )] pub bank: AccountLoader<'info, Bank>, - pub emissions_mint: Account<'info, Mint>, + pub emissions_mint: InterfaceAccount<'info, Mint>, #[account( seeds = [ @@ -127,13 +136,13 @@ pub struct LendingPoolSetupEmissions<'info> { ], bump, )] - pub emissions_token_account: Box>, + pub emissions_token_account: Box>, /// CHECK: Account provided only for funding rewards #[account(mut)] pub emissions_funding_account: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } @@ -166,18 +175,6 @@ pub fn lending_pool_update_emissions_parameters( } if let Some(additional_emissions) = additional_emissions { - transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.emissions_funding_account.to_account_info(), - to: ctx.accounts.emissions_token_account.to_account_info(), - authority: ctx.accounts.admin.to_account_info(), - }, - ), - additional_emissions, - )?; - bank.emissions_remaining = I80F48::from(bank.emissions_remaining) .checked_add(I80F48::from_num(additional_emissions)) .ok_or_else(math_error!())? @@ -188,6 +185,26 @@ pub fn lending_pool_update_emissions_parameters( additional_emissions, I80F48::from(bank.emissions_remaining) ); + + let additional_emissions_amount_pre_fee = utils::calculate_pre_fee_spl_deposit_amount( + ctx.accounts.emissions_mint.to_account_info(), + additional_emissions, + Clock::get()?.epoch, + )?; + + transfer_checked( + CpiContext::new( + ctx.accounts.token_program.to_account_info(), + TransferChecked { + from: ctx.accounts.emissions_funding_account.to_account_info(), + to: ctx.accounts.emissions_token_account.to_account_info(), + authority: ctx.accounts.admin.to_account_info(), + mint: ctx.accounts.emissions_mint.to_account_info(), + }, + ), + additional_emissions_amount_pre_fee, + ctx.accounts.emissions_mint.decimals, + )?; } Ok(()) @@ -209,7 +226,7 @@ pub struct LendingPoolUpdateEmissionsParameters<'info> { )] pub bank: AccountLoader<'info, Bank>, - pub emissions_mint: Account<'info, Mint>, + pub emissions_mint: InterfaceAccount<'info, Mint>, #[account( mut, @@ -220,11 +237,11 @@ pub struct LendingPoolUpdateEmissionsParameters<'info> { ], bump, )] - pub emissions_token_account: Box>, + pub emissions_token_account: Box>, /// CHECK: Account provided only for funding rewards #[account(mut)] pub emissions_funding_account: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/marginfi_group/handle_bankruptcy.rs b/programs/marginfi/src/instructions/marginfi_group/handle_bankruptcy.rs index eaba6476f..7e1ee9f40 100644 --- a/programs/marginfi/src/instructions/marginfi_group/handle_bankruptcy.rs +++ b/programs/marginfi/src/instructions/marginfi_group/handle_bankruptcy.rs @@ -1,19 +1,21 @@ -use crate::constants::{PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG, ZERO_AMOUNT_THRESHOLD}; -use crate::events::{AccountEventHeader, LendingPoolBankHandleBankruptcyEvent}; -use crate::state::marginfi_account::DISABLED_FLAG; use crate::{ bank_signer, check, - constants::{INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_SEED}, + constants::{ + INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_SEED, + PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG, ZERO_AMOUNT_THRESHOLD, + }, + debug, + events::{AccountEventHeader, LendingPoolBankHandleBankruptcyEvent}, math_error, prelude::MarginfiError, state::{ - marginfi_account::{BankAccountWrapper, MarginfiAccount, RiskEngine}, + marginfi_account::{BankAccountWrapper, MarginfiAccount, RiskEngine, DISABLED_FLAG}, marginfi_group::{Bank, BankVaultType, MarginfiGroup}, }, - MarginfiResult, + utils, MarginfiResult, }; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; +use anchor_spl::token_interface::{TokenAccount, TokenInterface}; use fixed::types::I80F48; use std::cmp::{max, min}; @@ -23,7 +25,9 @@ use std::cmp::{max, min}; /// 3. Cover the bad debt of the bankrupt account. /// 4. Transfer the insured amount from the insurance fund. /// 5. Socialize the loss between lenders if any. -pub fn lending_pool_handle_bankruptcy(ctx: Context) -> MarginfiResult { +pub fn lending_pool_handle_bankruptcy<'info>( + mut ctx: Context<'_, '_, 'info, 'info, LendingPoolHandleBankruptcy<'info>>, +) -> MarginfiResult { let LendingPoolHandleBankruptcy { marginfi_account: marginfi_account_loader, insurance_vault, @@ -33,6 +37,10 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context) .. } = ctx.accounts; let bank = bank_loader.load()?; + let maybe_bank_mint = + utils::maybe_take_bank_mint(&mut ctx.remaining_accounts, &bank.mint, token_program.key)?; + + let clock = Clock::get()?; if !bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG) { check!( @@ -50,7 +58,7 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context) let mut bank = bank_loader.load_mut()?; bank.accrue_interest( - Clock::get()?.unix_timestamp, + clock.unix_timestamp, #[cfg(not(feature = "client"))] bank_loader.key(), )?; @@ -76,30 +84,61 @@ pub fn lending_pool_handle_bankruptcy(ctx: Context) ); let (covered_by_insurance, socialized_loss) = { - let available_insurance_funds = I80F48::from_num(insurance_vault.amount); - - let covered_by_insurance = min(bad_debt, available_insurance_funds); + let available_insurance_fund: I80F48 = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_post_fee_spl_deposit_amount( + mint.to_account_info(), + insurance_vault.amount, + clock.epoch, + ) + }) + .transpose()? + .unwrap_or(insurance_vault.amount) + .into(); + + let covered_by_insurance = min(bad_debt, available_insurance_fund); let socialized_loss = max(bad_debt - covered_by_insurance, I80F48::ZERO); (covered_by_insurance, socialized_loss) }; // Cover bad debt with insurance funds. + let covered_by_insurance_rounded_up: u64 = covered_by_insurance + .checked_ceil() + .ok_or_else(math_error!())? + .checked_to_num() + .ok_or_else(math_error!())?; + debug!( + "covered_by_insurance_rounded_up: {}; socialized loss {}", + covered_by_insurance_rounded_up, socialized_loss + ); + + let insurance_coverage_deposit_pre_fee = maybe_bank_mint + .as_ref() + .map(|mint| { + utils::calculate_pre_fee_spl_deposit_amount( + mint.to_account_info(), + covered_by_insurance_rounded_up, + clock.epoch, + ) + }) + .transpose()? + .unwrap_or(covered_by_insurance_rounded_up); + bank.withdraw_spl_transfer( - covered_by_insurance - .checked_to_num() - .ok_or_else(math_error!())?, - Transfer { - from: ctx.accounts.insurance_vault.to_account_info(), - to: ctx.accounts.liquidity_vault.to_account_info(), - authority: ctx.accounts.insurance_vault_authority.to_account_info(), - }, + insurance_coverage_deposit_pre_fee, + ctx.accounts.insurance_vault.to_account_info(), + ctx.accounts.liquidity_vault.to_account_info(), + ctx.accounts.insurance_vault_authority.to_account_info(), + maybe_bank_mint.as_ref(), token_program.to_account_info(), bank_signer!( BankVaultType::Insurance, bank_loader.key(), bank.insurance_vault_authority_bump ), + ctx.remaining_accounts, )?; // Socialize bad debt among depositors. @@ -171,7 +210,7 @@ pub struct LendingPoolHandleBankruptcy<'info> { ], bump = bank.load()?.insurance_vault_bump )] - pub insurance_vault: Box>, + pub insurance_vault: Box>, /// CHECK: Seed constraint #[account( @@ -183,5 +222,5 @@ pub struct LendingPoolHandleBankruptcy<'info> { )] pub insurance_vault_authority: AccountInfo<'info>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, } diff --git a/programs/marginfi/src/instructions/mod.rs b/programs/marginfi/src/instructions/mod.rs index 1b5a6cdfb..306f39df1 100644 --- a/programs/marginfi/src/instructions/mod.rs +++ b/programs/marginfi/src/instructions/mod.rs @@ -1,5 +1,7 @@ +pub mod liquid_insurance_fund; pub mod marginfi_account; pub mod marginfi_group; +pub use liquid_insurance_fund::*; pub use marginfi_account::*; pub use marginfi_group::*; diff --git a/programs/marginfi/src/lib.rs b/programs/marginfi/src/lib.rs index 503327091..c6e14865e 100644 --- a/programs/marginfi/src/lib.rs +++ b/programs/marginfi/src/lib.rs @@ -10,6 +10,7 @@ pub mod utils; use anchor_lang::prelude::*; use instructions::*; use prelude::*; +use state::marginfi_group::WrappedI80F48; use state::marginfi_group::{BankConfigCompact, BankConfigOpt}; cfg_if::cfg_if! { @@ -17,6 +18,8 @@ cfg_if::cfg_if! { declare_id!("MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA"); } else if #[cfg(feature = "devnet")] { declare_id!("neetcne3Ctrrud7vLdt2ypMm21gZHGN2mCmqWaMVcBQ"); + } else if #[cfg(feature = "staging")] { + declare_id!("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"); } else { declare_id!("Mfi1111111111111111111111111111111111111111"); } @@ -24,6 +27,7 @@ cfg_if::cfg_if! { #[program] pub mod marginfi { + use super::*; pub fn marginfi_group_initialize(ctx: Context) -> MarginfiResult { @@ -86,8 +90,8 @@ pub mod marginfi { } /// Handle bad debt of a bankrupt marginfi account for a given bank. - pub fn lending_pool_handle_bankruptcy( - ctx: Context, + pub fn lending_pool_handle_bankruptcy<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingPoolHandleBankruptcy<'info>>, ) -> MarginfiResult { marginfi_group::lending_pool_handle_bankruptcy(ctx) } @@ -99,31 +103,31 @@ pub mod marginfi { marginfi_account::initialize_account(ctx) } - pub fn lending_account_deposit( - ctx: Context, + pub fn lending_account_deposit<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountDeposit<'info>>, amount: u64, ) -> MarginfiResult { marginfi_account::lending_account_deposit(ctx, amount) } - pub fn lending_account_repay( - ctx: Context, + pub fn lending_account_repay<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountRepay<'info>>, amount: u64, repay_all: Option, ) -> MarginfiResult { marginfi_account::lending_account_repay(ctx, amount, repay_all) } - pub fn lending_account_withdraw( - ctx: Context, + pub fn lending_account_withdraw<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountWithdraw<'info>>, amount: u64, withdraw_all: Option, ) -> MarginfiResult { marginfi_account::lending_account_withdraw(ctx, amount, withdraw_all) } - pub fn lending_account_borrow( - ctx: Context, + pub fn lending_account_borrow<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountBorrow<'info>>, amount: u64, ) -> MarginfiResult { marginfi_account::lending_account_borrow(ctx, amount) @@ -135,8 +139,8 @@ pub mod marginfi { marginfi_account::lending_account_close_balance(ctx) } - pub fn lending_account_withdraw_emissions( - ctx: Context, + pub fn lending_account_withdraw_emissions<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountWithdrawEmissions<'info>>, ) -> MarginfiResult { marginfi_account::lending_account_withdraw_emissions(ctx) } @@ -148,8 +152,8 @@ pub mod marginfi { } /// Liquidate a lending account balance of an unhealthy marginfi account - pub fn lending_account_liquidate( - ctx: Context, + pub fn lending_account_liquidate<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountLiquidate<'info>>, asset_amount: u64, ) -> MarginfiResult { marginfi_account::lending_account_liquidate(ctx, asset_amount) @@ -162,8 +166,8 @@ pub mod marginfi { marginfi_account::lending_account_start_flashloan(ctx, end_index) } - pub fn lending_account_end_flashloan( - ctx: Context, + pub fn lending_account_end_flashloan<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingAccountEndFlashloan<'info>>, ) -> MarginfiResult { marginfi_account::lending_account_end_flashloan(ctx) } @@ -175,24 +179,38 @@ pub mod marginfi { marginfi_group::lending_pool_accrue_bank_interest(ctx) } - pub fn lending_pool_collect_bank_fees( - ctx: Context, + pub fn lending_pool_collect_bank_fees<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingPoolCollectBankFees<'info>>, ) -> MarginfiResult { marginfi_group::lending_pool_collect_bank_fees(ctx) } - pub fn lending_pool_withdraw_fees( - ctx: Context, + pub fn lending_pool_withdraw_fees<'info>( + ctx: Context<'_, '_, 'info, 'info, LendingPoolWithdrawFees<'info>>, amount: u64, ) -> MarginfiResult { marginfi_group::lending_pool_withdraw_fees(ctx, amount) } - pub fn lending_pool_withdraw_insurance( - ctx: Context, + pub fn lending_pool_withdraw_insurance<'b, 'info>( + ctx: Context<'_, 'b, 'info, 'info, LendingPoolAdminDepositWithdrawInsurance<'info>>, + amount: WrappedI80F48, + ) -> MarginfiResult { + // safety: all lifetimes are valid for this scope + unsafe { + marginfi_group::lending_pool_withdraw_insurance( + core::mem::transmute(ctx), + amount.into(), + ) + } + } + + pub fn lending_pool_deposit_insurance<'b, 'info>( + ctx: Context<'_, 'b, 'info, 'info, LendingPoolAdminDepositWithdrawInsurance<'info>>, amount: u64, ) -> MarginfiResult { - marginfi_group::lending_pool_withdraw_insurance(ctx, amount) + // safety: all lifetimes are valid for this scope + unsafe { marginfi_group::lending_pool_deposit_insurance(core::mem::transmute(ctx), amount) } } pub fn set_account_flag(ctx: Context, flag: u64) -> MarginfiResult { @@ -208,6 +226,46 @@ pub mod marginfi { ) -> MarginfiResult { marginfi_account::set_account_transfer_authority(ctx) } + + pub fn create_liquid_insurance_fund( + ctx: Context, + min_withdraw_period: i64, + ) -> MarginfiResult { + liquid_insurance_fund::create_liquid_insurance_fund(ctx, min_withdraw_period) + } + + pub fn create_liquid_insurance_fund_account( + ctx: Context, + ) -> MarginfiResult { + liquid_insurance_fund::create_liquid_insurance_fund_account(ctx) + } + + pub fn deposit_into_liquid_insurance_fund<'info>( + ctx: Context<'_, '_, 'info, 'info, DepositIntoLiquidInsuranceFund<'info>>, + amount: u64, + ) -> MarginfiResult { + liquid_insurance_fund::deposit_into_liquid_insurance_fund(ctx, amount) + } + + pub fn create_withdraw_request_from_liquid_token_fund( + ctx: Context, + amount: Option, + ) -> MarginfiResult { + liquid_insurance_fund::create_withdraw_request_from_liquid_token_fund( + ctx, + amount.map(Into::into), + ) + } + + pub fn settle_withdraw_claim_in_liquid_insurance_fund<'info>( + ctx: Context<'_, '_, 'info, 'info, SettleWithdrawClaimInLiquidInsuranceFund<'info>>, + ) -> MarginfiResult { + liquid_insurance_fund::settle_withdraw_claim_in_liquid_insurance_fund(ctx) + } + + pub fn marginfi_account_close(ctx: Context) -> MarginfiResult { + marginfi_account::close_account(ctx) + } } #[cfg(not(feature = "no-entrypoint"))] diff --git a/programs/marginfi/src/state/liquid_insurance_fund.rs b/programs/marginfi/src/state/liquid_insurance_fund.rs new file mode 100644 index 000000000..60e03ada4 --- /dev/null +++ b/programs/marginfi/src/state/liquid_insurance_fund.rs @@ -0,0 +1,618 @@ +use anchor_lang::prelude::*; +use anchor_spl::{ + token::{transfer, Transfer}, + token_interface::Mint, +}; +use fixed::types::I80F48; + +use crate::{ + check, + constants::{LIQUID_INSURANCE_SEED, LIQUID_INSURANCE_USER_SEED}, + debug, math_error, MarginfiError, MarginfiResult, +}; + +use super::marginfi_group::WrappedI80F48; + +/// Fund that represents tokenized insurance pool backed by liquidators +/// Uses the bank's underlying insurance vault to deposit funds +/// Creates a mint representing the tokens managed by the lif +#[account(zero_copy(unsafe))] +#[repr(C)] +#[derive(AnchorDeserialize, AnchorSerialize)] +pub struct LiquidInsuranceFund { + pub bank: Pubkey, + pub bank_mint: Pubkey, + pub vault_authority: Pubkey, + pub min_withdraw_period: i64, + + pub total_shares: WrappedI80F48, + /// This value is only updated at the beginning of relevant LIF instructions and + /// may be outdated. For the most up-to-date value (relevant for off-chain requests), + /// take the insurance vault balance and divide it by the total number of shares. + pub lazy_share_value: WrappedI80F48, + pub admin_shares: WrappedI80F48, + + pub lif_vault_bump: u8, + pub lif_authority_bump: u8, + + // TODO + pub _padding: [[u64; 2]; 28], +} + +impl LiquidInsuranceFund { + pub fn initialize( + &mut self, + bank: Pubkey, + bank_mint: Pubkey, + vault_authority: Pubkey, + min_withdraw_period: i64, + lif_vault_bump: u8, + lif_authority_bump: u8, + balance: u64, + ) -> MarginfiResult<()> { + check!( + min_withdraw_period > 0, + MarginfiError::InsuranceFundInvalidWithdrawPeriod + ); + let admin_shares = I80F48::from(balance); + *self = LiquidInsuranceFund { + bank, + bank_mint, + min_withdraw_period, + total_shares: admin_shares.into(), + lazy_share_value: I80F48::ONE.into(), + admin_shares: admin_shares.into(), + vault_authority, + lif_vault_bump, + lif_authority_bump, + _padding: [[0; 2]; 28], + }; + Ok(()) + } + + pub fn process_withdrawal( + &mut self, + withdrawal: &mut LiquidInsuranceFundWithdrawal, + ) -> MarginfiResult { + // Fetch shares to withdraw + let withdraw_shares: I80F48 = withdrawal.shares.into(); + + // Calculate token amount + let withdraw_token_amount = self.get_value(withdraw_shares)?.to_num(); + + // Decrement total share count and update share value + self.withdraw_shares(withdraw_shares)?; + + // Free withdrawal slot + withdrawal.free(); + + Ok(withdraw_token_amount) + } + + pub(crate) fn deposit_spl_transfer<'info>( + &self, + amount: u64, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + program: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + remaining_accounts: &'info [AccountInfo<'info>], + ) -> MarginfiResult { + // Only deposits to the bank's insurance vault are allowed. + // TODO add check against bank insurance vault? By deriving address + + debug!( + "deposit_spl_transfer: amount: {} from {} to {}, auth {}", + amount, from.key, to.key, authority.key + ); + + if let Some(mint) = maybe_mint { + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + program.key, + from, + mint.to_account_info(), + to, + authority, + remaining_accounts, + amount, + mint.decimals, + &[], + )?; + } else { + #[allow(deprecated)] + transfer( + CpiContext::new_with_signer( + program, + Transfer { + from, + to, + authority, + }, + &[], + ), + amount, + )?; + } + + Ok(()) + } + + pub(crate) fn withdraw_spl_transfer<'info>( + &self, + amount: u64, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + program: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + remaining_accounts: &'info [AccountInfo<'info>], + signer_seeds: &[&[&[u8]]], + ) -> MarginfiResult { + // Only withdraws from the bank's insurance vault are allowed. + // TODO add check against bank insurance vault? By deriving address + + debug!( + "withdraw_spl_transfer: amount: {} from {} to {}, auth {}", + amount, from.key, to.key, authority.key + ); + + if let Some(mint) = maybe_mint { + anchor_spl::token_2022::spl_token_2022::onchain::invoke_transfer_checked( + program.key, + from, + mint.to_account_info(), + to, + authority, + remaining_accounts, + amount, + mint.decimals, + signer_seeds, + )?; + } else { + #[allow(deprecated)] + transfer( + CpiContext::new_with_signer( + program, + Transfer { + from, + to, + authority, + }, + signer_seeds, + ), + amount, + )?; + } + + Ok(()) + } + + pub(crate) fn deposit_shares(&mut self, shares: I80F48) -> MarginfiResult { + // Update the internal count of shares + self.increase_balance_internal(shares)?; + + Ok(()) + } + + /// Internal arithmetic for increase the balance of the liquid insurance fund + pub(crate) fn increase_balance_internal(&mut self, shares: I80F48) -> MarginfiResult { + check!(shares > I80F48::ZERO, MarginfiError::InvalidTransfer); + + // Add new shares to existing collection of shares + self.add_shares(shares)?; + + Ok(()) + } + + pub(crate) fn update_share_price_internal( + &mut self, + bank_insurance_vault_amount: u64, + ) -> MarginfiResult { + // Reset share value if there are no shares + if self.get_total_shares() == I80F48::ZERO { + self.lazy_share_value = I80F48::ONE.into(); + return Ok(()); + } + + // Update share price based on latest number of shares and the + // number of shares in the bank insurance vault + self.lazy_share_value = I80F48::from(bank_insurance_vault_amount) + .checked_div(self.total_shares.into()) + .ok_or_else(math_error!())? + .into(); + + Ok(()) + } + + pub(crate) fn add_shares(&mut self, shares: I80F48) -> MarginfiResult { + let total_shares: I80F48 = self.total_shares.into(); + self.total_shares = total_shares + .checked_add(shares) + .ok_or_else(math_error!())? + .into(); + Ok(()) + } + + pub(crate) fn get_shares(&self, value: I80F48) -> MarginfiResult { + Ok(value + .checked_div(self.lazy_share_value.into()) + .ok_or_else(math_error!())?) + } + + pub fn get_admin_shares(&self) -> I80F48 { + self.admin_shares.into() + } + + pub fn get_total_shares(&self) -> I80F48 { + self.total_shares.into() + } + + pub(crate) fn set_admin_shares(&mut self, shares: I80F48) { + self.admin_shares = shares.into(); + } + + pub(crate) fn get_value(&self, shares: I80F48) -> MarginfiResult { + Ok(shares + .checked_mul(self.lazy_share_value.into()) + .ok_or_else(math_error!())?) + } + + pub(crate) fn withdraw_shares(&mut self, delta: I80F48) -> MarginfiResult { + let total_shares: I80F48 = self.total_shares.into(); + let new_shares = total_shares.checked_sub(delta).ok_or_else(math_error!())?; + check!(new_shares >= 0, MarginfiError::MathError); + self.total_shares = new_shares.into(); + Ok(()) + } + + pub(crate) fn haircut_shares(&mut self, decrease_amount: u64) -> MarginfiResult { + let total_shares: I80F48 = self.total_shares.into(); + let share_value: I80F48 = self.lazy_share_value.into(); + + let new_share_value = total_shares + .checked_mul(share_value) + .ok_or_else(math_error!())? + .checked_sub(decrease_amount.into()) + .ok_or_else(math_error!())? + .checked_div(total_shares) + .ok_or_else(math_error!())?; + + self.lazy_share_value = new_share_value.into(); + + Ok(()) + } + + /// Utility function for attempting to load a lif; a bank may or may not have a lif. + /// + /// This does NOT check the account address. Caller must do this! + /// + /// Assuming address is checked: + /// 1) If the bank has a lif, this will return Ok(lif). + /// 2) If the bank does not have a lif, Err(e) will be returned. + /// + /// NOTE: this is done because if AccountLoader<'info, T> is used in a #[derive(Accounts)] + /// struct in case 2), the instruction will simply fail due to an account owner check. + pub fn try_loader<'a>( + ai: &'a AccountInfo<'a>, + // ) -> MarginfiResult> { + ) -> MarginfiResult> { + AccountLoader::try_from_unchecked(&crate::ID, ai) + } + + pub(crate) fn maybe_process_admin_withdraw<'info>( + liquid_insurance_fund: &'info AccountInfo<'info>, + insurance_vault_balance: u64, + amount: I80F48, + ) -> MarginfiResult { + let tokens = if let Ok(lif) = LiquidInsuranceFund::try_loader(&liquid_insurance_fund) { + let mut lif = lif.load_mut()?; + lif.update_share_price_internal(insurance_vault_balance)?; + + let admin_shares = lif.get_admin_shares(); + if admin_shares < amount { + msg!( + "admin shares {} vs amount requested {}", + admin_shares, + amount + ); + return err!(MarginfiError::InvalidWithdrawal); + } + + let token_amount = lif.get_value(amount)?; + lif.withdraw_shares(amount)?; + lif.set_admin_shares(admin_shares - amount); + + token_amount.to_num::() + } else { + // If we are in this branch, the I80F48 is token amount + amount.to_num::() + }; + + Ok(tokens) + } + + /// This does NOT check the lif account address. Caller must do this! + pub(crate) fn maybe_process_admin_deposit<'info>( + liquid_insurance_fund: &'info AccountInfo<'info>, + insurance_vault_balance: u64, + amount: u64, + ) -> MarginfiResult<()> { + if let Ok(lif) = LiquidInsuranceFund::try_loader(liquid_insurance_fund) { + let mut lif = lif.load_mut()?; + lif.update_share_price_internal(insurance_vault_balance)?; + + let admin_shares = lif.get_admin_shares(); + let added_shares = lif.get_shares(amount.into())?; + let new_admin_shares = admin_shares + added_shares; + lif.set_admin_shares(new_admin_shares); + lif.add_shares(added_shares)?; + } + + Ok(()) + } + + pub fn maybe_process_bankruptcy<'info>( + liquid_insurance_fund: &'info AccountInfo<'info>, + amount_covered_by_insurance: I80F48, + ) -> MarginfiResult<()> { + if let Ok(lif) = LiquidInsuranceFund::try_loader(liquid_insurance_fund) { + let amount_covered_by_insurance = amount_covered_by_insurance + .checked_to_num::() + .ok_or(MarginfiError::MathError)?; + let mut lif = lif.load_mut()?; + lif.haircut_shares(amount_covered_by_insurance)?; + } + + Ok(()) + } + + pub fn address(bank: &Pubkey) -> Pubkey { + Pubkey::find_program_address( + &[LIQUID_INSURANCE_SEED.as_bytes(), bank.as_ref()], + &crate::ID, + ) + .0 + } +} + +#[account(zero_copy)] +pub struct LiquidInsuranceFundAccount { + pub authority: Pubkey, + pub balances: [LiquidInsuranceFundBalance; 16], + pub withdrawals: [LiquidInsuranceFundWithdrawal; 16], + pub padding: [u64; 8], +} + +impl LiquidInsuranceFundAccount { + pub fn address(user: &Pubkey) -> Pubkey { + Pubkey::find_program_address( + &[LIQUID_INSURANCE_USER_SEED.as_bytes(), user.as_ref()], + &crate::ID, + ) + .0 + } + + pub fn initialize(&mut self, authority: Pubkey) { + self.authority = authority; + } + + pub fn get_deposit( + &mut self, + bank_insurance_fund: &Pubkey, + ) -> Option<&LiquidInsuranceFundBalance> { + for balance in self.balances.iter() { + if balance.bank_insurance_fund.eq(bank_insurance_fund) { + return Some(balance); + } + } + None + } + + /// Try to find an existing deposit for this insurance vault, + /// fallback to a new deposit if possible + pub fn get_or_init_deposit( + &mut self, + bank_insurance_fund: &Pubkey, + ) -> Option<&mut LiquidInsuranceFundBalance> { + let mut maybe_new = None; + for balance in self.balances.iter_mut() { + if balance.bank_insurance_fund.eq(bank_insurance_fund) { + return Some(balance); + } else if balance.is_empty() { + maybe_new.get_or_insert(balance); + } + } + + maybe_new.map(|deposit| { + deposit.bank_insurance_fund = *bank_insurance_fund; + deposit + }) + } + + /// Try to find an existing withdraw claim for this insurance vault. + /// If there are multiple, the oldest/earliest is returned + pub fn get_earliest_withdrawal( + &mut self, + bank_insurance_fund: &Pubkey, + ) -> Option<&mut LiquidInsuranceFundWithdrawal> { + let mut oldest_withdrawal = None; + for (i, withdrawal) in self.withdrawals.iter().enumerate() { + if withdrawal.bank_insurance_fund.eq(bank_insurance_fund) { + if self.withdrawals[*oldest_withdrawal.get_or_insert(i)].withdraw_request_timestamp + > withdrawal.withdraw_request_timestamp + { + oldest_withdrawal.replace(i); + } + } + } + + oldest_withdrawal.map(|i| &mut self.withdrawals[i]) + } + + /// 1) Look for existing deposit + /// 2) Validate and decrement requested shares + /// 3) Initialize withdrawal in empty slot + pub fn create_withdrawal( + &mut self, + bank_insurance_fund: &Pubkey, + // All if None + shares: Option, + timestamp: i64, + ) -> MarginfiResult { + // 1) Look for existing deposit + let deposit = self + .balances + .iter_mut() + .find(|deposit| deposit.bank_insurance_fund.eq(bank_insurance_fund)) + .ok_or(MarginfiError::InvalidWithdrawal)?; + + // 2) Validate and decrement requested shares + let deposit_shares: I80F48 = deposit.shares.into(); + let requested_shares = shares.unwrap_or(deposit_shares); + if requested_shares > deposit_shares { + return err!(MarginfiError::InvalidWithdrawal); + } + let new_shares = deposit_shares - requested_shares; + deposit.shares = new_shares.into(); + if new_shares == I80F48::ZERO { + deposit.free(); + } + + // 3) Initialize withdrawal in empty slot + let withdrawal_slot = self + .withdrawals + .iter_mut() + .find(|slot| slot.is_empty()) + .ok_or(MarginfiError::InsuranceFundAccountWithdrawSlotsFull)?; + *withdrawal_slot = LiquidInsuranceFundWithdrawal { + bank_insurance_fund: *bank_insurance_fund, + shares: requested_shares.into(), + withdraw_request_timestamp: timestamp, + }; + + Ok(requested_shares) + } +} + +#[account(zero_copy)] +#[derive(AnchorSerialize, AnchorDeserialize, Debug)] +pub struct LiquidInsuranceFundAccountData { + pub balances: [LiquidInsuranceFundBalance; 16], + pub withdrawals: [LiquidInsuranceFundWithdrawal; 16], +} + +#[account(zero_copy)] +#[derive(AnchorSerialize, AnchorDeserialize, Debug, PartialEq, Default)] +pub struct LiquidInsuranceFundBalance { + pub bank_insurance_fund: Pubkey, + pub shares: WrappedI80F48, +} + +impl LiquidInsuranceFundBalance { + fn is_empty(&self) -> bool { + self.shares.value == I80F48::ZERO.to_le_bytes() + } + + pub fn shares(&self) -> I80F48 { + self.shares.into() + } + + /// Can be used to subtract given I80F48 is signed + pub fn add_shares(&mut self, shares: I80F48) { + let current_shares: I80F48 = self.shares.into(); + self.shares = (current_shares + shares).into(); + } + + pub(crate) fn free(&mut self) { + *self = Default::default(); + } +} + +#[account(zero_copy)] +#[derive(AnchorSerialize, AnchorDeserialize, Debug, Default, PartialEq)] +pub struct LiquidInsuranceFundWithdrawal { + pub bank_insurance_fund: Pubkey, + pub shares: WrappedI80F48, + pub withdraw_request_timestamp: i64, +} + +impl LiquidInsuranceFundWithdrawal { + pub fn is_empty(&self) -> bool { + self.shares.value == I80F48::ZERO.to_le_bytes() + } + + pub fn shares(&self) -> I80F48 { + self.shares.into() + } + + pub fn free(&mut self) { + *self = Default::default(); + } +} + +// #[test] +// fn test_share_deposit_accounting() { +// let mut lif = LiquidInsuranceFund::new(); + +// // Total bank vault amount = 1_000_000 +// // Total number of shares = 10 by default +// // Price per share initial = 1000 +// assert!(lif.total_shares == I80F48::from_num(10).into()); +// assert!(lif.share_value == I80F48::from(100_000).into()); + +// let user_deposit_amount = I80F48::from_num(100_000); +// let bank_insurance_vault_amount_first_deposit = I80F48::from_num(1_000_000); + +// // User deposits 10 units of tokens +// lif.deposit_shares( +// user_deposit_amount, +// bank_insurance_vault_amount_first_deposit, +// ) +// .unwrap(); + +// assert!(lif.total_shares == I80F48::from(11).into()); // 11 +// assert!( +// lif.share_value +// == bank_insurance_vault_amount_first_deposit +// .checked_div(I80F48::from_num(11)) +// .unwrap() +// .into() +// ); // 90909.09090909090909 + +// // Deposit some more shares +// let user_deposit_amount_2 = I80F48::from_num(1_000); +// let bank_insurance_vault_amount_second_deposit = I80F48::from_num(1_000_100); +// lif.deposit_shares( +// user_deposit_amount_2, +// bank_insurance_vault_amount_second_deposit, +// ) +// .unwrap(); + +// println!("{:?}", lif.total_shares); +// println!("{:?}", lif.share_value); +// } + +// #[test] +// fn test_share_withdraw_accounting() { +// let mut lif = LiquidInsuranceFund::new(); + +// // Total bank vault amount = 1_000_000 +// // Total number of shares = 10 by default +// // Price per share initial = 1000 +// assert!(lif.total_shares == I80F48::from_num(10).into()); +// assert!(lif.share_value == I80F48::from(100_000).into()); + +// // uses shares instead of amounts (representing units of the liquid token) +// let user_withdraw_shares = I80F48::from_num(5); +// let bank_insurance_vault_amount_first_withdrawal = I80F48::from_num(1_000_000); + +// lif.withdraw_shares( +// user_withdraw_shares, +// bank_insurance_vault_amount_first_withdrawal, +// ) +// .unwrap(); + +// assert!(lif.total_shares == I80F48::from(5).into()); // 5 +// assert!(lif.share_value == I80F48::from_num(200_000).into()); // 200k +// } diff --git a/programs/marginfi/src/state/marginfi_account.rs b/programs/marginfi/src/state/marginfi_account.rs index e20521c2f..978612c7d 100644 --- a/programs/marginfi/src/state/marginfi_account.rs +++ b/programs/marginfi/src/state/marginfi_account.rs @@ -14,7 +14,7 @@ use crate::{ utils::NumTraitsWithTolerance, }; use anchor_lang::prelude::*; -use anchor_spl::token::Transfer; +use anchor_spl::token_interface::Mint; use fixed::types::I80F48; use std::{ cmp::{max, min}, @@ -42,7 +42,7 @@ pub struct MarginfiAccount { /// - DISABLED_FLAG = 1 << 0 = 1 - This flag indicates that the account is disabled, /// and no further actions can be taken on it. pub account_flags: u64, // 8 - pub _padding: [u64; 63], // 8 * 63 = 512 + pub _padding: [u64; 63], // 504 } pub const DISABLED_FLAG: u64 = 1 << 0; @@ -101,6 +101,17 @@ impl MarginfiAccount { ); Ok(()) } + + pub fn can_be_closed(&self) -> bool { + let is_disabled = self.get_flag(DISABLED_FLAG); + let only_has_empty_balances = self + .lending_account + .balances + .iter() + .all(|balance| balance.get_side().is_none()); + + !is_disabled && only_has_empty_balances + } } #[derive(Debug)] @@ -139,8 +150,8 @@ impl RequirementType { } } -pub struct BankAccountWithPriceFeed<'a, 'b> { - bank: AccountInfo<'b>, +pub struct BankAccountWithPriceFeed<'a, 'info> { + bank: AccountInfo<'info>, price_feed: Box>, balance: &'a Balance, } @@ -150,11 +161,11 @@ pub enum BalanceSide { Liabilities, } -impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { - pub fn load( +impl<'info> BankAccountWithPriceFeed<'_, 'info> { + pub fn load<'a>( lending_account: &'a LendingAccount, - remaining_ais: &[AccountInfo<'b>], - ) -> MarginfiResult>> { + remaining_ais: &'info [AccountInfo<'info>], + ) -> MarginfiResult>> { let active_balances = lending_account .balances .iter() @@ -165,11 +176,11 @@ impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { debug!("Got {} remaining accounts", remaining_ais.len()); check!( - active_balances.len() * 2 == remaining_ais.len(), + active_balances.len() * 2 <= remaining_ais.len(), MarginfiError::MissingPythOrBankAccount ); - let current_timestamp = Clock::get()?.unix_timestamp; + let clock = Clock::get()?; active_balances .iter() @@ -193,7 +204,7 @@ impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { Box::new(OraclePriceFeedAdapter::try_from_bank_config( &bank.config, oracle_ais, - current_timestamp, + &clock, )) }; @@ -215,13 +226,18 @@ impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { /// 3. Initial requirement is discounted by the initial discount, if enabled and the usd limit is exceeded. /// 4. Assets are only calculated for collateral risk tier. /// 5. Oracle errors are ignored for deposits in isolated risk tier. - fn calc_weighted_assets_and_liabilities_values( - &self, + fn calc_weighted_assets_and_liabilities_values<'a>( + &'a self, requirement_type: RequirementType, - ) -> MarginfiResult<(I80F48, I80F48)> { + ) -> MarginfiResult<(I80F48, I80F48)> + where + 'info: 'a, + { match self.balance.get_side() { Some(side) => { - let bank_al = AccountLoader::::try_from(&self.bank)?; + // SAFETY: We are shortening 'info -> 'a + let shorter_bank: &'a AccountInfo<'a> = unsafe { core::mem::transmute(&self.bank) }; + let bank_al = AccountLoader::::try_from(shorter_bank)?; let bank = bank_al.load()?; match side { BalanceSide::Assets => Ok(( @@ -239,10 +255,10 @@ impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { } #[inline(always)] - fn calc_weighted_assets( - &self, + fn calc_weighted_assets<'a>( + &'a self, requirement_type: RequirementType, - bank: &Bank, + bank: &'a Bank, ) -> MarginfiResult { match bank.config.risk_tier { RiskTier::Collateral => { @@ -315,7 +331,11 @@ impl<'a, 'b> BankAccountWithPriceFeed<'a, 'b> { fn try_get_price_feed(&self) -> std::result::Result<&OraclePriceFeedAdapter, PriceFeedError> { match self.price_feed.as_ref() { Ok(a) => Ok(a), - Err(_) => Err(PriceFeedError::StaleOracle), + #[allow(unused_variables)] + Err(e) => { + debug!("Price feed error: {:?}", e); + Err(PriceFeedError::StaleOracle) + } } } @@ -401,16 +421,16 @@ impl RiskRequirementType { } } -pub struct RiskEngine<'a, 'b> { +pub struct RiskEngine<'a, 'info> { marginfi_account: &'a MarginfiAccount, - bank_accounts_with_price: Vec>, + bank_accounts_with_price: Vec>, } -impl<'a, 'b> RiskEngine<'a, 'b> { - pub fn new( +impl<'info> RiskEngine<'_, 'info> { + pub fn new<'a>( marginfi_account: &'a MarginfiAccount, - remaining_ais: &[AccountInfo<'b>], - ) -> MarginfiResult { + remaining_ais: &'info [AccountInfo<'info>], + ) -> MarginfiResult> { check!( !marginfi_account.get_flag(IN_FLASHLOAN_FLAG), MarginfiError::AccountInFlashloan @@ -421,14 +441,14 @@ impl<'a, 'b> RiskEngine<'a, 'b> { /// Internal constructor used either after manually checking account is not in a flashloan, /// or explicity checking health for flashloan enabled actions. - fn new_no_flashloan_check( + fn new_no_flashloan_check<'a>( marginfi_account: &'a MarginfiAccount, - remaining_ais: &[AccountInfo<'b>], - ) -> MarginfiResult { + remaining_ais: &'info [AccountInfo<'info>], + ) -> MarginfiResult> { let bank_accounts_with_price = BankAccountWithPriceFeed::load(&marginfi_account.lending_account, remaining_ais)?; - Ok(Self { + Ok(RiskEngine { marginfi_account, bank_accounts_with_price, }) @@ -439,9 +459,9 @@ impl<'a, 'b> RiskEngine<'a, 'b> { /// `IN_FLASHLOAN_FLAG` behavior. /// - Health check is skipped. /// - `remaining_ais` can be an empty vec. - pub fn check_account_init_health( + pub fn check_account_init_health<'a>( marginfi_account: &'a MarginfiAccount, - remaining_ais: &[AccountInfo<'b>], + remaining_ais: &'info [AccountInfo<'info>], ) -> MarginfiResult<()> { if marginfi_account.get_flag(IN_FLASHLOAN_FLAG) { return Ok(()); @@ -465,6 +485,11 @@ impl<'a, 'b> RiskEngine<'a, 'b> { let (assets, liabilities) = a.calc_weighted_assets_and_liabilities_values(requirement_type.to_weight_type())?; + debug!( + "Balance {}, assets: {}, liabilities: {}", + a.balance.bank_pk, assets, liabilities + ); + total_assets = total_assets.checked_add(assets).ok_or_else(math_error!())?; total_liabilities = total_liabilities .checked_add(liabilities) @@ -475,7 +500,7 @@ impl<'a, 'b> RiskEngine<'a, 'b> { } pub fn get_account_health( - &self, + &'info self, requirement_type: RiskRequirementType, ) -> MarginfiResult { let (total_weighted_assets, total_weighted_liabilities) = @@ -648,7 +673,10 @@ impl<'a, 'b> RiskEngine<'a, 'b> { Ok(()) } - fn check_account_risk_tiers(&self) -> MarginfiResult { + fn check_account_risk_tiers<'a>(&'a self) -> MarginfiResult + where + 'info: 'a, + { let balances_with_liablities = self .bank_accounts_with_price .iter() @@ -657,7 +685,9 @@ impl<'a, 'b> RiskEngine<'a, 'b> { let n_balances_with_liablities = balances_with_liablities.clone().count(); let is_in_isolated_risk_tier = balances_with_liablities.clone().any(|a| { - AccountLoader::::try_from(&a.bank) + // SAFETY: We are shortening 'info -> 'a + let shorter_bank: &'a AccountInfo<'a> = unsafe { core::mem::transmute(&a.bank) }; + AccountLoader::::try_from(shorter_bank) .unwrap() .load() .unwrap() @@ -778,10 +808,10 @@ impl Balance { asset_shares < EMPTY_BALANCE_THRESHOLD || liability_shares < EMPTY_BALANCE_THRESHOLD ); - if I80F48::from(self.asset_shares) >= EMPTY_BALANCE_THRESHOLD { - Some(BalanceSide::Assets) - } else if I80F48::from(self.liability_shares) >= EMPTY_BALANCE_THRESHOLD { + if I80F48::from(self.liability_shares) >= EMPTY_BALANCE_THRESHOLD { Some(BalanceSide::Liabilities) + } else if I80F48::from(self.asset_shares) >= EMPTY_BALANCE_THRESHOLD { + Some(BalanceSide::Assets) } else { None } @@ -816,7 +846,7 @@ impl<'a> BankAccountWrapper<'a> { .balances .iter_mut() .find(|balance| balance.active && balance.bank_pk.eq(bank_pk)) - .ok_or_else(|| error!(MarginfiError::BankAccoutNotFound))?; + .ok_or_else(|| error!(MarginfiError::BankAccountNotFound))?; Ok(Self { balance, bank }) } @@ -838,7 +868,7 @@ impl<'a> BankAccountWrapper<'a> { let balance = lending_account .balances .get_mut(balance_index) - .ok_or_else(|| error!(MarginfiError::BankAccoutNotFound))?; + .ok_or_else(|| error!(MarginfiError::BankAccountNotFound))?; Ok(Self { balance, bank }) } @@ -1259,24 +1289,48 @@ impl<'a> BankAccountWrapper<'a> { // ------------ SPL helpers - pub fn deposit_spl_transfer<'b: 'c, 'c: 'b>( + pub fn deposit_spl_transfer<'info>( &self, amount: u64, - accounts: Transfer<'b>, - program: AccountInfo<'c>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + program: AccountInfo<'info>, + remaining_accounts: &[AccountInfo<'info>], ) -> MarginfiResult { - self.bank.deposit_spl_transfer(amount, accounts, program) + self.bank.deposit_spl_transfer( + amount, + from, + to, + authority, + maybe_mint, + program, + remaining_accounts, + ) } - pub fn withdraw_spl_transfer<'b: 'c, 'c: 'b>( + pub fn withdraw_spl_transfer<'info>( &self, amount: u64, - accounts: Transfer<'b>, - program: AccountInfo<'c>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + program: AccountInfo<'info>, signer_seeds: &[&[&[u8]]], + remaining_accounts: &[AccountInfo<'info>], ) -> MarginfiResult { - self.bank - .withdraw_spl_transfer(amount, accounts, program, signer_seeds) + self.bank.withdraw_spl_transfer( + amount, + from, + to, + authority, + maybe_mint, + program, + signer_seeds, + remaining_accounts, + ) } } diff --git a/programs/marginfi/src/state/marginfi_group.rs b/programs/marginfi/src/state/marginfi_group.rs index 6914bdb6b..968d2fa83 100644 --- a/programs/marginfi/src/state/marginfi_group.rs +++ b/programs/marginfi/src/state/marginfi_group.rs @@ -2,6 +2,7 @@ use super::{ marginfi_account::{BalanceSide, RequirementType}, price::{OraclePriceFeedAdapter, OracleSetup}, }; +use crate::borsh::{BorshDeserialize, BorshSerialize}; #[cfg(not(feature = "client"))] use crate::events::{GroupEventHeader, LendingPoolBankAccrueInterestEvent}; use crate::{ @@ -19,10 +20,12 @@ use crate::{ state::marginfi_account::calc_value, MarginfiResult, }; +use anchor_lang::prelude::borsh; use anchor_lang::prelude::*; -use anchor_spl::token::{transfer, Transfer}; +use anchor_spl::token_interface::*; use fixed::types::I80F48; -use pyth_sdk_solana::{load_price_feed_from_account_info, PriceFeed}; +use pyth_sdk_solana::{state::SolanaPriceAccount, PriceFeed}; +use pyth_solana_receiver_sdk::price_update::FeedId; #[cfg(feature = "client")] use std::fmt::Display; use std::{ @@ -73,12 +76,11 @@ pub struct GroupConfig { /// Load and validate a pyth price feed account. pub fn load_pyth_price_feed(ai: &AccountInfo) -> MarginfiResult { check!(ai.owner.eq(&PYTH_ID), MarginfiError::InvalidOracleAccount); - let price_feed = - load_price_feed_from_account_info(ai).map_err(|_| MarginfiError::InvalidOracleAccount)?; + let price_feed = SolanaPriceAccount::account_info_to_feed(ai) + .map_err(|_| MarginfiError::InvalidOracleAccount)?; Ok(price_feed) } -#[zero_copy] #[repr(C)] #[cfg_attr( any(feature = "test", feature = "client"), @@ -133,7 +135,7 @@ impl From for InterestRateConfigCompact { any(feature = "test", feature = "client"), derive(PartialEq, Eq, TypeLayout) )] -#[derive(Default, Debug, AnchorDeserialize, AnchorSerialize)] +#[derive(Default, Debug)] pub struct InterestRateConfig { // Curve Params pub optimal_utilization_rate: WrappedI80F48, @@ -377,8 +379,7 @@ impl Bank { emissions_rate: 0, emissions_remaining: I80F48::ZERO.into(), emissions_mint: Pubkey::default(), - _padding_0: [[0; 2]; 28], - _padding_1: [[0; 2]; 32], + ..Default::default() } } @@ -644,41 +645,157 @@ impl Bank { Ok(()) } - pub fn deposit_spl_transfer<'b: 'c, 'c: 'b>( + pub fn deposit_spl_transfer<'info>( &self, amount: u64, - accounts: Transfer<'b>, - program: AccountInfo<'c>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + program: AccountInfo<'info>, + remaining_accounts: &[AccountInfo<'info>], ) -> MarginfiResult { check!( - accounts.to.key.eq(&self.liquidity_vault), + to.key.eq(&self.liquidity_vault), MarginfiError::InvalidTransfer ); debug!( "deposit_spl_transfer: amount: {} from {} to {}, auth {}", - amount, accounts.from.key, accounts.to.key, accounts.authority.key + amount, from.key, to.key, authority.key ); - transfer(CpiContext::new(program, accounts), amount) + if let Some(mint) = maybe_mint { + spl_token_2022::onchain::invoke_transfer_checked( + program.key, + from, + mint.to_account_info(), + to, + authority, + remaining_accounts, + amount, + mint.decimals, + &[], + )?; + } else { + #[allow(deprecated)] + transfer( + CpiContext::new_with_signer( + program, + Transfer { + from, + to, + authority, + }, + &[], + ), + amount, + )?; + } + + Ok(()) + } + + pub fn deposit_insurance_spl_transfer<'info>( + &self, + amount: u64, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + program: AccountInfo<'info>, + remaining_accounts: &[AccountInfo<'info>], + ) -> MarginfiResult { + check!( + to.key.eq(&self.insurance_vault), + MarginfiError::InvalidTransfer + ); + + debug!( + "deposit_spl_transfer: amount: {} from {} to {}, auth {}", + amount, from.key, to.key, authority.key + ); + + if let Some(mint) = maybe_mint { + spl_token_2022::onchain::invoke_transfer_checked( + program.key, + from, + mint.to_account_info(), + to, + authority, + remaining_accounts, + amount, + mint.decimals, + &[], + )?; + } else { + #[allow(deprecated)] + transfer( + CpiContext::new_with_signer( + program, + Transfer { + from, + to, + authority, + }, + &[], + ), + amount, + )?; + } + + Ok(()) } - pub fn withdraw_spl_transfer<'b: 'c, 'c: 'b>( + pub fn withdraw_spl_transfer<'info>( &self, amount: u64, - accounts: Transfer<'b>, - program: AccountInfo<'c>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + maybe_mint: Option<&InterfaceAccount<'info, Mint>>, + program: AccountInfo<'info>, signer_seeds: &[&[&[u8]]], + remaining_accounts: &[AccountInfo<'info>], ) -> MarginfiResult { debug!( "withdraw_spl_transfer: amount: {} from {} to {}, auth {}", - amount, accounts.from.key, accounts.to.key, accounts.authority.key + amount, from.key, to.key, authority.key ); - transfer( - CpiContext::new_with_signer(program, accounts, signer_seeds), - amount, - ) + if let Some(mint) = maybe_mint { + spl_token_2022::onchain::invoke_transfer_checked( + program.key, + from, + mint.to_account_info(), + to, + authority, + remaining_accounts, + amount, + mint.decimals, + signer_seeds, + )?; + } else { + // `transfer_checked` and `transfer` does the same thing, the additional `_checked` logic + // is only to assert the expected attributes by the user (mint, decimal scaling), + // + // Security of `transfer` is equal to `transfer_checked`. + #[allow(deprecated)] + transfer( + CpiContext::new_with_signer( + program, + Transfer { + from, + to, + authority, + }, + signer_seeds, + ), + amount, + )?; + } + + Ok(()) } /// Socialize a loss `loss_amount` among depositors, @@ -876,7 +993,6 @@ pub enum RiskTier { Isolated, } -#[zero_copy(unsafe)] #[repr(C)] #[cfg_attr( any(feature = "test", feature = "client"), @@ -973,7 +1089,7 @@ assert_struct_align!(BankConfig, 8); any(feature = "test", feature = "client"), derive(PartialEq, Eq, TypeLayout) )] -#[derive(AnchorDeserialize, AnchorSerialize, Debug)] +#[derive(Debug)] /// TODO: Convert weights to (u64, u64) to avoid precision loss (maybe?) pub struct BankConfig { pub asset_weight_init: WrappedI80F48, @@ -1118,19 +1234,25 @@ impl BankConfig { pub fn get_oracle_max_age(&self) -> u64 { match (self.oracle_max_age, self.oracle_setup) { (0, OracleSetup::SwitchboardV2) => MAX_SWB_ORACLE_AGE, - (0, OracleSetup::PythEma) => MAX_PYTH_ORACLE_AGE, + (0, OracleSetup::PythLegacy | OracleSetup::PythPushOracle) => MAX_PYTH_ORACLE_AGE, (n, _) => n as u64, } } + + pub fn get_pyth_push_oracle_feed_id(&self) -> Option<&FeedId> { + if matches!(self.oracle_setup, OracleSetup::PythPushOracle) { + let bytes: &[u8; 32] = self.oracle_keys[0].as_ref().try_into().unwrap(); + Some(bytes) + } else { + None + } + } } #[zero_copy] #[repr(C, align(8))] -#[cfg_attr( - any(feature = "test", feature = "client"), - derive(PartialEq, Eq, TypeLayout) -)] -#[derive(Default, AnchorDeserialize, AnchorSerialize)] +#[cfg_attr(any(feature = "test", feature = "client"), derive(TypeLayout))] +#[derive(Default, BorshDeserialize, BorshSerialize, PartialEq, Eq)] pub struct WrappedI80F48 { pub value: [u8; 16], } @@ -1189,7 +1311,7 @@ pub struct BankConfigOpt { any(feature = "test", feature = "client"), derive(PartialEq, Eq, TypeLayout) )] -#[derive(Clone, Copy, AnchorDeserialize, AnchorSerialize)] +#[derive(Clone, Copy, AnchorDeserialize, AnchorSerialize, Debug)] pub struct OracleConfig { pub setup: OracleSetup, pub keys: [Pubkey; MAX_ORACLE_KEYS], diff --git a/programs/marginfi/src/state/mod.rs b/programs/marginfi/src/state/mod.rs index 710755626..f7e15668b 100644 --- a/programs/marginfi/src/state/mod.rs +++ b/programs/marginfi/src/state/mod.rs @@ -1,3 +1,4 @@ +pub mod liquid_insurance_fund; pub mod marginfi_account; pub mod marginfi_group; pub mod price; diff --git a/programs/marginfi/src/state/price.rs b/programs/marginfi/src/state/price.rs index e951bc09c..99b005b6b 100644 --- a/programs/marginfi/src/state/price.rs +++ b/programs/marginfi/src/state/price.rs @@ -1,32 +1,38 @@ use std::cmp::min; use anchor_lang::prelude::*; - use enum_dispatch::enum_dispatch; use fixed::types::I80F48; -use pyth_sdk_solana::{load_price_feed_from_account_info, Price, PriceFeed}; -use switchboard_v2::{ +use pyth_sdk_solana::{state::SolanaPriceAccount, Price, PriceFeed}; +use pyth_solana_receiver_sdk::price_update::{self, FeedId, PriceUpdateV2}; +use switchboard_solana::{ AggregatorAccountData, AggregatorResolutionMode, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID, }; +pub use pyth_sdk_solana; + use crate::{ check, constants::{ - CONF_INTERVAL_MULTIPLE, EXP_10, EXP_10_I80F48, MAX_CONF_INTERVAL, PYTH_ID, STD_DEV_MULTIPLE, + CONF_INTERVAL_MULTIPLE, EXP_10, EXP_10_I80F48, MAX_CONF_INTERVAL, + MIN_PYTH_PUSH_VERIFICATION_LEVEL, PYTH_ID, STD_DEV_MULTIPLE, }, debug, math_error, prelude::*, }; use super::marginfi_group::BankConfig; +use anchor_lang::prelude::borsh; +use pyth_solana_receiver_sdk::PYTH_PUSH_ORACLE_ID; #[repr(u8)] #[cfg_attr(any(feature = "test", feature = "client"), derive(PartialEq, Eq))] #[derive(Copy, Clone, Debug, AnchorSerialize, AnchorDeserialize)] pub enum OracleSetup { None, - PythEma, + PythLegacy, SwitchboardV2, + PythPushOracle, } #[derive(Copy, Clone, Debug)] @@ -56,20 +62,21 @@ pub trait PriceAdapter { #[enum_dispatch(PriceAdapter)] #[cfg_attr(feature = "client", derive(Clone))] pub enum OraclePriceFeedAdapter { - PythEma(PythEmaPriceFeed), + PythLegacy(PythLegacyPriceFeed), SwitchboardV2(SwitchboardV2PriceFeed), + PythPushOracle(PythPushOraclePriceFeed), } impl OraclePriceFeedAdapter { pub fn try_from_bank_config( bank_config: &BankConfig, ais: &[AccountInfo], - current_timestamp: i64, + clock: &Clock, ) -> MarginfiResult { Self::try_from_bank_config_with_max_age( bank_config, ais, - current_timestamp, + clock, bank_config.get_oracle_max_age(), ) } @@ -77,13 +84,12 @@ impl OraclePriceFeedAdapter { pub fn try_from_bank_config_with_max_age( bank_config: &BankConfig, ais: &[AccountInfo], - current_timestamp: i64, + clock: &Clock, max_age: u64, ) -> MarginfiResult { - debug!("Max age: {}", max_age); match bank_config.oracle_setup { OracleSetup::None => Err(MarginfiError::OracleNotSetup.into()), - OracleSetup::PythEma => { + OracleSetup::PythLegacy => { check!(ais.len() == 1, MarginfiError::InvalidOracleAccount); check!( ais[0].key == &bank_config.oracle_keys[0], @@ -92,8 +98,8 @@ impl OraclePriceFeedAdapter { let account_info = &ais[0]; - Ok(OraclePriceFeedAdapter::PythEma( - PythEmaPriceFeed::load_checked(account_info, current_timestamp, max_age)?, + Ok(OraclePriceFeedAdapter::PythLegacy( + PythLegacyPriceFeed::load_checked(account_info, clock.unix_timestamp, max_age)?, )) } OracleSetup::SwitchboardV2 => { @@ -104,7 +110,28 @@ impl OraclePriceFeedAdapter { ); Ok(OraclePriceFeedAdapter::SwitchboardV2( - SwitchboardV2PriceFeed::load_checked(&ais[0], current_timestamp, max_age)?, + SwitchboardV2PriceFeed::load_checked(&ais[0], clock.unix_timestamp, max_age)?, + )) + } + OracleSetup::PythPushOracle => { + check!(ais.len() == 1, MarginfiError::InvalidOracleAccount); + + let account_info = &ais[0]; + + check!( + account_info.owner == &pyth_solana_receiver_sdk::id(), + MarginfiError::InvalidOracleAccount + ); + + let price_feed_id = bank_config.get_pyth_push_oracle_feed_id().unwrap(); + + Ok(OraclePriceFeedAdapter::PythPushOracle( + PythPushOraclePriceFeed::load_checked( + account_info, + price_feed_id, + clock, + max_age, + )?, )) } } @@ -116,14 +143,14 @@ impl OraclePriceFeedAdapter { ) -> MarginfiResult { match bank_config.oracle_setup { OracleSetup::None => Err(MarginfiError::OracleNotSetup.into()), - OracleSetup::PythEma => { + OracleSetup::PythLegacy => { check!(oracle_ais.len() == 1, MarginfiError::InvalidOracleAccount); check!( oracle_ais[0].key == &bank_config.oracle_keys[0], MarginfiError::InvalidOracleAccount ); - PythEmaPriceFeed::check_ais(&oracle_ais[0])?; + PythLegacyPriceFeed::check_ais(&oracle_ais[0])?; Ok(()) } @@ -136,6 +163,16 @@ impl OraclePriceFeedAdapter { SwitchboardV2PriceFeed::check_ais(&oracle_ais[0])?; + Ok(()) + } + OracleSetup::PythPushOracle => { + check!(oracle_ais.len() == 1, MarginfiError::InvalidOracleAccount); + + PythPushOraclePriceFeed::check_ai_and_feed_id( + &oracle_ais[0], + bank_config.get_pyth_push_oracle_feed_id().unwrap(), + )?; + Ok(()) } } @@ -143,20 +180,15 @@ impl OraclePriceFeedAdapter { } #[cfg_attr(feature = "client", derive(Clone, Debug))] -pub struct PythEmaPriceFeed { +pub struct PythLegacyPriceFeed { ema_price: Box, price: Box, } -impl PythEmaPriceFeed { +impl PythLegacyPriceFeed { pub fn load_checked(ai: &AccountInfo, current_time: i64, max_age: u64) -> MarginfiResult { let price_feed = load_pyth_price_feed(ai)?; - debug!( - "Oracle age: {}s", - price_feed.get_price_unchecked().publish_time - current_time - ); - let ema_price = price_feed .get_ema_price_no_older_than(current_time, max_age) .ok_or(MarginfiError::StaleOracle)?; @@ -219,7 +251,7 @@ impl PythEmaPriceFeed { } } -impl PriceAdapter for PythEmaPriceFeed { +impl PriceAdapter for PythLegacyPriceFeed { fn get_price_of_type( &self, price_type: OraclePriceType, @@ -299,13 +331,13 @@ impl SwitchboardV2PriceFeed { .get_result() .map_err(|_| MarginfiError::InvalidPrice)?; - Ok(swithcboard_decimal_to_i80f48(sw_decimal) + Ok(switchboard_decimal_to_i80f48(sw_decimal) .ok_or(MarginfiError::InvalidSwitchboardDecimalConversion)?) } fn get_confidence_interval(&self) -> MarginfiResult { let std_div = self.aggregator_account.latest_confirmed_round_std_deviation; - let std_div = swithcboard_decimal_to_i80f48(std_div) + let std_div = switchboard_decimal_to_i80f48(std_div) .ok_or(MarginfiError::InvalidSwitchboardDecimalConversion)?; let conf_interval = std_div @@ -358,11 +390,247 @@ impl PriceAdapter for SwitchboardV2PriceFeed { } } +pub fn load_price_update_v2_checked(ai: &AccountInfo) -> MarginfiResult { + check!( + ai.owner.eq(&pyth_solana_receiver_sdk::id()), + MarginfiError::InvalidOracleAccount + ); + + let price_feed_data = ai.try_borrow_data()?; + let discriminator = &price_feed_data[0..8]; + + check!( + discriminator == ::DISCRIMINATOR, + MarginfiError::InvalidOracleAccount + ); + + Ok(PriceUpdateV2::deserialize( + &mut &price_feed_data.as_ref()[8..], + )?) +} + +#[cfg_attr(feature = "client", derive(Clone, Debug))] +pub struct PythPushOraclePriceFeed { + ema_price: Box, + price: Box, +} + +impl PythPushOraclePriceFeed { + /// Pyth push oracles are update using crosschain messages from pythnet + /// There can be multiple pyth push oracles for a given feed_id. Marginfi allows using any + /// pyth push oracle with a sufficient verification level and price age. + /// + /// Meaning that when loading the pyth push oracle, we don't verify the account address + /// directly, but rather we verify the feed_id in the oracle data. + /// + /// Security assumptions: + /// - The pyth-push-oracle account is owned by the pyth-solana-receiver program, checked in `load_price_update_v2_checked` + /// - The pyth-push-oracle account is a PriceUpdateV2 account, checked in `load_price_update_v2_checked` + /// - The pyth-push-oracle account has a minimum verification level, checked in `get_price_no_older_than_with_custom_verification_level` + /// - The pyth-push-oracle account has a valid feed_id, the pyth-solana-receiver program enforces that the feed_id matches the pythnet feed_id, checked in + /// - `get_price_no_older_than_with_custom_verification_level` checks against the feed_id stored in the bank_config + /// - pyth-push-oracle asserts the a valid price update has a matching feed_id with the existing pyth-push-oracle update https://github.com/pyth-network/pyth-crosschain/blob/94f1bd54612adc3e186eaf0bb0f1f705880f20a6/target_chains/solana/programs/pyth-push-oracle/src/lib.rs#L101 + /// - pyth-solana-receiver set the feed_id directly from a pythnet verified price_update message https://github.com/pyth-network/pyth-crosschain/blob/94f1bd54612adc3e186eaf0bb0f1f705880f20a6/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs#L437 + /// - The pyth-push-oracle account is not older than the max_age, checked in `get_price_no_older_than_with_custom_verification_level` + pub fn load_checked( + ai: &AccountInfo, + feed_id: &FeedId, + clock: &Clock, + max_age: u64, + ) -> MarginfiResult { + let price_feed_account = load_price_update_v2_checked(ai)?; + + let price = price_feed_account + .get_price_no_older_than_with_custom_verification_level( + clock, + max_age, + feed_id, + MIN_PYTH_PUSH_VERIFICATION_LEVEL, + ) + .map_err(|e| { + debug!("Pyth push oracle error: {:?}", e); + + match e { + pyth_solana_receiver_sdk::error::GetPriceError::PriceTooOld => { + MarginfiError::StaleOracle + } + _ => MarginfiError::InvalidOracleAccount, + } + })?; + + let ema_price = { + let price_update::PriceFeedMessage { + exponent, + publish_time, + ema_price, + ema_conf, + .. + } = price_feed_account.price_message; + + pyth_solana_receiver_sdk::price_update::Price { + price: ema_price, + conf: ema_conf, + exponent, + publish_time, + } + }; + + Ok(Self { + price: Box::new(price), + ema_price: Box::new(ema_price), + }) + } + + #[cfg(feature = "client")] + pub fn load_unchecked(ai: &AccountInfo) -> MarginfiResult { + let price_feed_account = load_price_update_v2_checked(ai)?; + + let price = price_feed_account + .get_price_unchecked(&price_feed_account.price_message.feed_id) + .map_err(|e| { + println!("Pyth push oracle error: {:?}", e); + + match e { + pyth_solana_receiver_sdk::error::GetPriceError::PriceTooOld => { + MarginfiError::StaleOracle + } + _ => MarginfiError::InvalidOracleAccount, + } + })?; + + let ema_price = { + let price_update::PriceFeedMessage { + exponent, + publish_time, + ema_price, + ema_conf, + .. + } = price_feed_account.price_message; + + pyth_solana_receiver_sdk::price_update::Price { + price: ema_price, + conf: ema_conf, + exponent, + publish_time, + } + }; + + Ok(Self { + price: Box::new(price), + ema_price: Box::new(ema_price), + }) + } + + #[cfg(feature = "client")] + pub fn peek_feed_id(ai: &AccountInfo) -> MarginfiResult { + let price_feed_account = load_price_update_v2_checked(ai)?; + + Ok(price_feed_account.price_message.feed_id) + } + + pub fn check_ai_and_feed_id(ai: &AccountInfo, feed_id: &FeedId) -> MarginfiResult { + let price_feed_account = load_price_update_v2_checked(ai)?; + + check!( + &price_feed_account.price_message.feed_id.eq(feed_id), + MarginfiError::InvalidOracleAccount + ); + + Ok(()) + } + + fn get_confidence_interval(&self, use_ema: bool) -> MarginfiResult { + let price = if use_ema { + &self.ema_price + } else { + &self.price + }; + + let conf_interval = + pyth_price_components_to_i80f48(I80F48::from_num(price.conf), price.exponent)? + .checked_mul(CONF_INTERVAL_MULTIPLE) + .ok_or_else(math_error!())?; + + // Cap confidence interval to 5% of price + let price = pyth_price_components_to_i80f48(I80F48::from_num(price.price), price.exponent)?; + + let max_conf_interval = price + .checked_mul(MAX_CONF_INTERVAL) + .ok_or_else(math_error!())?; + + assert!( + max_conf_interval >= I80F48::ZERO, + "Negative max confidence interval" + ); + + assert!( + conf_interval >= I80F48::ZERO, + "Negative confidence interval" + ); + + Ok(min(conf_interval, max_conf_interval)) + } + + #[inline(always)] + fn get_ema_price(&self) -> MarginfiResult { + pyth_price_components_to_i80f48( + I80F48::from_num(self.ema_price.price), + self.ema_price.exponent, + ) + } + + #[inline(always)] + fn get_unweighted_price(&self) -> MarginfiResult { + pyth_price_components_to_i80f48(I80F48::from_num(self.price.price), self.price.exponent) + } + + /// Find PDA address of a pyth push oracle given a shard_id and feed_id + /// + /// Pyth sponsored feed id + /// `constants::PYTH_PUSH_PYTH_SPONSORED_SHARD_ID = 0` + /// + /// Marginfi sponsored feed id + /// `constants::PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID = 3301` + pub fn find_oracle_address(shard_id: u16, feed_id: &FeedId) -> (Pubkey, u8) { + Pubkey::find_program_address(&[&shard_id.to_le_bytes(), feed_id], &PYTH_PUSH_ORACLE_ID) + } +} + +impl PriceAdapter for PythPushOraclePriceFeed { + fn get_price_of_type( + &self, + price_type: OraclePriceType, + bias: Option, + ) -> MarginfiResult { + let price = match price_type { + OraclePriceType::TimeWeighted => self.get_ema_price()?, + OraclePriceType::RealTime => self.get_unweighted_price()?, + }; + + match bias { + None => Ok(price), + Some(price_bias) => { + let confidence_interval = self + .get_confidence_interval(matches!(price_type, OraclePriceType::TimeWeighted))?; + + match price_bias { + PriceBias::Low => Ok(price + .checked_sub(confidence_interval) + .ok_or_else(math_error!())?), + PriceBias::High => Ok(price + .checked_add(confidence_interval) + .ok_or_else(math_error!())?), + } + } + } + } +} + /// A slimmed down version of the AggregatorAccountData struct copied from the switchboard-v2/src/aggregator.rs #[cfg_attr(feature = "client", derive(Clone, Debug))] struct LiteAggregatorAccountData { /// Use sliding windoe or round based resolution - /// NOTE: This changes result propogation in latest_round_result + /// NOTE: This changes result propagation in latest_round_result pub resolution_mode: AggregatorResolutionMode, /// Latest confirmed update request result that has been accepted as valid. pub latest_confirmed_round_result: SwitchboardDecimal, @@ -396,6 +664,7 @@ impl LiteAggregatorAccountData { /// let feed_result = AggregatorAccountData::new(feed_account_info)?.get_result()?; /// let decimal: f64 = feed_result.try_into()?; /// ``` + pub fn get_result(&self) -> anchor_lang::Result { if self.resolution_mode == AggregatorResolutionMode::ModeSlidingResolution { return Ok(self.latest_confirmed_round_result); @@ -431,13 +700,13 @@ fn pyth_price_components_to_i80f48(price: I80F48, exponent: i32) -> MarginfiResu /// Load and validate a pyth price feed account. fn load_pyth_price_feed(ai: &AccountInfo) -> MarginfiResult { check!(ai.owner.eq(&PYTH_ID), MarginfiError::InvalidOracleAccount); - let price_feed = - load_price_feed_from_account_info(ai).map_err(|_| MarginfiError::InvalidOracleAccount)?; + let price_feed = SolanaPriceAccount::account_info_to_feed(ai) + .map_err(|_| MarginfiError::InvalidOracleAccount)?; Ok(price_feed) } #[inline(always)] -fn swithcboard_decimal_to_i80f48(decimal: SwitchboardDecimal) -> Option { +fn switchboard_decimal_to_i80f48(decimal: SwitchboardDecimal) -> Option { let decimal = fit_scale_switchboard_decimal(decimal, MAX_SCALE)?; I80F48::from_num(decimal.mantissa).checked_div(EXP_10_I80F48[decimal.scale as usize]) @@ -467,6 +736,7 @@ fn fit_scale_switchboard_decimal( #[cfg(test)] mod tests { use fixed_macro::types::I80F48; + use pretty_assertions::assert_eq; use rust_decimal::Decimal; use super::*; @@ -476,7 +746,7 @@ mod tests { mantissa: 1000000000000000000, scale: 18, }; - let i80f48 = swithcboard_decimal_to_i80f48(decimal).unwrap(); + let i80f48 = switchboard_decimal_to_i80f48(decimal).unwrap(); assert_eq!(i80f48, I80F48::from_num(1)); } @@ -493,7 +763,7 @@ mod tests { println!("control check: {:?}", decimal); } - let i80f48 = swithcboard_decimal_to_i80f48(dec).unwrap(); + let i80f48 = switchboard_decimal_to_i80f48(dec).unwrap(); assert_eq!(i80f48, I80F48::from_num(0.00139429375)); } @@ -517,7 +787,7 @@ mod tests { }); // Initialize PythEmaPriceFeed with high confidence price as EMA - let pyth_adapter = PythEmaPriceFeed { + let pyth_adapter = PythLegacyPriceFeed { ema_price: high_confidence_price, price: low_confidence_price, }; @@ -572,4 +842,183 @@ mod tests { assert_eq!(low_conf_interval, I80F48!(1.96)); } + + #[test] + fn pyth_and_pyth_push_cmp() { + fn get_prices( + price: i64, + conf: u64, + ) -> (Price, pyth_solana_receiver_sdk::price_update::Price) { + let legacy_price = Price { + price, + conf, + expo: -6, + publish_time: 0, + }; + + let push_price = pyth_solana_receiver_sdk::price_update::Price { + price, + conf, + exponent: -6, + publish_time: 0, + }; + + assert_eq!(legacy_price.price, push_price.price); + assert_eq!(legacy_price.conf, push_price.conf); + assert_eq!(legacy_price.expo, push_price.exponent); + assert_eq!(legacy_price.publish_time, push_price.publish_time); + + (legacy_price, push_price) + } + + let (legacy_price, push_price) = + get_prices(100i64 * EXP_10[6] as i64, 10u64 * EXP_10[6] as u64); + + let (legacy_ema, push_price_ema) = + get_prices(99i64 * EXP_10[6] as i64, 4u64 * EXP_10[6] as u64); + + let pyth_legacy = PythLegacyPriceFeed { + ema_price: Box::new(legacy_ema), + price: Box::new(legacy_price), + }; + + let pyth_push = PythPushOraclePriceFeed { + ema_price: Box::new(push_price_ema), + price: Box::new(push_price), + }; + + assert_eq!( + pyth_legacy.get_ema_price().unwrap(), + pyth_push.get_ema_price().unwrap() + ); + assert_eq!( + pyth_legacy.get_unweighted_price().unwrap(), + pyth_push.get_unweighted_price().unwrap() + ); + + assert_eq!( + pyth_legacy.get_confidence_interval(true).unwrap(), + pyth_push.get_confidence_interval(true).unwrap() + ); + + assert_eq!( + pyth_legacy.get_confidence_interval(false).unwrap(), + pyth_push.get_confidence_interval(false).unwrap() + ); + + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low)) + .unwrap() + ); + + // Test high bias ema + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::High)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::High)) + .unwrap() + ); + + // Test low bias ema + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low)) + .unwrap() + ); + + // Test no bias real time + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::RealTime, None) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::RealTime, None) + .unwrap() + ); + + // new pricees with very wide confidence + let (legacy_price, push_price) = + get_prices(100i64 * EXP_10[6] as i64, 100u64 * EXP_10[6] as u64); + + let (legacy_ema, push_price_ema) = + get_prices(99i64 * EXP_10[6] as i64, 88u64 * EXP_10[6] as u64); + + let pyth_legacy = PythLegacyPriceFeed { + ema_price: Box::new(legacy_ema), + price: Box::new(legacy_price), + }; + + let pyth_push = PythPushOraclePriceFeed { + ema_price: Box::new(push_price_ema), + price: Box::new(push_price), + }; + + // Test high bias ema + assert_eq!( + pyth_legacy.get_ema_price().unwrap(), + pyth_push.get_ema_price().unwrap() + ); + assert_eq!( + pyth_legacy.get_unweighted_price().unwrap(), + pyth_push.get_unweighted_price().unwrap() + ); + + assert_eq!( + pyth_legacy.get_confidence_interval(true).unwrap(), + pyth_push.get_confidence_interval(true).unwrap() + ); + + assert_eq!( + pyth_legacy.get_confidence_interval(false).unwrap(), + pyth_push.get_confidence_interval(false).unwrap() + ); + + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low)) + .unwrap() + ); + + // Test high bias ema + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::High)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::High)) + .unwrap() + ); + + // Test low bias ema + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low)) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low)) + .unwrap() + ); + + // Test no bias real time + assert_eq!( + pyth_legacy + .get_price_of_type(OraclePriceType::RealTime, None) + .unwrap(), + pyth_push + .get_price_of_type(OraclePriceType::RealTime, None) + .unwrap() + ); + } } diff --git a/programs/marginfi/src/utils.rs b/programs/marginfi/src/utils.rs index 6d9e67912..11bace1c9 100644 --- a/programs/marginfi/src/utils.rs +++ b/programs/marginfi/src/utils.rs @@ -1,5 +1,19 @@ -use crate::{bank_authority_seed, bank_seed, state::marginfi_group::BankVaultType}; -use anchor_lang::prelude::Pubkey; +use crate::{ + bank_authority_seed, bank_seed, state::marginfi_group::BankVaultType, MarginfiError, + MarginfiResult, +}; +use anchor_lang::prelude::*; +use anchor_spl::{ + token::Token, + token_2022::spl_token_2022::{ + self, + extension::{ + transfer_fee::{TransferFee, TransferFeeConfig}, + BaseStateWithExtensions, StateWithExtensions, + }, + }, + token_interface::Mint, +}; use fixed::types::I80F48; pub fn find_bank_vault_pda(bank_pk: &Pubkey, vault_type: BankVaultType) -> (Pubkey, u8) { @@ -27,3 +41,139 @@ where self.gt(&t) } } + +pub fn calculate_pre_fee_spl_deposit_amount( + mint_ai: AccountInfo, + post_fee_amount: u64, + epoch: u64, +) -> MarginfiResult { + if mint_ai.owner.eq(&Token::id()) { + return Ok(post_fee_amount); + } + + let mint_data = mint_ai.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + match mint.get_extension::() { + Ok(transfer_fee_config) => { + let epoch_fee = transfer_fee_config.get_epoch_fee(epoch); + let pre_fee_amount = calculate_pre_fee_amount(epoch_fee, post_fee_amount).unwrap(); + Ok(pre_fee_amount) + } + Err(_) => Ok(post_fee_amount), + } +} + +pub fn calculate_post_fee_spl_deposit_amount( + mint_ai: AccountInfo, + input_amount: u64, + epoch: u64, +) -> MarginfiResult { + if mint_ai.owner.eq(&Token::id()) { + return Ok(input_amount); + } + + let mint_data = mint_ai.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + let fee = if let Ok(transfer_fee_config) = mint.get_extension::() { + transfer_fee_config + .calculate_epoch_fee(epoch, input_amount) + .unwrap() + } else { + 0 + }; + + let output_amount = input_amount + .checked_sub(fee) + .ok_or(MarginfiError::MathError)?; + + Ok(output_amount) +} + +pub fn nonzero_fee(mint_ai: AccountInfo, epoch: u64) -> MarginfiResult { + if mint_ai.owner.eq(&Token::id()) { + return Ok(false); + } + + let mint_data = mint_ai.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + + if let Ok(transfer_fee_config) = mint.get_extension::() { + return Ok(u16::from( + transfer_fee_config + .get_epoch_fee(epoch) + .transfer_fee_basis_points, + ) != 0); + } + + Ok(false) +} + +/// Checks if first account is a mint account. If so, updates remaining_account -> &remaining_account[1..] +/// +/// Ok(None) if Tokenkeg +pub fn maybe_take_bank_mint<'c: 'info, 'info>( + remaining_accounts: &mut &'c [AccountInfo<'info>], + bank_mint: &Pubkey, + token_program: &Pubkey, +) -> MarginfiResult>> { + match *token_program { + anchor_spl::token::ID => Ok(None), + anchor_spl::token_2022::ID => { + let (maybe_mint, remaining) = remaining_accounts + .split_first() + .ok_or(MarginfiError::T22MintRequired)?; + *remaining_accounts = remaining; + + if *bank_mint != *maybe_mint.key { + return err!(MarginfiError::T22MintRequired); + } + + InterfaceAccount::try_from(maybe_mint) + .map(Option::Some) + .map_err(|e| { + msg!("failed to parse mint account: {:?}", e); + MarginfiError::T22MintRequired.into() + }) + } + + _ => panic!("unsupported token program"), + } +} + +const ONE_IN_BASIS_POINTS: u128 = 10_000; +/// backported fix from +/// https://github.com/solana-labs/solana-program-library/commit/20e6792179fc7f1251579c1c33a4a0feec48e15e +pub fn calculate_pre_fee_amount(transfer_fee: &TransferFee, post_fee_amount: u64) -> Option { + let maximum_fee = u64::from(transfer_fee.maximum_fee); + let transfer_fee_basis_points = u16::from(transfer_fee.transfer_fee_basis_points) as u128; + match (transfer_fee_basis_points, post_fee_amount) { + // no fee, same amount + (0, _) => Some(post_fee_amount), + // 0 zero out, 0 in + (_, 0) => Some(0), + // 100%, cap at max fee + (ONE_IN_BASIS_POINTS, _) => maximum_fee.checked_add(post_fee_amount), + _ => { + let numerator = (post_fee_amount as u128).checked_mul(ONE_IN_BASIS_POINTS)?; + let denominator = ONE_IN_BASIS_POINTS.checked_sub(transfer_fee_basis_points)?; + let raw_pre_fee_amount = ceil_div(numerator, denominator)?; + + if raw_pre_fee_amount.checked_sub(post_fee_amount as u128)? >= maximum_fee as u128 { + post_fee_amount.checked_add(maximum_fee) + } else { + // should return `None` if `pre_fee_amount` overflows + u64::try_from(raw_pre_fee_amount).ok() + } + } + } +} + +// Private function from spl-program-library +fn ceil_div(numerator: u128, denominator: u128) -> Option { + numerator + .checked_add(denominator)? + .checked_sub(1)? + .checked_div(denominator) +} diff --git a/programs/marginfi/tests/admin_actions/account_transfer.rs b/programs/marginfi/tests/admin_actions/account_transfer.rs new file mode 100644 index 000000000..063563a9f --- /dev/null +++ b/programs/marginfi/tests/admin_actions/account_transfer.rs @@ -0,0 +1,72 @@ +use fixtures::{assert_custom_error, test::TestFixture}; +use marginfi::{errors::MarginfiError, state::marginfi_account::TRANSFER_AUTHORITY_ALLOWED_FLAG}; +use solana_program_test::tokio; +use solana_sdk::{signature::Keypair, signer::Signer}; + +// Test transfer account authority. +// No transfer flag set -- tx should fail. +// Set the flag and try again -- tx should succeed. +// RUST_BACKTRACE=1 cargo test-bpf marginfi_account_authority_transfer_no_flag_set -- --exact +#[tokio::test] +async fn marginfi_account_authority_transfer_no_flag_set() -> anyhow::Result<()> { + let test_f = TestFixture::new(None).await; + // Default account with no flags set + let marginfi_account = test_f.create_marginfi_account().await; + let new_authority = Keypair::new().pubkey(); + + let res = marginfi_account + .try_transfer_account_authority(new_authority, None) + .await; + + // Check transfer authority is unchanged + let account = marginfi_account.load().await; + assert_eq!(account.authority, test_f.payer()); + + // Assert the response is an error due to the lack of the correct flag + assert!(res.is_err()); + assert_custom_error!( + res.unwrap_err(), + MarginfiError::IllegalAccountAuthorityTransfer + ); + + // set the flag on the account + marginfi_account + .try_set_flag(TRANSFER_AUTHORITY_ALLOWED_FLAG) + .await + .unwrap(); + + // Check transfer authority flag + let account = marginfi_account.load().await; + assert!(account.get_flag(TRANSFER_AUTHORITY_ALLOWED_FLAG)); + + let new_authority_2 = Keypair::new().pubkey(); + let res = marginfi_account + .try_transfer_account_authority(new_authority_2, None) + .await; + + assert!(res.is_ok()); + + // Check transfer authority + let account = marginfi_account.load().await; + assert_eq!(account.authority, new_authority_2); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_authority_transfer_not_account_owner() -> anyhow::Result<()> { + let test_f = TestFixture::new(None).await; + let marginfi_account = test_f.create_marginfi_account().await; + let new_authority = Keypair::new().pubkey(); + let signer = Keypair::new(); + + let res = marginfi_account + .try_transfer_account_authority(new_authority, Some(signer)) + .await; + + // Assert the response is an error due to fact that a non-owner of the + // acount attempted to initialize this account transfer + assert!(res.is_err()); + + Ok(()) +} diff --git a/programs/marginfi/tests/admin_actions/bankruptcy.rs b/programs/marginfi/tests/admin_actions/bankruptcy.rs new file mode 100644 index 000000000..7c72fef2b --- /dev/null +++ b/programs/marginfi/tests/admin_actions/bankruptcy.rs @@ -0,0 +1,941 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{assert_custom_error, assert_eq_noise, native, prelude::*}; +use marginfi::{ + prelude::{GroupConfig, MarginfiError}, + state::marginfi_group::{BankConfig, BankVaultType}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use test_case::test_case; + +#[test_case(BankMint::Usdc, BankMint::Sol)] +#[test_case(BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(BankMint::Sol, BankMint::Usdc)] +#[test_case(BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_failure_not_bankrupt( + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let borrow_amount = 10_000.; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank_f = test_f.get_bank(&debt_mint); + + let res = test_f + .marginfi_group + .try_handle_bankruptcy(debt_bank_f, &user_mfi_account_f) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::AccountNotBankrupt); + + Ok(()) +} + +#[test_case(BankMint::Usdc, BankMint::Sol)] +#[test_case(BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(BankMint::Sol, BankMint::Usdc)] +#[test_case(BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_failure_no_debt( + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let borrow_amount = 10_000.; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let collateral_bank_f = test_f.get_bank(&collateral_mint); + + // Artificially nullify the collateral to place the account in a bankrupt state + let mut user_mfi_account = user_mfi_account_f.load().await; + user_mfi_account.lending_account.balances[0] + .asset_shares + .value = 0_i128.to_le_bytes(); + user_mfi_account_f.set_account(&user_mfi_account).await?; + + let res = test_f + .marginfi_group + .try_handle_bankruptcy(collateral_bank_f, &user_mfi_account_f) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::BalanceNotBadDebt); + + Ok(()) +} + +#[test_case(BankMint::Usdc, BankMint::Sol)] +#[test_case(BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(BankMint::Sol, BankMint::Usdc)] +#[test_case(BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_success( + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let borrow_amount = 10_000.; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + // Artificially nullify the collateral to place the account in a bankrupt state + let mut user_mfi_account = user_mfi_account_f.load().await; + user_mfi_account.lending_account.balances[0] + .asset_shares + .value = 0_i128.to_le_bytes(); + user_mfi_account_f.set_account(&user_mfi_account).await?; + + let debt_bank_f = test_f.get_bank(&debt_mint); + + let res = test_f + .marginfi_group + .try_handle_bankruptcy(debt_bank_f, &user_mfi_account_f) + .await; + assert!(res.is_ok()); + + Ok(()) +} + +#[test_case(10_000., BankMint::Usdc, BankMint::Sol)] +#[test_case(10_000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(10_000., BankMint::Sol, BankMint::Usdc)] +#[test_case(10_000., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(10_000., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(10_000., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_success_fully_insured( + borrow_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + assert_eq!( + user_debt_token_account_f.balance().await, + native!( + borrow_amount, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + ) + ); + + // Artificially nullify the collateral to place the account in a bankrupt state + let mut user_mfi_account = user_mfi_account_f.load().await; + user_mfi_account.lending_account.balances[0].asset_shares = I80F48::ZERO.into(); + user_mfi_account_f.set_account(&user_mfi_account).await?; + + { + let (insurance_vault, _) = test_f + .get_bank(&debt_mint) + .get_vault(BankVaultType::Insurance); + let max_amount_to_cover_bad_debt = get_max_deposit_amount_pre_fee(borrow_amount); + + test_f + .get_bank_mut(&debt_mint) + .mint + .mint_to(&insurance_vault, max_amount_to_cover_bad_debt) + .await; + } + + let debt_bank = test_f.get_bank(&debt_mint); + + let (pre_liquidity_vault_balance, pre_insurance_vault_balance) = ( + debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await, + debt_bank + .get_vault_token_account(BankVaultType::Insurance) + .await + .balance() + .await, + ); + + test_f + .marginfi_group + .try_handle_bankruptcy(test_f.get_bank(&debt_mint), &user_mfi_account_f) + .await?; + + let (post_liquidity_vault_balance, post_insurance_vault_balance) = ( + debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await, + debt_bank + .get_vault_token_account(BankVaultType::Insurance) + .await + .balance() + .await, + ); + + let user_mfi_account = user_mfi_account_f.load().await; + let user_collateral_balance = user_mfi_account.lending_account.balances[1]; + + // Check that all user debt has been covered + assert_eq!( + I80F48::from(user_collateral_balance.liability_shares), + I80F48::ZERO + ); + + let lp_mfi_account = lp_mfi_account_f.load().await; + let debt_bank = test_f.get_bank(&debt_mint).load().await; + + let lp_collateral_value = debt_bank.get_asset_amount( + lp_mfi_account.lending_account.balances[0] + .asset_shares + .into(), + )?; + + // Check that no loss was socialized + assert_eq_noise!( + lp_collateral_value, + I80F48::from(native!( + lp_deposit_amount, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + )), + I80F48::ONE + ); + + let debt_bank_mint_state = test_f.get_bank(&debt_mint).mint.load_state().await; + + let borrow_amount_native = native!( + borrow_amount, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + ); + let actual_borrow_position = borrow_amount_native + + debt_bank_mint_state + .get_extension::() + .map(|tf| { + tf.calculate_inverse_epoch_fee(0, borrow_amount_native) + .unwrap_or(0) + }) + .unwrap_or(0); + + let expected_insurance_vault_delta = I80F48::from( + actual_borrow_position + + debt_bank_mint_state + .get_extension::() + .map(|tf| { + tf.calculate_inverse_epoch_fee(0, actual_borrow_position) + .unwrap_or(0) + }) + .unwrap_or(0), + ); + let expected_liquidity_vault_delta = I80F48::from(actual_borrow_position); + + let actual_liquidity_vault_delta = post_liquidity_vault_balance - pre_liquidity_vault_balance; + let actual_insurance_vault_delta = pre_insurance_vault_balance - post_insurance_vault_balance; + + assert_eq!(expected_insurance_vault_delta, actual_insurance_vault_delta); + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + + // Test account is disabled + + // Deposit 1 SOL + let res = user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + 1, + ) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); + + // Withdraw 1 SOL + let res = user_mfi_account_f + .try_bank_withdraw( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + 1, + None, + ) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); + + // Borrow 1 USDC + let res = user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + 1, + ) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); + + // Repay 1 USDC + let res = user_mfi_account_f + .try_bank_repay( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + 1, + None, + ) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); + + Ok(()) +} + +#[test_case(10_000., 5000., BankMint::Usdc, BankMint::Sol)] +#[test_case(10_000., 5000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(10_000., 5000., BankMint::Sol, BankMint::Usdc)] +#[test_case(10_000., 5000., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(10_000., 5000., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(10_000., 5000., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_success_partially_insured( + borrow_amount: f64, + initial_insurance_vault_balance: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + // Artificially nullify the collateral to place the account in a bankrupt state + let mut user_mfi_account = user_mfi_account_f.load().await; + user_mfi_account.lending_account.balances[0].asset_shares = I80F48::ZERO.into(); + user_mfi_account_f.set_account(&user_mfi_account).await?; + + // Load up the insurance vault with the requested balance + let insurance_vault = test_f.get_bank(&debt_mint).load().await.insurance_vault; + test_f + .get_bank_mut(&debt_mint) + .mint + .mint_to(&insurance_vault, initial_insurance_vault_balance) + .await; + + let debt_bank_f = test_f.get_bank(&debt_mint); + let debt_bank = test_f.get_bank(&debt_mint).load().await; + let collateral_bank = test_f.get_bank(&collateral_mint).load().await; + + let (pre_lp_collateral_amount, pre_user_debt_amount, pre_liquidity_vault_balance) = ( + collateral_bank.get_liability_amount( + lp_mfi_account_f.load().await.lending_account.balances[0] + .asset_shares + .into(), + )?, + debt_bank.get_liability_amount( + user_mfi_account_f.load().await.lending_account.balances[1] + .liability_shares + .into(), + )?, + debt_bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await, + ); + + test_f + .marginfi_group + .try_handle_bankruptcy(test_f.get_bank(&debt_mint), &user_mfi_account_f) + .await?; + + let borrower_mfi_account = user_mfi_account_f.load().await; + let (post_user_debt_balance, post_liquidity_vault_balance) = ( + borrower_mfi_account.lending_account.balances[1], + debt_bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await, + ); + + assert_eq!( + I80F48::from(post_user_debt_balance.liability_shares), + I80F48::ZERO + ); + + let lp_mfi_account = lp_mfi_account_f.load().await; + let debt_bank = test_f.get_bank(&debt_mint).load().await; + + let actual_post_lender_collateral_amount = debt_bank.get_asset_amount( + lp_mfi_account.lending_account.balances[0] + .asset_shares + .into(), + )?; + + let debt_bank_mint_state = test_f.get_bank(&debt_mint).mint.load_state().await; + + let initial_insurance_vault_balance_native = native!( + initial_insurance_vault_balance, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + ); + let insurance_fund_fee = debt_bank_mint_state + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, initial_insurance_vault_balance_native) + .unwrap_or(0) + }) + .unwrap_or(0); + + let available_insurance_amount = initial_insurance_vault_balance_native - insurance_fund_fee; + + let amount_not_covered = pre_user_debt_amount - I80F48::from(available_insurance_amount); + + let expected_post_lender_collateral_amount = pre_lp_collateral_amount - amount_not_covered; + assert_eq_noise!( + expected_post_lender_collateral_amount, + actual_post_lender_collateral_amount, + I80F48::ONE + ); + + let insurance_amount = test_f + .get_bank(&debt_mint) + .get_vault_token_account(BankVaultType::Insurance) + .await; + + assert_eq!(insurance_amount.balance().await, 0); + + let actual_liquidity_vault_delta = post_liquidity_vault_balance - pre_liquidity_vault_balance; + assert_eq!(available_insurance_amount, actual_liquidity_vault_delta); + + Ok(()) +} + +#[test_case(10_000., BankMint::Usdc, BankMint::Sol)] +#[test_case(10_000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(10_000., BankMint::Sol, BankMint::Usdc)] +#[test_case(10_000., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(10_000., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(10_000., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_success_not_insured( + borrow_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + // Artificially nullify the collateral to place the account in a bankrupt state + let mut user_mfi_account = user_mfi_account_f.load().await; + user_mfi_account.lending_account.balances[0].asset_shares = I80F48::ZERO.into(); + user_mfi_account_f.set_account(&user_mfi_account).await?; + + test_f + .marginfi_group + .try_handle_bankruptcy(test_f.get_bank(&debt_mint), &user_mfi_account_f) + .await?; + + let user_mfi_account = user_mfi_account_f.load().await; + let user_collateral_balance = user_mfi_account.lending_account.balances[1]; + + assert_eq!( + I80F48::from(user_collateral_balance.liability_shares), + I80F48::ZERO + ); + + let lp_mfi_account = lp_mfi_account_f.load().await; + let debt_bank_f = test_f.get_bank(&debt_mint); + let debt_bank = debt_bank_f.load().await; + + let actual_lender_collateral_amount = debt_bank.get_asset_amount( + lp_mfi_account.lending_account.balances[0] + .asset_shares + .into(), + )?; + let borrow_amount_native = native!( + borrow_amount, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + ); + let borrow_amount_with_fee = borrow_amount_native + + debt_bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_inverse_epoch_fee(0, borrow_amount_native) + .unwrap_or(0) + }) + .unwrap_or(0); + let expected_lender_collateral_amount = I80F48::from( + native!( + lp_deposit_amount, + test_f.get_bank(&debt_mint).mint.mint.decimals, + f64 + ) - borrow_amount_with_fee, + ); + + assert_eq_noise!( + actual_lender_collateral_amount, + expected_lender_collateral_amount, + I80F48::ONE + ); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_handle_bankruptcy_success_not_insured_3_depositors() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: None, + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_1_mfi_account_f = test_f.create_marginfi_account().await; + let lender_1_token_account = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + lender_1_mfi_account_f + .try_bank_deposit(lender_1_token_account.key, usdc_bank_f, 100_000) + .await?; + + let lender_2_mfi_account_f = test_f.create_marginfi_account().await; + let lender_2_token_account = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + lender_2_mfi_account_f + .try_bank_deposit(lender_2_token_account.key, usdc_bank_f, 100_000) + .await?; + + let lender_3_mfi_account_f = test_f.create_marginfi_account().await; + let lender_3_token_account = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + lender_3_mfi_account_f + .try_bank_deposit(lender_3_token_account.key, usdc_bank_f, 100_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_001) + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 1_001) + .await?; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 10_000) + .await?; + + let mut borrower_mfi_account = borrower_mfi_account_f.load().await; + borrower_mfi_account.lending_account.balances[0] + .asset_shares + .value = 0_i128.to_le_bytes(); + + borrower_mfi_account_f + .set_account(&borrower_mfi_account) + .await?; + + test_f + .marginfi_group + .try_handle_bankruptcy(usdc_bank_f, &borrower_mfi_account_f) + .await?; + + let borrower_mfi_account = borrower_mfi_account_f.load().await; + let borrower_usdc_balance = borrower_mfi_account.lending_account.balances[1]; + + assert_eq!( + I80F48::from(borrower_usdc_balance.liability_shares), + I80F48::ZERO + ); + + let lender_1_mfi_account = lender_1_mfi_account_f.load().await; + let usdc_bank = usdc_bank_f.load().await; + + let lender_usdc_value = usdc_bank.get_asset_amount( + lender_1_mfi_account.lending_account.balances[0] + .asset_shares + .into(), + )?; + + assert_eq_noise!( + lender_usdc_value, + I80F48::from(native!(96_666, "USDC")), + I80F48::from(native!(1, "USDC")) + ); + + Ok(()) +} diff --git a/programs/marginfi/tests/bankruptcy_auth.rs b/programs/marginfi/tests/admin_actions/bankruptcy_auth.rs similarity index 78% rename from programs/marginfi/tests/bankruptcy_auth.rs rename to programs/marginfi/tests/admin_actions/bankruptcy_auth.rs index 9bbfae4af..31e36932f 100644 --- a/programs/marginfi/tests/bankruptcy_auth.rs +++ b/programs/marginfi/tests/admin_actions/bankruptcy_auth.rs @@ -5,7 +5,10 @@ use fixtures::{ }; use marginfi::{ errors::MarginfiError, - state::marginfi_group::{BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, + state::{ + marginfi_account::DISABLED_FLAG, + marginfi_group::{BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, + }, }; use solana_program_test::tokio; use solana_sdk::pubkey::Pubkey; @@ -16,11 +19,11 @@ async fn marginfi_group_handle_bankruptcy_unauthorized() -> anyhow::Result<()> { group_config: Some(GroupConfig { admin: None }), banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, config: None, }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, config: Some(BankConfig { asset_weight_init: I80F48!(1).into(), ..*DEFAULT_SOL_TEST_BANK_CONFIG @@ -38,7 +41,7 @@ async fn marginfi_group_handle_bankruptcy_unauthorized() -> anyhow::Result<()> { lender_mfi_account_f .try_bank_deposit( lender_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 100_000, ) .await?; @@ -52,17 +55,17 @@ async fn marginfi_group_handle_bankruptcy_unauthorized() -> anyhow::Result<()> { borrower_account .try_bank_deposit( borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), + test_f.get_bank(&BankMint::Sol), 1_001, ) .await?; - let borrower_borrow_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let borrower_borrow_account = test_f.usdc_mint.create_empty_token_account().await; borrower_account .try_bank_borrow( borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 10_000, ) .await?; @@ -75,10 +78,10 @@ async fn marginfi_group_handle_bankruptcy_unauthorized() -> anyhow::Result<()> { { let (insurance_vault, _) = test_f - .get_bank(&BankMint::USDC) + .get_bank(&BankMint::Usdc) .get_vault(BankVaultType::Insurance); test_f - .get_bank_mut(&BankMint::USDC) + .get_bank_mut(&BankMint::Usdc) .mint .mint_to(&insurance_vault, 10_000) .await; @@ -91,7 +94,7 @@ async fn marginfi_group_handle_bankruptcy_unauthorized() -> anyhow::Result<()> { }) .await?; - let bank = test_f.get_bank(&BankMint::USDC); + let bank = test_f.get_bank(&BankMint::Usdc); let res = test_f .marginfi_group @@ -110,11 +113,11 @@ async fn marginfi_group_handle_bankruptcy_perimssionless() -> anyhow::Result<()> group_config: Some(GroupConfig { admin: None }), banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, config: None, }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, config: Some(BankConfig { asset_weight_init: I80F48!(1).into(), ..*DEFAULT_SOL_TEST_BANK_CONFIG @@ -132,7 +135,7 @@ async fn marginfi_group_handle_bankruptcy_perimssionless() -> anyhow::Result<()> lender_mfi_account_f .try_bank_deposit( lender_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 100_000, ) .await?; @@ -146,17 +149,17 @@ async fn marginfi_group_handle_bankruptcy_perimssionless() -> anyhow::Result<()> borrower_account .try_bank_deposit( borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), + test_f.get_bank(&BankMint::Sol), 1_001, ) .await?; - let borrower_borrow_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let borrower_borrow_account = test_f.usdc_mint.create_empty_token_account().await; borrower_account .try_bank_borrow( borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 10_000, ) .await?; @@ -169,16 +172,16 @@ async fn marginfi_group_handle_bankruptcy_perimssionless() -> anyhow::Result<()> { let (insurance_vault, _) = test_f - .get_bank(&BankMint::USDC) + .get_bank(&BankMint::Usdc) .get_vault(BankVaultType::Insurance); test_f - .get_bank_mut(&BankMint::USDC) + .get_bank_mut(&BankMint::Usdc) .mint .mint_to(&insurance_vault, 10_000) .await; } - let bank = test_f.get_bank(&BankMint::USDC); + let bank = test_f.get_bank(&BankMint::Usdc); bank.update_config(BankConfigOpt { permissionless_bad_debt_settlement: Some(true), @@ -200,5 +203,13 @@ async fn marginfi_group_handle_bankruptcy_perimssionless() -> anyhow::Result<()> assert!(res.is_ok()); + // Check borrower account is disabled and shares are + let borrower_marginfi_account = borrower_account.load().await; + assert!(borrower_marginfi_account.get_flag(DISABLED_FLAG)); + assert_eq!( + borrower_marginfi_account.lending_account.balances[1].liability_shares, + I80F48!(0.0).into() + ); + Ok(()) } diff --git a/programs/marginfi/tests/admin_actions/create_marginfi_group.rs b/programs/marginfi/tests/admin_actions/create_marginfi_group.rs new file mode 100644 index 000000000..762459cc8 --- /dev/null +++ b/programs/marginfi/tests/admin_actions/create_marginfi_group.rs @@ -0,0 +1,50 @@ +use anchor_lang::{InstructionData, ToAccountMetas}; +use fixtures::prelude::*; +use marginfi::prelude::MarginfiGroup; +use pretty_assertions::assert_eq; +use solana_program::{instruction::Instruction, system_program}; +use solana_program_test::*; +use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction}; + +#[tokio::test] +async fn marginfi_group_create_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(None).await; + + // Create & initialize marginfi group + let marginfi_group_key = Keypair::new(); + + let accounts = marginfi::accounts::MarginfiGroupInitialize { + marginfi_group: marginfi_group_key.pubkey(), + admin: test_f.payer(), + system_program: system_program::id(), + }; + let init_marginfi_group_ix = Instruction { + program_id: marginfi::id(), + accounts: accounts.to_account_metas(Some(true)), + data: marginfi::instruction::MarginfiGroupInitialize {}.data(), + }; + let tx = Transaction::new_signed_with_payer( + &[init_marginfi_group_ix], + Some(&test_f.payer().clone()), + &[&test_f.payer_keypair(), &marginfi_group_key], + test_f.get_latest_blockhash().await, + ); + let res = test_f + .context + .borrow_mut() + .banks_client + .process_transaction(tx) + .await; + + assert!(res.is_ok()); + + // Fetch & deserialize marginfi group account + let marginfi_group: MarginfiGroup = test_f + .load_and_deserialize(&marginfi_group_key.pubkey()) + .await; + + // Check basic properties + assert_eq!(marginfi_group.admin, test_f.payer()); + + Ok(()) +} diff --git a/programs/marginfi/tests/admin_actions/interest_accrual.rs b/programs/marginfi/tests/admin_actions/interest_accrual.rs new file mode 100644 index 000000000..75bde5597 --- /dev/null +++ b/programs/marginfi/tests/admin_actions/interest_accrual.rs @@ -0,0 +1,189 @@ +use anchor_lang::prelude::Clock; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{assert_eq_noise, native, prelude::*}; +use marginfi::{ + prelude::GroupConfig, + state::marginfi_group::{Bank, BankConfig, BankVaultType, InterestRateConfig}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; + +#[tokio::test] +async fn marginfi_group_accrue_interest_rates_success_1() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + group_config: Some(GroupConfig { admin: None }), + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(BankConfig { + interest_rate_config: InterestRateConfig { + optimal_utilization_rate: I80F48!(0.9).into(), + plateau_interest_rate: I80F48!(1).into(), + ..*DEFAULT_TEST_BANK_INTEREST_RATE_CONFIG + }, + ..*DEFAULT_USDC_TEST_BANK_CONFIG + }), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(100).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 999) + .await?; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 90) + .await?; + + { + let mut ctx = test_f.context.borrow_mut(); + let mut clock: Clock = ctx.banks_client.get_sysvar().await?; + // Advance clock by 1 year + clock.unix_timestamp += 365 * 24 * 60 * 60; + ctx.set_sysvar(&clock); + } + + test_f + .marginfi_group + .try_accrue_interest(usdc_bank_f) + .await?; + + let borrower_mfi_account = borrower_mfi_account_f.load().await; + let borrower_bank_account = borrower_mfi_account.lending_account.balances[1]; + let usdc_bank: Bank = usdc_bank_f.load().await; + let liabilities = + usdc_bank.get_liability_amount(borrower_bank_account.liability_shares.into())?; + + let lender_mfi_account = lender_mfi_account_f.load().await; + let lender_bank_account = lender_mfi_account.lending_account.balances[0]; + let assets = usdc_bank.get_asset_amount(lender_bank_account.asset_shares.into())?; + + assert_eq_noise!( + liabilities, + I80F48::from(native!(180, "USDC")), + I80F48!(100) + ); + assert_eq_noise!(assets, I80F48::from(native!(190, "USDC")), I80F48!(100)); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_accrue_interest_rates_success_2() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(BankConfig { + deposit_limit: native!(1_000_000_000, "USDC"), + interest_rate_config: InterestRateConfig { + optimal_utilization_rate: I80F48!(0.9).into(), + plateau_interest_rate: I80F48!(1).into(), + protocol_fixed_fee_apr: I80F48!(0.01).into(), + insurance_fee_fixed_apr: I80F48!(0.01).into(), + ..*DEFAULT_TEST_BANK_INTEREST_RATE_CONFIG + }, + ..*DEFAULT_USDC_TEST_BANK_CONFIG + }), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + deposit_limit: native!(200_000_000, "SOL"), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(10_000_000) + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10_000_000) + .await?; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 90_000_000) + .await?; + + // Advance clock by 1 minute + { + let mut ctx = test_f.context.borrow_mut(); + let mut clock: Clock = ctx.banks_client.get_sysvar().await?; + clock.unix_timestamp += 60; + ctx.set_sysvar(&clock); + } + + test_f + .marginfi_group + .try_accrue_interest(usdc_bank_f) + .await?; + + test_f.marginfi_group.try_collect_fees(usdc_bank_f).await?; + + let borrower_mfi_account = borrower_mfi_account_f.load().await; + let borrower_bank_account = borrower_mfi_account.lending_account.balances[1]; + let usdc_bank = usdc_bank_f.load().await; + let liabilities = + usdc_bank.get_liability_amount(borrower_bank_account.liability_shares.into())?; + + let lender_mfi_account = lender_mfi_account_f.load().await; + let lender_bank_account = lender_mfi_account.lending_account.balances[0]; + let assets = usdc_bank.get_asset_amount(lender_bank_account.asset_shares.into())?; + + assert_eq_noise!(liabilities, I80F48!(90000174657530), I80F48!(10)); + assert_eq_noise!(assets, I80F48!(100000171232876), I80F48!(10)); + + let protocol_fees = usdc_bank_f + .get_vault_token_account(BankVaultType::Fee) + .await; + let insurance_fees = usdc_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await; + + assert_eq!(protocol_fees.balance().await, 1712328); + assert_eq!(insurance_fees.balance().await, 1712328); + + Ok(()) +} diff --git a/programs/marginfi/tests/admin_actions/mod.rs b/programs/marginfi/tests/admin_actions/mod.rs new file mode 100644 index 000000000..d2fd32cf1 --- /dev/null +++ b/programs/marginfi/tests/admin_actions/mod.rs @@ -0,0 +1,7 @@ +mod account_transfer; +mod bankruptcy; +mod bankruptcy_auth; +mod create_marginfi_group; +mod interest_accrual; +mod setup_bank; +mod withdraw_fees; diff --git a/programs/marginfi/tests/admin_actions/setup_bank.rs b/programs/marginfi/tests/admin_actions/setup_bank.rs new file mode 100644 index 000000000..4a3abe996 --- /dev/null +++ b/programs/marginfi/tests/admin_actions/setup_bank.rs @@ -0,0 +1,348 @@ +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{assert_custom_error, prelude::*}; +use marginfi::{ + constants::PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG, + prelude::MarginfiError, + state::marginfi_group::{Bank, BankConfig, BankConfigOpt, BankVaultType}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use solana_sdk::pubkey::Pubkey; +use test_case::test_case; + +#[tokio::test] +async fn add_bank_success() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(None).await; + + let mints = vec![ + ( + MintFixture::new(test_f.context.clone(), None, None).await, + *DEFAULT_USDC_TEST_BANK_CONFIG, + ), + ( + MintFixture::new_token_22( + test_f.context.clone(), + None, + None, + &[SupportedExtension::TransferFee], + ) + .await, + *DEFAULT_T22_WITH_FEE_TEST_BANK_CONFIG, + ), + ( + MintFixture::new_from_file(&test_f.context.clone(), "src/fixtures/pyUSD.json"), + *DEFAULT_PYUSD_TEST_BANK_CONFIG, + ), + ]; + + for (mint_f, bank_config) in mints { + let res = test_f + .marginfi_group + .try_lending_pool_add_bank(&mint_f, bank_config) + .await; + + // Check bank + let bank_f = res.unwrap(); + let Bank { + mint, + mint_decimals, + group, + asset_share_value, + liability_share_value, + liquidity_vault, + liquidity_vault_bump, + liquidity_vault_authority_bump, + insurance_vault, + insurance_vault_bump, + insurance_vault_authority_bump, + collected_insurance_fees_outstanding, + fee_vault, + fee_vault_bump, + fee_vault_authority_bump, + collected_group_fees_outstanding, + total_liability_shares, + total_asset_shares, + last_update, + config, + flags, + emissions_rate, + emissions_remaining, + emissions_mint, + _padding_0, + _padding_1, + } = bank_f.load().await; + #[rustfmt::skip] + let _ = { + assert_eq!(mint, bank_f.mint.key); + assert_eq!(mint_decimals, bank_f.mint.load_state().await.base.decimals); + assert_eq!(group, test_f.marginfi_group.key); + assert_eq!(asset_share_value, I80F48!(1.0).into()); + assert_eq!(liability_share_value, I80F48!(1.0).into()); + assert_eq!(liquidity_vault, bank_f.get_vault(BankVaultType::Liquidity).0); + assert_eq!(liquidity_vault_bump, bank_f.get_vault(BankVaultType::Liquidity).1); + assert_eq!(liquidity_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Liquidity).1); + assert_eq!(insurance_vault, bank_f.get_vault(BankVaultType::Insurance).0); + assert_eq!(insurance_vault_bump, bank_f.get_vault(BankVaultType::Insurance).1); + assert_eq!(insurance_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Insurance).1); + assert_eq!(fee_vault, bank_f.get_vault(BankVaultType::Fee).0); + assert_eq!(fee_vault_bump, bank_f.get_vault(BankVaultType::Fee).1); + assert_eq!(fee_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Fee).1); + assert_eq!(collected_insurance_fees_outstanding, I80F48!(0.0).into()); + assert_eq!(collected_group_fees_outstanding, I80F48!(0.0).into()); + assert_eq!(total_liability_shares, I80F48!(0.0).into()); + assert_eq!(total_asset_shares, I80F48!(0.0).into()); + assert_eq!(config, bank_config); + assert_eq!(flags, 0); + assert_eq!(emissions_rate, 0); + assert_eq!(emissions_mint, Pubkey::new_from_array([0; 32])); + assert_eq!(emissions_remaining, I80F48!(0.0).into()); + + assert_eq!(_padding_0, <[[u64; 2]; 28] as Default>::default()); + assert_eq!(_padding_1, <[[u64; 2]; 32] as Default>::default()); + + // this is the only loosely checked field + assert!(last_update >= 0 && last_update <= 5); + }; + } + + Ok(()) +} + +#[tokio::test] +async fn add_bank_with_seed_success() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(None).await; + + let mints = vec![ + ( + MintFixture::new(test_f.context.clone(), None, None).await, + *DEFAULT_USDC_TEST_BANK_CONFIG, + ), + ( + MintFixture::new_token_22( + test_f.context.clone(), + None, + None, + &[SupportedExtension::TransferFee], + ) + .await, + *DEFAULT_T22_WITH_FEE_TEST_BANK_CONFIG, + ), + ( + MintFixture::new_from_file(&test_f.context.clone(), "src/fixtures/pyUSD.json"), + *DEFAULT_PYUSD_TEST_BANK_CONFIG, + ), + ]; + + for (mint_f, bank_config) in mints { + let bank_seed = 1200_u64; + + let res = test_f + .marginfi_group + .try_lending_pool_add_bank_with_seed(&mint_f, bank_config, bank_seed) + .await; + assert!(res.is_ok()); + + // Check bank + let bank_f = res.unwrap(); + let Bank { + mint, + mint_decimals, + group, + asset_share_value, + liability_share_value, + liquidity_vault, + liquidity_vault_bump, + liquidity_vault_authority_bump, + insurance_vault, + insurance_vault_bump, + insurance_vault_authority_bump, + collected_insurance_fees_outstanding, + fee_vault, + fee_vault_bump, + fee_vault_authority_bump, + collected_group_fees_outstanding, + total_liability_shares, + total_asset_shares, + last_update, + config, + flags, + emissions_rate, + emissions_remaining, + emissions_mint, + _padding_0, + _padding_1, + } = bank_f.load().await; + #[rustfmt::skip] + let _ = { + assert_eq!(mint, bank_f.mint.key); + assert_eq!(mint_decimals, bank_f.mint.load_state().await.base.decimals); + assert_eq!(group, test_f.marginfi_group.key); + assert_eq!(asset_share_value, I80F48!(1.0).into()); + assert_eq!(liability_share_value, I80F48!(1.0).into()); + assert_eq!(liquidity_vault, bank_f.get_vault(BankVaultType::Liquidity).0); + assert_eq!(liquidity_vault_bump, bank_f.get_vault(BankVaultType::Liquidity).1); + assert_eq!(liquidity_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Liquidity).1); + assert_eq!(insurance_vault, bank_f.get_vault(BankVaultType::Insurance).0); + assert_eq!(insurance_vault_bump, bank_f.get_vault(BankVaultType::Insurance).1); + assert_eq!(insurance_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Insurance).1); + assert_eq!(fee_vault, bank_f.get_vault(BankVaultType::Fee).0); + assert_eq!(fee_vault_bump, bank_f.get_vault(BankVaultType::Fee).1); + assert_eq!(fee_vault_authority_bump, bank_f.get_vault_authority(BankVaultType::Fee).1); + assert_eq!(collected_insurance_fees_outstanding, I80F48!(0.0).into()); + assert_eq!(collected_group_fees_outstanding, I80F48!(0.0).into()); + assert_eq!(total_liability_shares, I80F48!(0.0).into()); + assert_eq!(total_asset_shares, I80F48!(0.0).into()); + assert_eq!(config, bank_config); + assert_eq!(flags, 0); + assert_eq!(emissions_rate, 0); + assert_eq!(emissions_mint, Pubkey::new_from_array([0; 32])); + assert_eq!(emissions_remaining, I80F48!(0.0).into()); + + assert_eq!(_padding_0, <[[u64; 2]; 28] as Default>::default()); + assert_eq!(_padding_1, <[[u64; 2]; 32] as Default>::default()); + + // this is the only loosely checked field + assert!(last_update >= 0 && last_update <= 5); + }; + } + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_add_bank_failure_inexistent_pyth_feed() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(None).await; + + let bank_asset_mint_fixture = MintFixture::new(test_f.context.clone(), None, None).await; + + let res = test_f + .marginfi_group + .try_lending_pool_add_bank( + &bank_asset_mint_fixture, + BankConfig { + oracle_setup: marginfi::state::price::OracleSetup::PythLegacy, + oracle_keys: create_oracle_key_array(INEXISTENT_PYTH_USDC_FEED), + ..*DEFAULT_USDC_TEST_BANK_CONFIG + }, + ) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::InvalidOracleAccount); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn configure_bank_success(bank_mint: BankMint) -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let bank = test_f.get_bank(&bank_mint); + let old_bank = bank.load().await; + + let config_bank_opt = BankConfigOpt { + interest_rate_config: Some(marginfi::state::marginfi_group::InterestRateConfigOpt { + optimal_utilization_rate: Some(I80F48::from_num(0.91).into()), + plateau_interest_rate: Some(I80F48::from_num(0.44).into()), + max_interest_rate: Some(I80F48::from_num(1.44).into()), + insurance_fee_fixed_apr: Some(I80F48::from_num(0.13).into()), + insurance_ir_fee: Some(I80F48::from_num(0.11).into()), + protocol_fixed_fee_apr: Some(I80F48::from_num(0.51).into()), + protocol_ir_fee: Some(I80F48::from_num(0.011).into()), + }), + ..BankConfigOpt::default() + }; + let res = bank.update_config(config_bank_opt.clone()).await; + assert!(res.is_ok()); + + // Load bank and check each property in config matches + // Ensure bank didn't change any other fields. Only need to check the opt fields + + let bank: Bank = test_f.load_and_deserialize(&bank.key).await; + let BankConfigOpt { + interest_rate_config, + asset_weight_init, + asset_weight_maint, + liability_weight_init, + liability_weight_maint, + deposit_limit, + borrow_limit, + operational_state, + oracle, + risk_tier, + total_asset_value_init_limit, + oracle_max_age, + permissionless_bad_debt_settlement, + } = &config_bank_opt; + // Compare bank field to opt field if Some, otherwise compare to old bank field + macro_rules! check_bank_field { + ($field:tt, $subfield:tt) => { + assert_eq!( + bank.config.$field.$subfield, + $field + .as_ref() + .map(|opt| opt + .$subfield + .clone() + .unwrap_or(old_bank.config.$field.$subfield)) + .unwrap() + ); + }; + + ($field:tt) => { + assert_eq!(bank.config.$field, $field.unwrap_or(old_bank.config.$field)); + }; + } + + #[rustfmt::skip] + let _ = { + check_bank_field!(interest_rate_config, optimal_utilization_rate); + check_bank_field!(interest_rate_config, plateau_interest_rate); + check_bank_field!(interest_rate_config, max_interest_rate); + check_bank_field!(interest_rate_config, insurance_fee_fixed_apr); + check_bank_field!(interest_rate_config, insurance_ir_fee); + check_bank_field!(interest_rate_config, protocol_fixed_fee_apr); + check_bank_field!(interest_rate_config, protocol_ir_fee); + + check_bank_field!(asset_weight_init); + check_bank_field!(asset_weight_maint); + check_bank_field!(liability_weight_init); + check_bank_field!(liability_weight_maint); + check_bank_field!(deposit_limit); + check_bank_field!(borrow_limit); + check_bank_field!(operational_state); + check_bank_field!(risk_tier); + check_bank_field!(total_asset_value_init_limit); + check_bank_field!(oracle_max_age); + + + + assert!(permissionless_bad_debt_settlement + // If Some(...) check flag set properly + .map(|set| set == bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG)) + // If None check flag is unchanged + .unwrap_or( bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG) == old_bank.get_flag(PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG)) + ); + + assert_eq!( + bank.config.oracle_keys, + // If Some(...) check keys set properly + // If None check keys unchanged + oracle.map(|o| o.keys).unwrap_or(old_bank.config.oracle_keys)); + assert_eq!( + bank.config.oracle_setup, + // If Some(...) check setup set properly + // If None check setup unchanged + oracle.map(|o| o.setup).unwrap_or(old_bank.config.oracle_setup) + ); + }; + + Ok(()) +} diff --git a/programs/marginfi/tests/admin_actions/withdraw_fees.rs b/programs/marginfi/tests/admin_actions/withdraw_fees.rs new file mode 100644 index 000000000..720e2f6f9 --- /dev/null +++ b/programs/marginfi/tests/admin_actions/withdraw_fees.rs @@ -0,0 +1,162 @@ +use anchor_lang::error::ErrorCode; +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixtures::{ + assert_anchor_error, native, + test::{BankMint, TestFixture, TestSettings}, +}; +use marginfi::state::marginfi_group::GroupConfig; +use solana_program_test::tokio; +use solana_sdk::pubkey::Pubkey; +use test_case::test_case; + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_group_withdraw_fees_and_insurance_fund_as_admin_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let bank_f = test_f.banks.get_mut(&bank_mint).unwrap(); + + let insurance_vault_balance = 1_000; + let insurance_vault_balance_native = + native!(insurance_vault_balance, bank_f.mint.mint.decimals); + let fee_vault_balance = 750; + + // Mint `insurance_vault_balance` USDC to the insurance vault + // 1) Mint to admin + // 2) Deposit as admin + let receiving_account = bank_f.mint.create_empty_token_account().await; + let bank = bank_f.load().await; + bank_f + .mint + .mint_to(&receiving_account.key, insurance_vault_balance as f64) + .await; + bank_f + .try_admin_deposit_insurance(&receiving_account, insurance_vault_balance_native) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, insurance_vault_balance_native) + .unwrap_or(0) + }) + .unwrap_or(0); + + // Create a receiving account and try to withdraw `insurance_vault_balance` USDC from the insurance vault + bank_f + .try_admin_withdraw_insurance( + &receiving_account, + (insurance_vault_balance_native - first_transfer_fee).into(), + ) + .await?; + + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, insurance_vault_balance_native - first_transfer_fee) + .unwrap_or(0) + }) + .unwrap_or(0); + + let expected_received_balance = + insurance_vault_balance_native - first_transfer_fee - second_transfer_fee; + assert_eq!(receiving_account.balance().await, expected_received_balance); // Verifies that the receiving account balance is 1000 USDC + + // Mint `fee_vault_balance` USDC to the fee vault + bank_f + .mint + .mint_to(&bank.fee_vault, fee_vault_balance as f64) + .await; + + // Create a receiving account and try to withdraw `fee_vault_balance` USDC from the fee vault + let receiving_account = bank_f.mint.create_empty_token_account().await; + bank_f + .try_withdraw_fees(&receiving_account, fee_vault_balance) + .await?; + + let transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, fee_vault_balance).unwrap_or(0)) + .unwrap_or(0); + + let expected_received_balance = fee_vault_balance - transfer_fee; + assert_eq!(receiving_account.balance().await, expected_received_balance); // Verifies that the receiving account balance is 750 USDC + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_group_withdraw_fees_and_insurance_fund_as_non_admin_failure( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let bank_f = test_f.banks.get_mut(&bank_mint).unwrap(); + let bank = bank_f.load().await; + + let insurance_vault_balance = 1_000_u64; + let fee_vault_balance = 750; + + // Update the admin of the marginfi group + test_f + .marginfi_group + .try_update(GroupConfig { + admin: Some(Pubkey::new_unique()), + }) + .await?; + + // Mint `insurance_vault_balance` USDC to the insurance vault + bank_f + .mint + .mint_to(&bank.insurance_vault, insurance_vault_balance as f64) + .await; + + // Create a receiving account and try to withdraw `insurance_vault_balance` USDC from the insurance vault + let receiving_account = bank_f.mint.create_empty_token_account().await; + let res = bank_f + .try_admin_withdraw_insurance(&receiving_account, insurance_vault_balance.into()) + .await; + + // Unable to withdraw 1000 USDC from the insurance vault, because the signer is not the admin + assert_anchor_error!(res.unwrap_err(), ErrorCode::ConstraintAddress); + + // Mint `fee_vault_balance` USDC to the fee vault + bank_f + .mint + .mint_to(&bank.fee_vault, fee_vault_balance as f64) + .await; + + // Create a receiving account and try to withdraw `fee_vault_balance` USDC from the fee vault + let receiving_account = bank_f.mint.create_empty_token_account().await; + let res = bank_f + .try_withdraw_fees(&receiving_account, fee_vault_balance) + .await; + + // Unable to withdraw `fee_vault_balance` USDC from the fee vault, because the signer is not the admin + assert_anchor_error!(res.unwrap_err(), ErrorCode::ConstraintAddress); + + Ok(()) +} diff --git a/programs/marginfi/tests/liquid_insurance_fund.rs b/programs/marginfi/tests/liquid_insurance_fund.rs new file mode 100644 index 000000000..77a981a84 --- /dev/null +++ b/programs/marginfi/tests/liquid_insurance_fund.rs @@ -0,0 +1,825 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{ + bank::BankFixture, + lif::LiquidInsuranceFundAccountFixture, + spl::TokenAccountFixture, + test::{BankMint, TestFixture, TestSettings}, +}; +use marginfi::state::marginfi_group::{BankConfigOpt, BankVaultType}; +use solana_program_test::tokio; +use test_case::test_case; + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_create_success(bank_mint: BankMint) -> anyhow::Result<()> { + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let bank_fixture = test_f.banks.get_mut(&bank_mint).unwrap(); + + let min_withdraw_period = 60_u64 * 60_u64 * 24_u64 * 14_u64; // 2 weeks + + test_f + .marginfi_group + .try_create_liquid_insurance_fund(&bank_fixture, min_withdraw_period) + .await?; + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_admin_deposit_withdraw_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let bank_f = test_f.get_bank(&bank_mint); + + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + let min_withdraw_period = 60_u64 * 60_u64 * 24_u64 * 14_u64; // 2 weeks + + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + // Deposit through deposit ix should update admin shares + let source_account = bank_f + .mint + .create_token_account_and_mint_to(deposit_amount_ui) + .await; + + bank_f + .try_admin_deposit_insurance(&source_account, deposit_amount) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount); + assert_eq!(source_account.balance().await, 0); + + // Withdraw + bank_f + .try_admin_withdraw_insurance(&source_account, admin_shares) + .await + .unwrap(); + + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, deposit_amount - first_transfer_fee) + .unwrap_or(0) + }) + .unwrap_or(0); + let postfee_withdraw_amount = deposit_amount - first_transfer_fee - second_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48!(0.0)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 0); + assert_eq!(source_account.balance().await, postfee_withdraw_amount); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_convert_insurance_fund_admin_deposit_withdraw_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let mint_f = test_f.get_mint_fixture(&bank_mint); + let bank_f = test_f.get_bank(&bank_mint); + let insurance_vault_key = bank_f.get_vault(BankVaultType::Insurance).0; + + // Transfer to insurance fund vault + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + let min_withdraw_period = 60_u64 * 60_u64 * 24_u64 * 14_u64; // 2 weeks + let source_account = bank_f + .mint + .create_token_account_with_owner_and_mint_to(&test_f.payer_keypair(), deposit_amount_ui) + .await; + source_account + .transfer( + &test_f.payer_keypair(), + mint_f, + &insurance_vault_key, + deposit_amount, + ) + .await + .unwrap(); + + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount); + assert_eq!(source_account.balance().await, 0); + + // Withdraw + bank_f + .try_admin_withdraw_insurance(&source_account, admin_shares) + .await + .unwrap(); + + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, deposit_amount - first_transfer_fee) + .unwrap_or(0) + }) + .unwrap_or(0); + let postfee_withdraw_amount = deposit_amount - first_transfer_fee - second_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48!(0.0)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 0); + assert_eq!(source_account.balance().await, postfee_withdraw_amount); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_user_deposit_withdraw_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let bank_f = test_f.get_bank(&bank_mint); + + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + + // Seconds + let min_withdraw_period = 2; + + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + // Create user lif and fund user with usdc + let mut user_lif_account: LiquidInsuranceFundAccountFixture = + lif_f.create_lif_user_account().await.unwrap(); + let source_account = bank_f + .mint + .create_token_account_with_owner_and_mint_to(user_lif_account.user(), deposit_amount_ui) + .await; + + // User deposit + user_lif_account + .try_user_deposit_insurance(bank_f, &source_account, deposit_amount) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(admin_shares, I80F48!(0.0)); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount); + assert_eq!(source_account.balance().await, 0); + + // Initiate withdrawal of first half of shares + user_lif_account + .try_user_withdraw_insurance_request(&bank_f, Some(total_shares >> 1), 1) + .await + .unwrap(); + + // Check withdrawal and deposit state + let account = user_lif_account.load().await; + let withdrawal = &account.withdrawals[0]; + assert_eq!(withdrawal.shares(), total_shares >> 1); + let deposit = account.get_deposit(&lif_f.key).unwrap(); + assert_eq!(deposit.shares(), total_shares >> 1); + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(admin_shares, I80F48!(0.0)); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Initiate withdrawal of second half of shares + user_lif_account + .try_user_withdraw_insurance_request(&bank_f, Some(total_shares >> 1), 2) + .await + .unwrap(); + + // Check withdrawal and deposit state (deposit no longer exists) + let account = user_lif_account.load().await; + let withdrawal = &account.withdrawals[1]; + assert_eq!(withdrawal.shares(), total_shares >> 1); + assert!(account.get_deposit(&lif_f.key).is_none()); + assert_eq!(account.balances[0], Default::default()); + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(admin_shares, I80F48!(0.0)); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Withdrawal claim should fail now, but should succeed after withdrawal period + assert!(user_lif_account + .try_user_withdraw_insurance_claim(&bank_f, &source_account, 1) + .await + .is_err()); + let old_time = test_f.get_clock().await.unix_timestamp; + test_f.advance_time(min_withdraw_period as i64).await; + let new_time = test_f.get_clock().await.unix_timestamp; + eprintln!("time {} -> {}", old_time, new_time); + user_lif_account + .try_user_withdraw_insurance_claim(&bank_f, &source_account, 2) + .await + .unwrap(); + + // Check only first withdrawal was freed + let account = user_lif_account.load().await; + assert_eq!(account.withdrawals[0], Default::default()); + assert!(!account.withdrawals[1].is_empty()); + + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, postfee_deposit_amount >> 1) + .unwrap_or(0) + }) + .unwrap_or(0); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount >> 1); + assert_eq!( + source_account.balance().await, + (postfee_deposit_amount >> 1) - second_transfer_fee + ); + + user_lif_account + .try_user_withdraw_insurance_claim(&bank_f, &source_account, 3) + .await + .unwrap(); + + // Check second withdrawal was freed + let account = user_lif_account.load().await; + assert!(account.withdrawals[1].is_empty()); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 0); + assert_eq!( + source_account.balance().await, + postfee_deposit_amount - 2 * second_transfer_fee + ); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_admin_user_withdraw_limits( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank and lif + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let bank_f = test_f.get_bank(&bank_mint); + + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + + // Seconds + let min_withdraw_period = 2; + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + // Admin deposit + let admin_source_account = bank_f + .mint + .create_token_account_and_mint_to(deposit_amount_ui) + .await; + bank_f + .try_admin_deposit_insurance(&admin_source_account, deposit_amount) + .await + .unwrap(); + + // User deposit + let user_lif_account: LiquidInsuranceFundAccountFixture = + lif_f.create_lif_user_account().await.unwrap(); + let user_source_account = bank_f + .mint + .create_token_account_with_owner_and_mint_to(user_lif_account.user(), deposit_amount_ui) + .await; + + // Deposit through deposit ix should update + user_lif_account + .try_user_deposit_insurance(bank_f, &user_source_account, deposit_amount) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + let lif = lif_f.load().await; + assert_eq!( + lif.total_shares, + I80F48::from(2 * postfee_deposit_amount).into() + ); + assert_eq!( + lif.admin_shares, + I80F48::from(postfee_deposit_amount).into() + ); + + // Admin should not be able to withdraw more than 100_000_000 shares + assert!(bank_f + .try_admin_withdraw_insurance( + &admin_source_account, + I80F48::from(postfee_deposit_amount + 1) + ) + .await + .is_err()); + + // User should not be able to submit claim for more than 100_000_000 shares + assert!(user_lif_account + .try_user_withdraw_insurance_request( + &bank_f, + Some(I80F48::from(postfee_deposit_amount + 1)), + 1 + ) + .await + .is_err()); + + // Both should be able to withdraw their full shares + bank_f + .try_admin_withdraw_insurance(&admin_source_account, postfee_deposit_amount.into()) + .await + .unwrap(); + + // User should not be able to submit claim for more than 100_000_000 shares + user_lif_account + .try_user_withdraw_insurance_request(&bank_f, Some(postfee_deposit_amount.into()), 1) + .await + .unwrap(); + test_f.advance_time(2).await; + user_lif_account + .try_user_withdraw_insurance_claim(&bank_f, &user_source_account, 1) + .await + .unwrap(); + + // Check balance + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, postfee_deposit_amount) + .unwrap_or(0) + }) + .unwrap_or(0); + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 0); + assert_eq!( + admin_source_account.balance().await, + deposit_amount - first_transfer_fee - second_transfer_fee + ); + assert_eq!( + user_source_account.balance().await, + deposit_amount - first_transfer_fee - second_transfer_fee + ); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_admin_deposit_withdraw_with_liquidation_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let bank_f = test_f.get_bank(&bank_mint); + + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + + let min_withdraw_period = 60_u64 * 60_u64 * 24_u64 * 14_u64; // 2 weeks + + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + // Deposit through deposit ix should update admin shares + let source_account = bank_f + .mint + .create_token_account_and_mint_to(deposit_amount_ui) + .await; + + bank_f + .try_admin_deposit_insurance(&source_account, deposit_amount) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount); + assert_eq!(source_account.balance().await, 0); + + // Liquidation occurs + liquidation(&test_f, &bank_mint).await.unwrap(); + + // Second transfer fee + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, postfee_deposit_amount) + .unwrap_or(0) + }) + .unwrap_or(0); + let pre_liq_withdraw_amount = postfee_deposit_amount - second_transfer_fee; + + // Withdraw + bank_f + .try_admin_withdraw_insurance(&source_account, admin_shares) + .await + .unwrap(); + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48!(0.0)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 1); + assert!(source_account.balance().await > pre_liq_withdraw_amount); + + Ok(()) +} + +#[test_case(BankMint::Usdc)] +#[test_case(BankMint::Sol)] +#[test_case(BankMint::PyUSD)] +#[test_case(BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_liquid_insurance_fund_admin_deposit_withdraw_with_bad_debt_success( + bank_mint: BankMint, +) -> anyhow::Result<()> { + // first create bank + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + let bank_f = test_f.get_bank(&bank_mint); + + let deposit_amount_ui = 100_f64; + let deposit_amount: u64 = + (deposit_amount_ui as u64) * 10_u64.pow(bank_f.mint.mint.decimals as u32); + + let min_withdraw_period = 60_u64 * 60_u64 * 24_u64 * 14_u64; // 2 weeks + + let mut lif_f = test_f + .marginfi_group + .try_create_liquid_insurance_fund(bank_f, min_withdraw_period) + .await?; + + // Deposit through deposit ix should update admin shares + let source_account = bank_f + .mint + .create_token_account_and_mint_to(deposit_amount_ui) + .await; + + bank_f + .try_admin_deposit_insurance(&source_account, deposit_amount) + .await + .unwrap(); + + let first_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| tf.calculate_epoch_fee(0, deposit_amount).unwrap_or(0)) + .unwrap_or(0); + let postfee_deposit_amount = deposit_amount - first_transfer_fee; + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48::from(postfee_deposit_amount)); + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, postfee_deposit_amount); + assert_eq!(source_account.balance().await, 0); + + // Bad Debt handling occurs + bad_debt(&test_f, bank_f).await?; + + // Withdraw + bank_f + .try_admin_withdraw_insurance(&source_account, postfee_deposit_amount.into()) + .await + .unwrap(); + + // Check shares + let lif = lif_f.load().await; + let admin_shares = lif.get_admin_shares(); + let total_shares = lif.get_total_shares(); + assert_eq!(total_shares, admin_shares); + assert_eq!(total_shares, I80F48!(0.0)); + + let second_transfer_fee = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, postfee_deposit_amount) + .unwrap_or(0) + }) + .unwrap_or(0); + // In the absence of bad debt + let postfee_withdraw_amount = deposit_amount - first_transfer_fee - second_transfer_fee; + + // Check balance + let insurance_vault = TokenAccountFixture::fetch( + test_f.context.clone(), + bank_f.get_vault(BankVaultType::Insurance).0, + ) + .await; + assert_eq!(insurance_vault.balance().await, 0); + assert!(dbg!(source_account.balance().await) < postfee_withdraw_amount); + + Ok(()) +} + +// TODO: +// [x] users deposit/withdraw success +// [x] admin cannot withdraw more than their share value (when user deposits are present) +// [x] users cannot withdraw more than their share value (when other user deposits or admin deposits are present) +// [x] with liquidation +// [x] with bad debt + +async fn liquidation(test_f: &TestFixture, bank_mint: &BankMint) -> anyhow::Result<()> { + let bank_f = test_f.get_bank(&bank_mint); + let sol_eq = test_f.get_bank(&BankMint::SolEquivalent); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account = bank_f.mint.create_token_account_and_mint_to(3_000).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account.key, bank_f, 2_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol_eq = sol_eq.mint.create_token_account_and_mint_to(200).await; + let borrower_token_account_bank = bank_f.mint.create_token_account_and_mint_to(0).await; + + // Borrower deposits 100 SOL worth of $1000 + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq, 100) + .await?; + + // Borrower borrows 20 from bank_f + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_bank.key, bank_f, 20) + .await?; + let borrower_account = borrower_mfi_account_f.load().await; + println!( + "borrower balances: {:?}", + borrower_account.lending_account.balances + ); + + // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank + sol_eq + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.000025).into()), + asset_weight_maint: Some(I80F48!(0.00005).into()), + ..Default::default() + }) + .await?; + + lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_eq, 1, bank_f) + .await + .unwrap(); + + Ok(()) +} + +async fn bad_debt(test_f: &TestFixture, bank_f: &BankFixture) -> anyhow::Result<()> { + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account = bank_f.mint.create_token_account_and_mint_to(200_000).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account.key, bank_f, 100_000) + .await?; + + let borrower_account = test_f.create_marginfi_account().await; + let borrower_deposit_account = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(2_000) + .await; + + borrower_account + .try_bank_deposit( + borrower_deposit_account.key, + test_f.get_bank(&BankMint::SolEquivalent), + 1_000, + ) + .await?; + + let borrower_borrow_account = bank_f.mint.create_token_account_and_mint_to(0).await; + + borrower_account + .try_bank_borrow(borrower_borrow_account.key, bank_f, 1_000) + .await?; + + // Artificially bankrupt user + let mut borrower_mfi_account = borrower_account.load().await; + borrower_mfi_account.lending_account.balances[0] + .asset_shares + .value = 0_i128.to_le_bytes(); + borrower_account.set_account(&borrower_mfi_account).await?; + + test_f + .marginfi_group + .try_handle_bankruptcy(bank_f, &borrower_account) + .await + .unwrap(); + + Ok(()) +} diff --git a/programs/marginfi/tests/marginfi_account.rs b/programs/marginfi/tests/marginfi_account.rs deleted file mode 100644 index 6ddf32090..000000000 --- a/programs/marginfi/tests/marginfi_account.rs +++ /dev/null @@ -1,2611 +0,0 @@ -use std::fs::File; -use std::io::Read; -use std::path::PathBuf; -use std::str::FromStr; - -use anchor_lang::prelude::Clock; -use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas}; -use anyhow::bail; -use base64::engine::general_purpose::STANDARD; -use base64::Engine; -use fixed::types::I80F48; -use fixed_macro::types::I80F48; -use fixtures::prelude::*; -use fixtures::{assert_custom_error, assert_eq_noise, native}; -use marginfi::constants::{ - EMISSIONS_FLAG_BORROW_ACTIVE, EMISSIONS_FLAG_LENDING_ACTIVE, MIN_EMISSIONS_START_TIME, -}; -use marginfi::state::marginfi_account::{ - BankAccountWrapper, DISABLED_FLAG, FLASHLOAN_ENABLED_FLAG, IN_FLASHLOAN_FLAG, - TRANSFER_AUTHORITY_ALLOWED_FLAG, -}; -use marginfi::state::{ - marginfi_account::MarginfiAccount, - marginfi_group::{Bank, BankConfig, BankConfigOpt, BankVaultType}, -}; -use marginfi::{assert_eq_with_tolerance, prelude::*}; -use pretty_assertions::assert_eq; - -use solana_account_decoder::UiAccountData; -use solana_cli_output::CliAccount; -use solana_program::{instruction::Instruction, system_program}; -use solana_program::{pubkey, pubkey::Pubkey}; -use solana_program_test::*; -use solana_sdk::compute_budget::ComputeBudgetInstruction; -use solana_sdk::timing::SECONDS_PER_YEAR; -use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction}; - -// Feature baseline - -#[tokio::test] -async fn marginfi_account_create_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(None).await; - - // Create & initialize marginfi account - let marginfi_account_key = Keypair::new(); - let accounts = marginfi::accounts::MarginfiAccountInitialize { - marginfi_group: test_f.marginfi_group.key, - marginfi_account: marginfi_account_key.pubkey(), - authority: test_f.payer(), - fee_payer: test_f.payer(), - system_program: system_program::id(), - }; - let init_marginfi_account_ix = Instruction { - program_id: marginfi::id(), - accounts: accounts.to_account_metas(Some(true)), - data: marginfi::instruction::MarginfiAccountInitialize {}.data(), - }; - - let tx = Transaction::new_signed_with_payer( - &[init_marginfi_account_ix], - Some(&test_f.payer()), - &[&test_f.payer_keypair(), &marginfi_account_key], - test_f.get_latest_blockhash().await, - ); - - let res = test_f - .context - .borrow_mut() - .banks_client - .process_transaction(tx) - .await; - - assert!(res.is_ok()); - - // Fetch & deserialize marginfi account - let marginfi_account: MarginfiAccount = test_f - .load_and_deserialize(&marginfi_account_key.pubkey()) - .await; - - // Check basic properties - assert_eq!(marginfi_account.group, test_f.marginfi_group.key); - assert_eq!(marginfi_account.authority, test_f.payer()); - assert!(marginfi_account - .lending_account - .balances - .iter() - .all(|bank| !bank.active)); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_deposit_success() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }], - ..TestSettings::default() - })) - .await; - - let marginfi_account_f = test_f.create_marginfi_account().await; - - let owner = test_f.payer(); - let token_account_f = - TokenAccountFixture::new(test_f.context.clone(), &test_f.usdc_mint.key, &owner).await; - test_f.usdc_mint.mint_to(&token_account_f.key, 1_000).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - let res = marginfi_account_f - .try_bank_deposit(token_account_f.key, usdc_bank_f, 1_000) - .await; - assert!(res.is_ok()); - - let marginfi_account = marginfi_account_f.load().await; - - // Check balance is active - assert!(marginfi_account - .lending_account - .get_balance(&usdc_bank_f.key) - .is_some()); - - assert_eq!( - marginfi_account - .lending_account - .get_active_balances_iter() - .collect::>() - .len(), - 1 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_deposit_failure_capacity_exceeded() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - config: Some(BankConfig { - deposit_limit: native!(100, "USDC"), - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }), - }], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - - // Fund user account - let user_mfi_account_f = test_f.create_marginfi_account().await; - let user_token_account = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - - // Make unlawful deposit - let res = user_mfi_account_f - .try_bank_deposit(user_token_account.key, usdc_bank, 100) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::BankAssetCapacityExceeded); - - // Make lawful deposit - let res = user_mfi_account_f - .try_bank_deposit(user_token_account.key, usdc_bank, 99) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_withdraw_success() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }], - ..TestSettings::default() - })) - .await; - - let marginfi_account_f = test_f.create_marginfi_account().await; - - let owner = test_f.payer(); - let token_account_f = - TokenAccountFixture::new(test_f.context.clone(), &test_f.usdc_mint.key, &owner).await; - test_f.usdc_mint.mint_to(&token_account_f.key, 1_000).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - marginfi_account_f - .try_bank_deposit(token_account_f.key, usdc_bank_f, 1_000) - .await - .unwrap(); - - let res = marginfi_account_f - .try_bank_withdraw(token_account_f.key, usdc_bank_f, 900, None) - .await; - - assert!(res.is_ok()); - - let marginfi_account = marginfi_account_f.load().await; - - // Check balance is correct - let usdc_bank = usdc_bank_f.load().await; - - let usdc_balance = usdc_bank - .get_asset_amount( - marginfi_account - .lending_account - .get_balance(&usdc_bank_f.key) - .unwrap() - .asset_shares - .into(), - ) - .unwrap(); - - assert_eq!(usdc_balance, I80F48::from(native!(100, "USDC"))); - - assert_eq!( - marginfi_account - .lending_account - .get_active_balances_iter() - .collect::>() - .len(), - 1 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_withdraw_failure_withdrawing_too_much() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }], - ..TestSettings::default() - })) - .await; - - let marginfi_account_f = test_f.create_marginfi_account().await; - - let owner = test_f.payer(); - let token_account_f = - TokenAccountFixture::new(test_f.context.clone(), &test_f.usdc_mint.key, &owner).await; - test_f.usdc_mint.mint_to(&token_account_f.key, 1_000).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - marginfi_account_f - .try_bank_deposit(token_account_f.key, usdc_bank_f, 1_000) - .await - .unwrap(); - - let res = marginfi_account_f - .try_bank_withdraw(token_account_f.key, usdc_bank_f, 1_001, None) - .await; - - assert!(res.is_err()); - - assert_custom_error!(res.unwrap_err(), MarginfiError::OperationWithdrawOnly); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_withdraw_failure_borrow_limit() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - usdc_bank - .update_config(BankConfigOpt { - borrow_limit: Some(native!(1000, "USDC")), - deposit_limit: Some(native!(10001, "USDC")), - ..Default::default() - }) - .await?; - - let marginfi_account_f = test_f.create_marginfi_account().await; - - let _owner = test_f.payer(); - let depositor_usdc_account = usdc_bank - .mint - .create_token_account_and_mint_to(10_000) - .await; - - marginfi_account_f - .try_bank_deposit(depositor_usdc_account.key, usdc_bank, 10_000) - .await - .unwrap(); - - let borrower = test_f.create_marginfi_account().await; - - let borrower_sol_account = sol_bank - .mint - .create_token_account_and_mint_to(100_000) - .await; - - let borrower_usdc_account = usdc_bank.mint.create_token_account_and_mint_to(0).await; - - borrower - .try_bank_deposit(borrower_sol_account.key, sol_bank, 1000) - .await?; - - let res = borrower - .try_bank_borrow(borrower_usdc_account.key, usdc_bank, 1001) - .await; - - assert!(res.is_err()); - assert_custom_error!( - res.unwrap_err(), - MarginfiError::BankLiabilityCapacityExceeded - ); - - let res = borrower - .try_bank_borrow(borrower_usdc_account.key, usdc_bank, 999) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_withdraw_all_success() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }], - ..TestSettings::default() - })) - .await; - - let marginfi_account_f = test_f.create_marginfi_account().await; - - let owner = test_f.payer(); - let token_account_f = - TokenAccountFixture::new(test_f.context.clone(), &test_f.usdc_mint.key, &owner).await; - test_f.usdc_mint.mint_to(&token_account_f.key, 1_000).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - marginfi_account_f - .try_bank_deposit(token_account_f.key, usdc_bank_f, 1_000) - .await - .unwrap(); - - let res = marginfi_account_f - .try_bank_withdraw(token_account_f.key, usdc_bank_f, 0, Some(true)) - .await; - - assert!(res.is_ok()); - - let marginfi_account = marginfi_account_f.load().await; - - // Check balance is correct - assert!(marginfi_account - .lending_account - .get_balance(&usdc_bank_f.key) - .is_none()); - - assert_eq!( - marginfi_account - .lending_account - .get_active_balances_iter() - .collect::>() - .len(), - 0 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_repay_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 99) - .await - .unwrap(); - - let res = borrower_mfi_account_f - .try_bank_repay(borrower_token_account_f_sol.key, sol_bank, 80, None) - .await; - - assert!(res.is_ok()); - - // Check token balances are correct - assert_eq!( - borrower_token_account_f_usdc.balance().await, - native!(0, "USDC") - ); - - assert_eq!( - borrower_token_account_f_sol.balance().await, - native!(19, "SOL") - ); - - // TODO: check health is sane - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_repay_failure_repaying_too_much() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 99) - .await - .unwrap(); - - let res = borrower_mfi_account_f - .try_bank_repay(borrower_token_account_f_sol.key, sol_bank, 110, None) - .await; - - assert!(res.is_err()); - - assert_custom_error!(res.unwrap_err(), MarginfiError::OperationRepayOnly); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_repay_all_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank_f, 99) - .await - .unwrap(); - - let res = borrower_mfi_account_f - .try_bank_repay(borrower_token_account_f_sol.key, sol_bank_f, 0, Some(true)) - .await; - - assert!(res.is_ok()); - - let marginfi_account = borrower_mfi_account_f.load().await; - - assert!(marginfi_account - .lending_account - .get_balance(&sol_bank_f.key) - .is_none()); - - assert_eq!( - marginfi_account - .lending_account - .get_active_balances_iter() - .collect::>() - .len(), - 1 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_borrow_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 99) - .await; - - assert!(res.is_ok()); - - // Check token balances are correct - assert_eq!( - borrower_token_account_f_usdc.balance().await, - native!(0, "USDC") - ); - - assert_eq!( - borrower_token_account_f_sol.balance().await, - native!(99, "SOL") - ); - - // TODO: check health is sane - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_borrow_success_swb() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_swb_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 99) - .await; - - assert!(res.is_ok()); - - // Check token balances are correct - assert_eq!( - borrower_token_account_f_usdc.balance().await, - native!(0, "USDC") - ); - - assert_eq!( - borrower_token_account_f_sol.balance().await, - native!(99, "SOL") - ); - - // TODO: check health is sane - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_borrow_failure_not_enough_collateral() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 101) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); - - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 100) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - asset_weight_maint: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(2_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - - // Borrower deposits 100 SOL worth of $1000 - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) - .await?; - - // Borrower borrows $999 - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) - .await?; - - // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.5).into()), - ..Default::default() - }) - .await?; - - lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await?; - - // Checks - let sol_bank: Bank = sol_bank_f.load().await; - let usdc_bank: Bank = usdc_bank_f.load().await; - - let depositor_ma = lender_mfi_account_f.load().await; - let borrower_ma = borrower_mfi_account_f.load().await; - - // Depositors should have 1 SOL - assert_eq!( - sol_bank - .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1, "SOL")) - ); - - // Depositors should have 1990.25 USDC - assert_eq_noise!( - usdc_bank - .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1990.25, "USDC", f64)), - native!(0.00001, "USDC", f64) - ); - - // Borrower should have 99 SOL - assert_eq!( - sol_bank - .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(99, "SOL")) - ); - - // Borrower should have 989.50 USDC - assert_eq_noise!( - usdc_bank - .get_liability_amount( - borrower_ma.lending_account.balances[1] - .liability_shares - .into() - ) - .unwrap(), - I80F48::from(native!(989.50, "USDC", f64)), - native!(0.00001, "USDC", f64) - ); - - // Check insurance fund fee - let insurance_fund_usdc = usdc_bank_f - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq_noise!( - insurance_fund_usdc.balance().await as i64, - native!(0.25, "USDC", f64) as i64, - 1 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_success_many_balances() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::many_banks_10())).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); - let sol_eq1_bank_f = test_f.get_bank(&BankMint::SolEquivalent1); - let sol_eq2_bank_f = test_f.get_bank(&BankMint::SolEquivalent2); - let sol_eq3_bank_f = test_f.get_bank(&BankMint::SolEquivalent3); - let sol_eq4_bank_f = test_f.get_bank(&BankMint::SolEquivalent4); - let sol_eq5_bank_f = test_f.get_bank(&BankMint::SolEquivalent5); - let sol_eq6_bank_f = test_f.get_bank(&BankMint::SolEquivalent6); - let sol_eq7_bank_f = test_f.get_bank(&BankMint::SolEquivalent7); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(2_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - let borrower_token_account_sol_eq = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(5000) - .await; - - // Borrower deposits 100 SOL worth of $1000 - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) - .await?; - - // Borrower borrows $999 - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) - .await?; - - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq1_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq2_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq3_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq4_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq5_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq6_bank_f, 0) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq7_bank_f, 0) - .await?; - - // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.5).into()), - ..Default::default() - }) - .await?; - - lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await?; - - // Checks - let sol_bank: Bank = sol_bank_f.load().await; - let usdc_bank: Bank = usdc_bank_f.load().await; - - let depositor_ma = lender_mfi_account_f.load().await; - let borrower_ma = borrower_mfi_account_f.load().await; - - // Depositors should have 1 SOL - assert_eq!( - sol_bank - .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1, "SOL")) - ); - - // Depositors should have 1990.25 USDC - assert_eq_noise!( - usdc_bank - .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1990.25, "USDC", f64)), - native!(0.00001, "USDC", f64) - ); - - // Borrower should have 99 SOL - assert_eq!( - sol_bank - .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(99, "SOL")) - ); - - // Borrower should have 989.50 USDC - assert_eq_noise!( - usdc_bank - .get_liability_amount( - borrower_ma.lending_account.balances[1] - .liability_shares - .into() - ) - .unwrap(), - I80F48::from(native!(989.50, "USDC", f64)), - native!(0.00001, "USDC", f64) - ); - - // Check insurance fund fee - let insurance_fund_usdc = usdc_bank_f - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq_noise!( - insurance_fund_usdc.balance().await as i64, - native!(0.25, "USDC", f64) as i64, - 1 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_success_swb() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - asset_weight_maint: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_SW_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(2_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - - // Borrower deposits 100 SOL worth of $1000 - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) - .await?; - - // Borrower borrows $999 - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) - .await?; - - // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.5).into()), - ..Default::default() - }) - .await?; - - lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await?; - - // Checks - let sol_bank: Bank = sol_bank_f.load().await; - let usdc_bank: Bank = usdc_bank_f.load().await; - - let depositor_ma = lender_mfi_account_f.load().await; - let borrower_ma = borrower_mfi_account_f.load().await; - - // Depositors should have 1 SOL - assert_eq!( - sol_bank - .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1, "SOL")) - ); - - // Depositors should have 1990.25 USDC - assert_eq_noise!( - usdc_bank - .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(1990.25, "USDC", f64)), - native!(0.01, "USDC", f64) - ); - - // Borrower should have 99 SOL - assert_eq!( - sol_bank - .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) - .unwrap(), - I80F48::from(native!(99, "SOL")) - ); - - // Borrower should have 989.50 USDC - assert_eq_noise!( - usdc_bank - .get_liability_amount( - borrower_ma.lending_account.balances[1] - .liability_shares - .into() - ) - .unwrap(), - I80F48::from(native!(989.50, "USDC", f64)), - native!(0.01, "USDC", f64) - ); - - // Check insurance fund fee - let insurance_fund_usdc = usdc_bank_f - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq_noise!( - insurance_fund_usdc.balance().await as i64, - native!(0.25, "USDC", f64) as i64, - native!(0.001, "USDC", f64) as i64 - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_failure_liquidatee_not_unhealthy() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: Some(BankConfig { - asset_weight_maint: I80F48!(1).into(), - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }), - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - asset_weight_maint: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) - .await?; - - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 100) - .await?; - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await; - - assert!(res.is_err()); - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_failure_liquidation_too_severe() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(10).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) - .await?; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 61) - .await?; - - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.5).into()), - ..Default::default() - }) - .await?; - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 10, usdc_bank_f) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_failure_liquidator_no_collateral() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: Some(BankConfig { - liability_weight_init: I80F48!(1.2).into(), - liability_weight_maint: I80F48!(1.1).into(), - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }), - }, - TestBankSetting { - mint: BankMint::SOL, - config: None, - }, - TestBankSetting { - mint: BankMint::SolEquivalent, - config: None, - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_sol_eq = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(100) - .await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 1) - .await?; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 60) - .await?; - - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.3).into()), - ..Default::default() - }) - .await?; - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 2, usdc_bank_f) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 1, usdc_bank_f) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_liquidation_failure_bank_not_liquidatable() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_sol_eq = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(100) - .await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) - .await?; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 1) - .await?; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 60) - .await?; - - sol_bank_f - .update_config(BankConfigOpt { - asset_weight_init: Some(I80F48!(0.25).into()), - asset_weight_maint: Some(I80F48!(0.4).into()), - ..Default::default() - }) - .await?; - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 1, sol_bank_f) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); - - let res = lender_mfi_account_f - .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn automatic_interest_payments() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - // Create lender user accounts and deposit SOL asset - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 1_000) - .await?; - - // Create borrower user accounts and deposit USDC asset - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_usdc.key, usdc_bank_f, 1_000) - .await?; - - // Borrow SOL from borrower mfi account - borrower_mfi_account_f - .try_bank_borrow(lender_token_account_sol.key, sol_bank_f, 99) - .await?; - - // Let a year go by - { - let mut ctx = test_f.context.borrow_mut(); - let mut clock: Clock = ctx.banks_client.get_sysvar().await?; - // Advance clock by 1 year - clock.unix_timestamp += 365 * 24 * 60 * 60; - ctx.set_sysvar(&clock); - } - - // Repay principal, leaving only the accrued interest - borrower_mfi_account_f - .try_bank_repay(lender_token_account_sol.key, sol_bank_f, 99, None) - .await?; - - let sol_bank = sol_bank_f.load().await; - let borrower_mfi_account = borrower_mfi_account_f.load().await; - let lender_mfi_account = lender_mfi_account_f.load().await; - - // Verify that interest accrued matches on both sides - assert_eq_noise!( - sol_bank - .get_liability_amount( - borrower_mfi_account.lending_account.balances[1] - .liability_shares - .into() - ) - .unwrap(), - I80F48::from(native!(11.761, "SOL", f64)), - native!(0.0002, "SOL", f64) - ); - - assert_eq_noise!( - sol_bank - .get_asset_amount( - lender_mfi_account.lending_account.balances[0] - .asset_shares - .into() - ) - .unwrap(), - I80F48::from(native!(1011.761, "SOL", f64)), - native!(0.0002, "SOL", f64) - ); - // TODO: check health is sane - - Ok(()) -} - -// Regression - -#[tokio::test] -async fn marginfi_account_correct_balance_selection_after_closing_position() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 1_000) - .await?; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(2_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) - .await?; - - lender_mfi_account_f - .try_bank_withdraw(lender_token_account_sol.key, sol_bank_f, 0, Some(true)) - .await - .unwrap(); - - let mut marginfi_account = lender_mfi_account_f.load().await; - let mut usdc_bank = usdc_bank_f.load().await; - - let bank_account = BankAccountWrapper::find( - &usdc_bank_f.key, - &mut usdc_bank, - &mut marginfi_account.lending_account, - ); - - assert!(bank_account.is_ok()); - - let bank_account = bank_account.unwrap(); - - assert_eq!( - bank_account - .bank - .get_asset_amount(bank_account.balance.asset_shares.into()) - .unwrap() - .to_num::(), - native!(2_000, "USDC") - ); - - Ok(()) -} - -#[tokio::test] -async fn isolated_borrows() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_one_isolated())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_eq_bank, 1_000) - .await?; - - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(0) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL EQ - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_eq_bank, 10) - .await; - - assert!(res.is_ok()); - - // Repay isolated SOL EQ borrow and borrow SOL successfully, - let borrower_sol_account = test_f.sol_mint.create_token_account_and_mint_to(0).await; - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_sol_account.key, sol_bank, 10) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IsolatedAccountIllegalState); - - borrower_mfi_account_f - .try_bank_repay(borrower_token_account_f_sol.key, sol_eq_bank, 0, Some(true)) - .await?; - - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_sol_account.key, sol_bank, 10) - .await; - - assert!(res.is_ok()); - - // Borrowing SOL EQ again fails - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_eq_bank, 10) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IsolatedAccountIllegalState); - - Ok(()) -} - -#[tokio::test] -async fn emissions_test() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_one_isolated())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Setup emissions (Deposit for USDC, Borrow for SOL) - - let funding_account = test_f.usdc_mint.create_token_account_and_mint_to(100).await; - - usdc_bank - .try_setup_emissions( - EMISSIONS_FLAG_LENDING_ACTIVE, - 1_000_000, - native!(50, "USDC"), - usdc_bank.mint.key, - funding_account.key, - ) - .await?; - - // SOL Emissions are not in SOL Bank mint - let sol_emissions_mint = MintFixture::new(test_f.context.clone(), None, Some(6)).await; - - let funding_account = sol_emissions_mint - .create_token_account_and_mint_to(200) - .await; - - sol_bank - .try_setup_emissions( - EMISSIONS_FLAG_BORROW_ACTIVE, - 1_000_000, - native!(100, 6), - sol_emissions_mint.key, - funding_account.key, - ) - .await?; - - let sol_emissions_mint_2 = MintFixture::new(test_f.context.clone(), None, Some(6)).await; - - let res = sol_bank - .try_setup_emissions( - EMISSIONS_FLAG_BORROW_ACTIVE, - 1_000_000, - native!(50, 6), - sol_emissions_mint_2.key, - funding_account.key, - ) - .await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::EmissionsAlreadySetup); - - // Fund SOL bank - let sol_lender_account = test_f.create_marginfi_account().await; - let sol_lender_token_account = test_f.sol_mint.create_token_account_and_mint_to(100).await; - - sol_lender_account - .try_bank_deposit(sol_lender_token_account.key, sol_bank, 100) - .await?; - - // Create account and setup positions - test_f.set_time(MIN_EMISSIONS_START_TIME as i64); - test_f - .set_pyth_oracle_timestamp(PYTH_USDC_FEED, MIN_EMISSIONS_START_TIME as i64) - .await; - test_f - .set_pyth_oracle_timestamp(PYTH_SOL_FEED, MIN_EMISSIONS_START_TIME as i64) - .await; - - let mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(50).await; - - mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank, 50) - .await?; - - let sol_account = test_f.sol_mint.create_token_account_and_mint_to(0).await; - - mfi_account_f - .try_bank_borrow(sol_account.key, sol_bank, 2) - .await?; - - // Advance for half a year and claim half emissions - test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; - - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - - mfi_account_f - .try_withdraw_emissions(usdc_bank, lender_token_account_usdc.key) - .await?; - - let sol_emissions_ta = sol_emissions_mint.create_token_account_and_mint_to(0).await; - - mfi_account_f - .try_withdraw_emissions(sol_bank, sol_emissions_ta.key) - .await?; - - assert_eq_with_tolerance!( - lender_token_account_usdc.balance().await as i64, - native!(25, "USDC") as i64, - native!(1, "USDC") as i64 - ); - - assert_eq_with_tolerance!( - sol_emissions_ta.balance().await as i64, - native!(1, 6) as i64, - native!(0.1, 6, f64) as i64 - ); - - // Advance for another half a year and claim the rest - - test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; - - { - let slot = test_f.get_slot().await; - test_f - .context - .borrow_mut() - .warp_to_slot(slot + 100) - .unwrap(); - } - - mfi_account_f - .try_withdraw_emissions(usdc_bank, lender_token_account_usdc.key) - .await?; - - mfi_account_f - .try_withdraw_emissions(sol_bank, sol_emissions_ta.key) - .await?; - - assert_eq_with_tolerance!( - lender_token_account_usdc.balance().await as i64, - native!(50, "USDC") as i64, - native!(1, "USDC") as i64 - ); - - assert_eq_with_tolerance!( - sol_emissions_ta.balance().await as i64, - native!(2, 6) as i64, - native!(0.1, 6, f64) as i64 - ); - - // Advance a year, and no more USDC emissions can be claimed (drained), SOL emissions can be claimed - - { - let slot = test_f.get_slot().await; - test_f - .context - .borrow_mut() - .warp_to_slot(slot + 100) - .unwrap(); - } - - test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; - - mfi_account_f - .try_withdraw_emissions(usdc_bank, lender_token_account_usdc.key) - .await?; - - mfi_account_f - .try_withdraw_emissions(sol_bank, sol_emissions_ta.key) - .await?; - - assert_eq_with_tolerance!( - lender_token_account_usdc.balance().await as i64, - native!(50, "USDC") as i64, - native!(1, "USDC") as i64 - ); - - assert_eq_with_tolerance!( - sol_emissions_ta.balance().await as i64, - native!(3, 6) as i64, - native!(0.1, 6, f64) as i64 - ); - - // SOL lendeing account can't claim emissions, bc SOL is borrow only emissions - let sol_lender_emissions = sol_emissions_mint.create_token_account_and_mint_to(0).await; - - sol_lender_account - .try_withdraw_emissions(sol_bank, sol_lender_emissions.key) - .await?; - - assert_eq!(sol_lender_emissions.balance().await as i64, 0); - - Ok(()) -} - -#[tokio::test] -async fn emissions_test_2() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_one_isolated())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - - let funding_account = test_f.usdc_mint.create_token_account_and_mint_to(100).await; - - usdc_bank - .try_setup_emissions( - EMISSIONS_FLAG_LENDING_ACTIVE, - 1_000_000, - native!(50, "USDC"), - usdc_bank.mint.key, - funding_account.key, - ) - .await?; - - let usdc_bank_data = usdc_bank.load().await; - - assert_eq!(usdc_bank_data.flags, EMISSIONS_FLAG_LENDING_ACTIVE); - - assert_eq!(usdc_bank_data.emissions_rate, 1_000_000); - - assert_eq!( - I80F48::from(usdc_bank_data.emissions_remaining), - I80F48::from_num(native!(50, "USDC")) - ); - - usdc_bank - .try_update_emissions( - Some(EMISSIONS_FLAG_BORROW_ACTIVE), - Some(500_000), - Some((native!(25, "USDC"), funding_account.key)), - ) - .await?; - - let usdc_bank_data = usdc_bank.load().await; - - assert_eq!(usdc_bank_data.flags, EMISSIONS_FLAG_BORROW_ACTIVE); - - assert_eq!(usdc_bank_data.emissions_rate, 500_000); - - assert_eq!( - I80F48::from(usdc_bank_data.emissions_remaining), - I80F48::from_num(native!(75, "USDC")) - ); - - Ok(()) -} - -#[tokio::test] -async fn account_flags() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let mfi_account_f = test_f.create_marginfi_account().await; - - mfi_account_f.try_set_flag(FLASHLOAN_ENABLED_FLAG).await?; - - let mfi_account_data = mfi_account_f.load().await; - - assert_eq!(mfi_account_data.account_flags, FLASHLOAN_ENABLED_FLAG); - - assert!(mfi_account_data.get_flag(FLASHLOAN_ENABLED_FLAG)); - - mfi_account_f.try_unset_flag(FLASHLOAN_ENABLED_FLAG).await?; - - let mfi_account_data = mfi_account_f.load().await; - - assert_eq!(mfi_account_data.account_flags, 0); - - let res = mfi_account_f.try_set_flag(DISABLED_FLAG).await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlag); - - let res = mfi_account_f.try_unset_flag(IN_FLASHLOAN_FLAG).await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlag); - - Ok(()) -} - -// Flashloan tests -// 1. Flashloan success (1 action) -// 2. Flashloan success (3 actions) -// 3. Flashloan fails because of bad account health -// 4. Flashloan fails because of non whitelisted account -// 5. Flashloan fails because of missing `end_flashloan` ix -// 6. Flashloan fails because of invalid instructions sysvar -// 7. Flashloan fails because of invalid `end_flashloan` ix order -// 8. Flashloan fails because `end_flashloan` ix is for another account -// 9. Flashloan fails because account is already in a flashloan - -#[tokio::test] -async fn flashloan_success_1op() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let repay_ix = borrower_mfi_account_f - .make_bank_repay_ix( - borrower_token_account_f_sol.key, - sol_bank, - 1_000, - Some(true), - ) - .await; - - let flash_loan_result = borrower_mfi_account_f - .try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![]) - .await; - - assert!(flash_loan_result.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_success_3op() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - - // Create borrow and repay instructions - let mut ixs = Vec::new(); - for _ in 0..3 { - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - ixs.push(borrow_ix); - - let repay_ix = borrower_mfi_account_f - .make_bank_repay_ix( - borrower_token_account_f_sol.key, - sol_bank, - 1_000, - Some(true), - ) - .await; - ixs.push(repay_ix); - } - - ixs.push(ComputeBudgetInstruction::set_compute_unit_limit(1_400_000)); - - let flash_loan_result = borrower_mfi_account_f - .try_flashloan(ixs, vec![], vec![]) - .await; - - assert!(flash_loan_result.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_account_health() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let flash_loan_result = borrower_mfi_account_f - .try_flashloan(vec![borrow_ix], vec![], vec![sol_bank.key]) - .await; - - assert_custom_error!( - flash_loan_result.unwrap_err(), - MarginfiError::RiskEngineInitRejected - ); - - Ok(()) -} - -#[tokio::test] -// Note: The flashloan flag is now deprecated -async fn flashloan_ok_missing_flag() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let repay_ix = borrower_mfi_account_f - .make_bank_repay_ix( - borrower_token_account_f_sol.key, - sol_bank, - 1_000, - Some(true), - ) - .await; - - let flash_loan_result = borrower_mfi_account_f - .try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![]) - .await; - - assert!(flash_loan_result.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_missing_fe_ix() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let repay_ix = borrower_mfi_account_f - .make_bank_repay_ix( - borrower_token_account_f_sol.key, - sol_bank, - 1_000, - Some(true), - ) - .await; - - let mut ixs = vec![borrow_ix, repay_ix]; - - let start_ix = borrower_mfi_account_f - .make_lending_account_start_flashloan_ix(ixs.len() as u64) - .await; - - ixs.insert(0, start_ix); - - let mut ctx = test_f.context.borrow_mut(); - - let tx = Transaction::new_signed_with_payer( - &ixs, - Some(&ctx.payer.pubkey().clone()), - &[&ctx.payer], - ctx.last_blockhash, - ); - - let res = ctx.banks_client.process_transaction(tx).await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_missing_invalid_sysvar_ixs() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let repay_ix = borrower_mfi_account_f - .make_bank_repay_ix( - borrower_token_account_f_sol.key, - sol_bank, - 1_000, - Some(true), - ) - .await; - - let mut ixs = vec![borrow_ix, repay_ix]; - - let start_ix = Instruction { - program_id: marginfi::id(), - accounts: marginfi::accounts::LendingAccountStartFlashloan { - marginfi_account: borrower_mfi_account_f.key, - signer: test_f.context.borrow().payer.pubkey(), - ixs_sysvar: Pubkey::default(), - } - .to_account_metas(Some(true)), - data: marginfi::instruction::LendingAccountStartFlashloan { - end_index: ixs.len() as u64 + 1, - } - .data(), - }; - - let end_ix = borrower_mfi_account_f - .make_lending_account_end_flashloan_ix(vec![], vec![]) - .await; - - ixs.insert(0, start_ix); - ixs.push(end_ix); - - let mut ctx = test_f.context.borrow_mut(); - - let tx = Transaction::new_signed_with_payer( - &ixs, - Some(&ctx.payer.pubkey().clone()), - &[&ctx.payer], - ctx.last_blockhash, - ); - - let res = ctx.banks_client.process_transaction(tx).await; - - assert!(res.is_err()); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_invalid_end_fl_order() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let mut ixs = vec![borrow_ix]; - - let start_ix = borrower_mfi_account_f - .make_lending_account_start_flashloan_ix(0) - .await; - - let end_ix = borrower_mfi_account_f - .make_lending_account_end_flashloan_ix(vec![], vec![]) - .await; - - ixs.insert(0, start_ix); - ixs.insert(0, end_ix); - - let mut ctx = test_f.context.borrow_mut(); - - let tx = Transaction::new_signed_with_payer( - &ixs, - Some(&ctx.payer.pubkey().clone()), - &[&ctx.payer], - ctx.last_blockhash, - ); - - let res = ctx.banks_client.process_transaction(tx).await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_invalid_end_fl_different_m_account() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let mut ixs = vec![borrow_ix]; - - let start_ix = borrower_mfi_account_f - .make_lending_account_start_flashloan_ix(ixs.len() as u64 + 1) - .await; - - let end_ix = lender_mfi_account_f - .make_lending_account_end_flashloan_ix(vec![], vec![]) - .await; - - ixs.insert(0, start_ix); - ixs.push(end_ix); - - let mut ctx = test_f.context.borrow_mut(); - - let tx = Transaction::new_signed_with_payer( - &ixs, - Some(&ctx.payer.pubkey().clone()), - &[&ctx.payer], - ctx.last_blockhash, - ); - - let res = ctx.banks_client.process_transaction(tx).await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); - - Ok(()) -} - -#[tokio::test] -async fn flashloan_fail_already_in_flashloan() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) - .await?; - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - - borrower_mfi_account_f - .try_set_flag(FLASHLOAN_ENABLED_FLAG) - .await?; - - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - // Borrow SOL - - let borrow_ix = borrower_mfi_account_f - .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) - .await; - - let mut ixs = vec![borrow_ix]; - - let start_ix = borrower_mfi_account_f - .make_lending_account_start_flashloan_ix(ixs.len() as u64 + 2) - .await; - - let end_ix = borrower_mfi_account_f - .make_lending_account_end_flashloan_ix(vec![], vec![]) - .await; - - ixs.insert(0, start_ix.clone()); - ixs.insert(0, start_ix.clone()); - ixs.push(end_ix); - - let mut ctx = test_f.context.borrow_mut(); - - let tx = Transaction::new_signed_with_payer( - &ixs, - Some(&ctx.payer.pubkey().clone()), - &[&ctx.payer], - ctx.last_blockhash, - ); - - let res = ctx.banks_client.process_transaction(tx).await; - - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); - - Ok(()) -} - -#[tokio::test] -async fn lending_account_close_balance() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - // Fund SOL lender - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_eq_bank, 1_000) - .await?; - - let lender_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) - .await?; - - let res = lender_mfi_account_f.try_balance_close(sol_bank).await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalBalanceState); - - // Fund SOL borrower - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_f_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - let borrower_token_account_f_sol_eq = test_f - .sol_equivalent_mint - .create_token_account_and_mint_to(1_000) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) - .await?; - - // Borrow SOL EQ - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol_eq.key, sol_eq_bank, 0.01) - .await; - - assert!(res.is_ok()); - - // Borrow SOL - let res = borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 0.01) - .await; - - assert!(res.is_ok()); - - // This issue is not that bad, because the user can still borrow other assets (isolated liab < empty threshold) - let res = borrower_mfi_account_f.try_balance_close(sol_bank).await; - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalBalanceState); - - // Let a second go b - { - let mut ctx = test_f.context.borrow_mut(); - let mut clock: Clock = ctx.banks_client.get_sysvar().await?; - // Advance clock by 1 second - clock.unix_timestamp += 1; - ctx.set_sysvar(&clock); - } - - // Repay isolated SOL EQ borrow successfully - let res = borrower_mfi_account_f - .try_bank_repay( - borrower_token_account_f_sol_eq.key, - sol_eq_bank, - 0.01, - Some(false), - ) - .await; - assert!(res.is_ok()); - - // Liability share in balance is smaller than 0.0001, so repay all should fail - let res = borrower_mfi_account_f - .try_bank_repay( - borrower_token_account_f_sol_eq.key, - sol_eq_bank, - 1, - Some(true), - ) - .await; - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::NoLiabilityFound); - - // This issue is not that bad, because the user can still borrow other assets (isolated liab < empty threshold) - let res = borrower_mfi_account_f.try_balance_close(sol_eq_bank).await; - assert!(res.is_ok()); - - Ok(()) -} - -// Test transfer account authority. -// No transfer flag set -- tx should fail. -// Set the flag and try again -- tx should succeed. -// RUST_BACKTRACE=1 cargo test-bpf marginfi_account_authority_transfer_no_flag_set -- --exact -#[tokio::test] -async fn marginfi_account_authority_transfer_no_flag_set() -> anyhow::Result<()> { - let test_f = TestFixture::new(None).await; - // Default account with no flags set - let marginfi_account = test_f.create_marginfi_account().await; - let new_authority = Keypair::new().pubkey(); - - let res = marginfi_account - .try_transfer_account_authority(new_authority, None) - .await; - - // Assert the response is an error due to the lack of the correct flag - assert!(res.is_err()); - assert_custom_error!( - res.unwrap_err(), - MarginfiError::IllegalAccountAuthorityTransfer - ); - - // set the flag on the account - marginfi_account - .try_set_flag(TRANSFER_AUTHORITY_ALLOWED_FLAG) - .await - .unwrap(); - - let new_authority_2 = Keypair::new().pubkey(); - let res = marginfi_account - .try_transfer_account_authority(new_authority_2, None) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_account_authority_transfer_not_account_owner() -> anyhow::Result<()> { - let test_f = TestFixture::new(None).await; - let marginfi_account = test_f.create_marginfi_account().await; - let new_authority = Keypair::new().pubkey(); - let signer = Keypair::new(); - - let res = marginfi_account - .try_transfer_account_authority(new_authority, Some(signer)) - .await; - - // Assert the response is an error due to fact that a non-owner of the - // acount attempted to initialize this account transfer - assert!(res.is_err()); - - Ok(()) -} - -#[tokio::test] -async fn account_field_values_reg() -> anyhow::Result<()> { - let account_fixtures_path = "tests/fixtures/marginfi_account"; - - // Sample 1 - - let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); - path.push("marginfi_account_sample_1.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let account = MarginfiAccount::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - account.authority, - pubkey!("Dq7wypbedtaqQK9QqEFvfrxc4ppfRGXCeTVd7ee7n2jw") - ); - - let balance_1 = account.lending_account.balances[0]; - assert!(balance_1.active); - assert_eq!( - I80F48::from(balance_1.asset_shares), - I80F48::from_str("1650216221.466876226897366").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.liability_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.emissions_outstanding), - I80F48::from_str("0").unwrap() - ); - - let balance_2 = account.lending_account.balances[1]; - assert!(balance_2.active); - assert_eq!( - I80F48::from(balance_2.asset_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_2.liability_shares), - I80F48::from_str("3806372611.588862122556122").unwrap() - ); - assert_eq!( - I80F48::from(balance_2.emissions_outstanding), - I80F48::from_str("0").unwrap() - ); - - // Sample 2 - - let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); - path.push("marginfi_account_sample_2.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let account = MarginfiAccount::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - account.authority, - pubkey!("3T1kGHp7CrdeW9Qj1t8NMc2Ks233RyvzVhoaUPWoBEFK") - ); - - let balance_1 = account.lending_account.balances[0]; - assert!(balance_1.active); - assert_eq!( - I80F48::from(balance_1.asset_shares), - I80F48::from_str("470.952530958931234").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.liability_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.emissions_outstanding), - I80F48::from_str("26891413.388324654086347").unwrap() - ); - - let balance_2 = account.lending_account.balances[1]; - assert!(!balance_2.active); - assert_eq!( - I80F48::from(balance_2.asset_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_2.liability_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_2.emissions_outstanding), - I80F48::from_str("0").unwrap() - ); - - // Sample 3 - - let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); - path.push("marginfi_account_sample_3.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let account = MarginfiAccount::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - account.authority, - pubkey!("7hmfVTuXc7HeX3YQjpiCXGVQuTeXonzjp795jorZukVR") - ); - - let balance_1 = account.lending_account.balances[0]; - assert!(!balance_1.active); - assert_eq!( - I80F48::from(balance_1.asset_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.liability_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(balance_1.emissions_outstanding), - I80F48::from_str("0").unwrap() - ); - - Ok(()) -} diff --git a/programs/marginfi/tests/marginfi_group.rs b/programs/marginfi/tests/marginfi_group.rs deleted file mode 100644 index 892a578cf..000000000 --- a/programs/marginfi/tests/marginfi_group.rs +++ /dev/null @@ -1,1575 +0,0 @@ -use std::{fs::File, io::Read, path::PathBuf, str::FromStr}; - -use anchor_lang::{prelude::Clock, AccountDeserialize, InstructionData, ToAccountMetas}; -use anyhow::bail; -use base64::{engine::general_purpose::STANDARD, Engine}; -use fixed::types::I80F48; -use fixed_macro::types::I80F48; -use fixtures::{assert_custom_error, assert_eq_noise, native, prelude::*}; -use marginfi::{ - constants::TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE, - prelude::{GroupConfig, MarginfiError, MarginfiGroup}, - state::marginfi_group::{ - Bank, BankConfig, BankConfigOpt, BankOperationalState, BankVaultType, InterestRateConfig, - }, -}; -use pretty_assertions::assert_eq; -use solana_account_decoder::UiAccountData; -use solana_cli_output::CliAccount; -use solana_program::{instruction::Instruction, pubkey, system_program}; -use solana_program_test::*; -use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction}; - -#[tokio::test] -async fn marginfi_group_create_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(None).await; - - // Create & initialize marginfi group - let marginfi_group_key = Keypair::new(); - - let accounts = marginfi::accounts::MarginfiGroupInitialize { - marginfi_group: marginfi_group_key.pubkey(), - admin: test_f.payer(), - system_program: system_program::id(), - }; - let init_marginfi_group_ix = Instruction { - program_id: marginfi::id(), - accounts: accounts.to_account_metas(Some(true)), - data: marginfi::instruction::MarginfiGroupInitialize {}.data(), - }; - let tx = Transaction::new_signed_with_payer( - &[init_marginfi_group_ix], - Some(&test_f.payer().clone()), - &[&test_f.payer_keypair(), &marginfi_group_key], - test_f.get_latest_blockhash().await, - ); - let res = test_f - .context - .borrow_mut() - .banks_client - .process_transaction(tx) - .await; - - assert!(res.is_ok()); - - // Fetch & deserialize marginfi group account - let marginfi_group: MarginfiGroup = test_f - .load_and_deserialize(&marginfi_group_key.pubkey()) - .await; - - // Check basic properties - assert_eq!(marginfi_group.admin, test_f.payer()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_config_check() -> anyhow::Result<()> { - let test_f = TestFixture::new(None).await; - - // Create & initialize marginfi group - let marginfi_group_key = Keypair::new(); - - let accounts = marginfi::accounts::MarginfiGroupInitialize { - marginfi_group: marginfi_group_key.pubkey(), - admin: test_f.payer(), - system_program: system_program::id(), - }; - let init_marginfi_group_ix = Instruction { - program_id: marginfi::id(), - accounts: accounts.to_account_metas(Some(true)), - data: marginfi::instruction::MarginfiGroupInitialize {}.data(), - }; - let tx = Transaction::new_signed_with_payer( - &[init_marginfi_group_ix], - Some(&test_f.payer().clone()), - &[&test_f.payer_keypair(), &marginfi_group_key], - test_f.get_latest_blockhash().await, - ); - test_f - .context - .borrow_mut() - .banks_client - .process_transaction(tx) - .await?; - - let bank_asset_mint_fixture = MintFixture::new(test_f.context.clone(), None, None).await; - - let bank = test_f - .marginfi_group - .try_lending_pool_add_bank(&bank_asset_mint_fixture, *DEFAULT_USDC_TEST_BANK_CONFIG) - .await?; - - let res = bank - .update_config(BankConfigOpt { - interest_rate_config: Some(marginfi::state::marginfi_group::InterestRateConfigOpt { - optimal_utilization_rate: Some(I80F48::from_num(0.91).into()), - plateau_interest_rate: Some(I80F48::from_num(0.44).into()), - max_interest_rate: Some(I80F48::from_num(1.44).into()), - insurance_fee_fixed_apr: Some(I80F48::from_num(0.13).into()), - insurance_ir_fee: Some(I80F48::from_num(0.11).into()), - protocol_fixed_fee_apr: Some(I80F48::from_num(0.51).into()), - protocol_ir_fee: Some(I80F48::from_num(0.011).into()), - }), - ..BankConfigOpt::default() - }) - .await; - - assert!(res.is_ok()); - - // Load bank and check each property in config matches - - let bank: Bank = test_f.load_and_deserialize(&bank.key).await; - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), - I80F48::from_num(0.91) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), - I80F48::from_num(0.44) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.max_interest_rate), - I80F48::from_num(1.44) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), - I80F48::from_num(0.13) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), - I80F48::from_num(0.11) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), - I80F48::from_num(0.51) - ); - - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), - I80F48::from_num(0.011) - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_add_bank_success() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(None).await; - - let bank_asset_mint_fixture = MintFixture::new(test_f.context.clone(), None, None).await; - - let res = test_f - .marginfi_group - .try_lending_pool_add_bank(&bank_asset_mint_fixture, *DEFAULT_USDC_TEST_BANK_CONFIG) - .await; - assert!(res.is_ok()); - - // Check bank is active - let bank = res.unwrap(); - let bank = test_f.try_load(&bank.key).await?; - assert!(bank.is_some()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_add_bank_with_seed_success() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(None).await; - - let bank_asset_mint_fixture = MintFixture::new(test_f.context.clone(), None, None).await; - let bank_seed = 1200_u64; - - let res = test_f - .marginfi_group - .try_lending_pool_add_bank_with_seed( - &bank_asset_mint_fixture, - *DEFAULT_USDC_TEST_BANK_CONFIG, - bank_seed, - ) - .await; - assert!(res.is_ok()); - - // Check bank is active - let bank = res.unwrap(); - let bank = test_f.try_load(&bank.key).await?; - assert!(bank.is_some()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_add_bank_failure_fake_pyth_feed() -> anyhow::Result<()> { - // Setup test executor with non-admin payer - let test_f = TestFixture::new(None).await; - - let bank_asset_mint_fixture = MintFixture::new(test_f.context.clone(), None, None).await; - - let res = test_f - .marginfi_group - .try_lending_pool_add_bank( - &bank_asset_mint_fixture, - BankConfig { - oracle_setup: marginfi::state::price::OracleSetup::PythEma, - oracle_keys: create_oracle_key_array(FAKE_PYTH_USDC_FEED), - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }, - ) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::InvalidOracleAccount); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_accrue_interest_rates_success_1() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - group_config: Some(GroupConfig { admin: None }), - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: Some(BankConfig { - interest_rate_config: InterestRateConfig { - optimal_utilization_rate: I80F48!(0.9).into(), - plateau_interest_rate: I80F48!(1).into(), - ..*DEFAULT_TEST_BANK_INTEREST_RATE_CONFIG - }, - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }), - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(100).await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_000) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 999) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 90) - .await?; - - { - let mut ctx = test_f.context.borrow_mut(); - let mut clock: Clock = ctx.banks_client.get_sysvar().await?; - // Advance clock by 1 year - clock.unix_timestamp += 365 * 24 * 60 * 60; - ctx.set_sysvar(&clock); - } - - test_f - .marginfi_group - .try_accrue_interest(usdc_bank_f) - .await?; - - let borrower_mfi_account = borrower_mfi_account_f.load().await; - let borrower_bank_account = borrower_mfi_account.lending_account.balances[1]; - let usdc_bank: Bank = usdc_bank_f.load().await; - let liabilities = - usdc_bank.get_liability_amount(borrower_bank_account.liability_shares.into())?; - - let lender_mfi_account = lender_mfi_account_f.load().await; - let lender_bank_account = lender_mfi_account.lending_account.balances[0]; - let assets = usdc_bank.get_asset_amount(lender_bank_account.asset_shares.into())?; - - assert_eq_noise!( - liabilities, - I80F48::from(native!(180, "USDC")), - I80F48!(100) - ); - assert_eq_noise!(assets, I80F48::from(native!(190, "USDC")), I80F48!(100)); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_accrue_interest_rates_success_2() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: Some(BankConfig { - deposit_limit: native!(1_000_000_000, "USDC"), - interest_rate_config: InterestRateConfig { - optimal_utilization_rate: I80F48!(0.9).into(), - plateau_interest_rate: I80F48!(1).into(), - protocol_fixed_fee_apr: I80F48!(0.01).into(), - insurance_fee_fixed_apr: I80F48!(0.01).into(), - ..*DEFAULT_TEST_BANK_INTEREST_RATE_CONFIG - }, - ..*DEFAULT_USDC_TEST_BANK_CONFIG - }), - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - deposit_limit: native!(200_000_000, "SOL"), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(10_000_000) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10_000_000) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 90_000_000) - .await?; - - // Advance clock by 1 minute - { - let mut ctx = test_f.context.borrow_mut(); - let mut clock: Clock = ctx.banks_client.get_sysvar().await?; - clock.unix_timestamp += 60; - ctx.set_sysvar(&clock); - } - - test_f - .marginfi_group - .try_accrue_interest(usdc_bank_f) - .await?; - - test_f.marginfi_group.try_collect_fees(usdc_bank_f).await?; - - let borrower_mfi_account = borrower_mfi_account_f.load().await; - let borrower_bank_account = borrower_mfi_account.lending_account.balances[1]; - let usdc_bank = usdc_bank_f.load().await; - let liabilities = - usdc_bank.get_liability_amount(borrower_bank_account.liability_shares.into())?; - - let lender_mfi_account = lender_mfi_account_f.load().await; - let lender_bank_account = lender_mfi_account.lending_account.balances[0]; - let assets = usdc_bank.get_asset_amount(lender_bank_account.asset_shares.into())?; - - assert_eq_noise!(liabilities, I80F48!(90000174657530), I80F48!(10)); - assert_eq_noise!(assets, I80F48!(100000171232876), I80F48!(10)); - - let protocol_fees = usdc_bank_f - .get_vault_token_account(BankVaultType::Fee) - .await; - let insurance_fees = usdc_bank_f - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq!(protocol_fees.balance().await, 1712328); - assert_eq!(insurance_fees.balance().await, 1712328); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_failure_not_bankrupt() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 1_001) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 10_000) - .await?; - - let res = test_f - .marginfi_group - .try_handle_bankruptcy(usdc_bank_f, &borrower_mfi_account_f) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::AccountNotBankrupt); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_success_no_debt() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 1_001) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 10_000) - .await?; - - let mut borrower_mfi_account = borrower_mfi_account_f.load().await; - borrower_mfi_account.lending_account.balances[0] - .asset_shares - .value = 0_i128.to_le_bytes(); - borrower_mfi_account_f - .set_account(&borrower_mfi_account) - .await?; - - let res = test_f - .marginfi_group - .try_handle_bankruptcy(sol_bank_f, &borrower_mfi_account_f) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::BalanceNotBadDebt); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_success_fully_insured() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - group_config: Some(GroupConfig { admin: None }), - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - })) - .await; - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit( - lender_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), - 100_000, - ) - .await?; - - let borrower_account = test_f.create_marginfi_account().await; - let borrower_deposit_account = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - - borrower_account - .try_bank_deposit( - borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), - 1_001, - ) - .await?; - - let borrower_borrow_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - - borrower_account - .try_bank_borrow( - borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), - 10_000, - ) - .await?; - - let mut borrower_mfi_account = borrower_account.load().await; - borrower_mfi_account.lending_account.balances[0] - .asset_shares - .value = 0_i128.to_le_bytes(); - borrower_account.set_account(&borrower_mfi_account).await?; - - { - let (insurance_vault, _) = test_f - .get_bank(&BankMint::USDC) - .get_vault(BankVaultType::Insurance); - test_f - .get_bank_mut(&BankMint::USDC) - .mint - .mint_to(&insurance_vault, 10_000) - .await; - } - - test_f - .marginfi_group - .try_handle_bankruptcy(test_f.get_bank(&BankMint::USDC), &borrower_account) - .await?; - - let borrower_mfi_account = borrower_account.load().await; - let borrower_usdc_balance = borrower_mfi_account.lending_account.balances[1]; - - assert_eq!( - I80F48::from(borrower_usdc_balance.liability_shares), - I80F48::ZERO - ); - - let lender_mfi_account = lender_mfi_account_f.load().await; - let usdc_bank = test_f.get_bank(&BankMint::USDC).load().await; - - let lender_usdc_value = usdc_bank.get_asset_amount( - lender_mfi_account.lending_account.balances[0] - .asset_shares - .into(), - )?; - - assert_eq_noise!( - lender_usdc_value, - I80F48::from(native!(100_000, "USDC")), - I80F48::ONE - ); - - let insurance_amount = test_f - .get_bank(&BankMint::USDC) - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq!(insurance_amount.balance().await, 0); - - // Test account is disabled - - // Deposit 1 SOL - let res = borrower_account - .try_bank_deposit( - borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), - 1, - ) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); - - // Withdraw 1 SOL - let res = borrower_account - .try_bank_withdraw( - borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), - 1, - None, - ) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); - - // Borrow 1 USDC - let res = borrower_account - .try_bank_borrow( - borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), - 1, - ) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); - - // Repay 1 USDC - let res = borrower_account - .try_bank_repay( - borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), - 1, - None, - ) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::AccountDisabled); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_success_partially_insured() -> anyhow::Result<()> { - let mut test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit( - lender_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), - 100_000, - ) - .await?; - - let borrower_account = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - borrower_account - .try_bank_deposit( - borrower_token_account_sol.key, - test_f.get_bank(&BankMint::SOL), - 1_001, - ) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_account - .try_bank_borrow( - borrower_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), - 10_000, - ) - .await?; - - let mut borrower_mfi_account = borrower_account.load().await; - borrower_mfi_account.lending_account.balances[0] - .asset_shares - .value = 0_i128.to_le_bytes(); - borrower_account.set_account(&borrower_mfi_account).await?; - - test_f - .usdc_mint - .mint_to( - &test_f - .get_bank(&BankMint::USDC) - .load() - .await - .insurance_vault, - 5_000, - ) - .await; - - test_f - .marginfi_group - .try_handle_bankruptcy(test_f.get_bank(&BankMint::USDC), &borrower_account) - .await?; - - let borrower_mfi_account = borrower_account.load().await; - let borrower_usdc_balance = borrower_mfi_account.lending_account.balances[1]; - - assert_eq!( - I80F48::from(borrower_usdc_balance.liability_shares), - I80F48::ZERO - ); - - let lender_mfi_account = lender_mfi_account_f.load().await; - let usdc_bank = test_f.get_bank(&BankMint::USDC).load().await; - - let lender_usdc_value = usdc_bank.get_asset_amount( - lender_mfi_account.lending_account.balances[0] - .asset_shares - .into(), - )?; - - assert_eq_noise!( - lender_usdc_value, - I80F48::from(native!(95_000, "USDC")), - I80F48::ONE - ); - - let insurance_amount = test_f - .get_bank(&BankMint::USDC) - .get_vault_token_account(BankVaultType::Insurance) - .await; - - assert_eq!(insurance_amount.balance().await, 0); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_success_not_insured() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - let borrower_account = test_f.create_marginfi_account().await; - let borrower_deposit_account = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - borrower_account - .try_bank_deposit(borrower_deposit_account.key, sol_bank_f, 1_001) - .await?; - let borrower_borrow_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_account - .try_bank_borrow(borrower_borrow_account.key, usdc_bank_f, 10_000) - .await?; - - let mut borrower_mfi_account = borrower_account.load().await; - borrower_mfi_account.lending_account.balances[0] - .asset_shares - .value = 0_i128.to_le_bytes(); - - borrower_account.set_account(&borrower_mfi_account).await?; - - test_f - .marginfi_group - .try_handle_bankruptcy(usdc_bank_f, &borrower_account) - .await?; - - let borrower_mfi_account = borrower_account.load().await; - let borrower_usdc_balance = borrower_mfi_account.lending_account.balances[1]; - - assert_eq!( - I80F48::from(borrower_usdc_balance.liability_shares), - I80F48::ZERO - ); - - let lender_mfi_account = lender_mfi_account_f.load().await; - let usdc_bank = usdc_bank_f.load().await; - - let lender_usdc_value = usdc_bank.get_asset_amount( - lender_mfi_account.lending_account.balances[0] - .asset_shares - .into(), - )?; - - assert_eq_noise!( - lender_usdc_value, - I80F48::from(native!(90_000, "USDC")), - I80F48::ONE - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_handle_bankruptcy_success_not_insured_3_depositors() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_1_mfi_account_f = test_f.create_marginfi_account().await; - let lender_1_token_account = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_1_mfi_account_f - .try_bank_deposit(lender_1_token_account.key, usdc_bank_f, 100_000) - .await?; - - let lender_2_mfi_account_f = test_f.create_marginfi_account().await; - let lender_2_token_account = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_2_mfi_account_f - .try_bank_deposit(lender_2_token_account.key, usdc_bank_f, 100_000) - .await?; - - let lender_3_mfi_account_f = test_f.create_marginfi_account().await; - let lender_3_token_account = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_3_mfi_account_f - .try_bank_deposit(lender_3_token_account.key, usdc_bank_f, 100_000) - .await?; - - let borrower_mfi_account_f = test_f.create_marginfi_account().await; - let borrower_token_account_sol = test_f - .sol_mint - .create_token_account_and_mint_to(1_001) - .await; - borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 1_001) - .await?; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; - borrower_mfi_account_f - .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 10_000) - .await?; - - let mut borrower_mfi_account = borrower_mfi_account_f.load().await; - borrower_mfi_account.lending_account.balances[0] - .asset_shares - .value = 0_i128.to_le_bytes(); - - borrower_mfi_account_f - .set_account(&borrower_mfi_account) - .await?; - - test_f - .marginfi_group - .try_handle_bankruptcy(usdc_bank_f, &borrower_mfi_account_f) - .await?; - - let borrower_mfi_account = borrower_mfi_account_f.load().await; - let borrower_usdc_balance = borrower_mfi_account.lending_account.balances[1]; - - assert_eq!( - I80F48::from(borrower_usdc_balance.liability_shares), - I80F48::ZERO - ); - - let lender_1_mfi_account = lender_1_mfi_account_f.load().await; - let usdc_bank = usdc_bank_f.load().await; - - let lender_usdc_value = usdc_bank.get_asset_amount( - lender_1_mfi_account.lending_account.balances[0] - .asset_shares - .into(), - )?; - - assert_eq_noise!( - lender_usdc_value, - I80F48::from(native!(96_666, "USDC")), - I80F48::from(native!(1, "USDC")) - ); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_bank_paused_should_error() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - config: None, - }], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - test_f - .marginfi_group - .try_lending_pool_configure_bank( - usdc_bank_f, - BankConfigOpt { - operational_state: Some(BankOperationalState::Paused), - ..BankConfigOpt::default() - }, - ) - .await?; - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - let res = lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::BankPaused); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_bank_reduce_only_withdraw_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - config: None, - }], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - test_f - .set_bank_operational_state(usdc_bank_f, BankOperationalState::ReduceOnly) - .await - .unwrap(); - - let res = lender_mfi_account_f - .try_bank_withdraw(lender_token_account_usdc.key, usdc_bank_f, 0, Some(true)) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_bank_reduce_only_deposit_success() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_1_mfi_account = test_f.create_marginfi_account().await; - let lender_1_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - lender_1_mfi_account - .try_bank_deposit(lender_1_token_account_sol.key, sol_bank_f, 100) - .await?; - - let lender_2_mfi_account = test_f.create_marginfi_account().await; - let lender_2_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - lender_2_mfi_account - .try_bank_deposit(lender_2_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - let lender_2_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - lender_2_mfi_account - .try_bank_borrow(lender_2_token_account_sol.key, sol_bank_f, 1) - .await?; - - test_f - .set_bank_operational_state(usdc_bank_f, BankOperationalState::ReduceOnly) - .await - .unwrap(); - - let res = lender_2_mfi_account - .try_bank_repay(lender_2_token_account_sol.key, sol_bank_f, 1, None) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_bank_reduce_only_borrow_failure() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - config: None, - }, - TestBankSetting { - mint: BankMint::SOL, - config: Some(BankConfig { - asset_weight_init: I80F48!(1).into(), - ..*DEFAULT_SOL_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); - - let lender_mfi_account = test_f.create_marginfi_account().await; - let lender_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - lender_mfi_account - .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 100) - .await?; - - let borrower_mfi_account = test_f.create_marginfi_account().await; - let borrower_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - borrower_mfi_account - .try_bank_deposit(borrower_token_account_usdc.key, usdc_bank_f, 100_000) - .await?; - - test_f - .set_bank_operational_state(sol_bank_f, BankOperationalState::ReduceOnly) - .await - .unwrap(); - - let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; - let res = borrower_mfi_account - .try_bank_borrow(borrower_token_account_sol.key, sol_bank_f, 1) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::BankReduceOnly); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_bank_reduce_only_deposit_failure() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings { - banks: vec![TestBankSetting { - mint: BankMint::USDC, - config: None, - }], - group_config: Some(GroupConfig { admin: None }), - })) - .await; - - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - - test_f - .set_bank_operational_state(usdc_bank_f, BankOperationalState::ReduceOnly) - .await - .unwrap(); - - let lender_mfi_account_f = test_f.create_marginfi_account().await; - let lender_token_account_usdc = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - - let res = lender_mfi_account_f - .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::BankReduceOnly); - - Ok(()) -} - -#[tokio::test] -async fn marginfi_group_init_limit_0() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - - usdc_bank - .update_config(BankConfigOpt { - total_asset_value_init_limit: Some(101), - ..BankConfigOpt::default() - }) - .await?; - - let sol_depositor = test_f.create_marginfi_account().await; - let usdc_depositor = test_f.create_marginfi_account().await; - - let sol_token_account = test_f.sol_mint.create_token_account_and_mint_to(100).await; - - sol_depositor - .try_bank_deposit(sol_token_account.key, sol_bank, 100) - .await?; - - let usdc_token_account = test_f - .usdc_mint - .create_token_account_and_mint_to(100_000) - .await; - - sol_depositor - .try_bank_deposit(usdc_token_account.key, usdc_bank, 1900) - .await?; - - usdc_depositor - .try_bank_deposit(usdc_token_account.key, usdc_bank, 100) - .await?; - - // Borrowing 10 SOL should fail bc of init limit - let depositor_sol_account = sol_bank.mint.create_token_account_and_mint_to(0).await; - let res = usdc_depositor - .try_bank_borrow(depositor_sol_account.key, sol_bank, 9.9) - .await; - - assert!(res.is_err()); - assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); - - sol_depositor - .try_bank_withdraw(usdc_token_account.key, usdc_bank, 1900, Some(true)) - .await?; - - // Borrowing 10 SOL should succeed now - let res = usdc_depositor - .try_bank_borrow(depositor_sol_account.key, sol_bank, 10) - .await; - - usdc_bank - .update_config(BankConfigOpt { - total_asset_value_init_limit: Some(TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE), - ..BankConfigOpt::default() - }) - .await?; - - assert!(res.is_ok()); - - sol_depositor - .try_bank_deposit(usdc_token_account.key, usdc_bank, 1901) - .await?; - - usdc_depositor - .try_bank_deposit(usdc_token_account.key, usdc_bank, 100) - .await?; - - // Borrowing 10 SOL should succeed now - let res = usdc_depositor - .try_bank_borrow(depositor_sol_account.key, sol_bank, 10) - .await; - - assert!(res.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn bank_field_values_reg() -> anyhow::Result<()> { - let bank_fixtures_path = "tests/fixtures/bank"; - - // Sample 1 (Jito) - - let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); - path.push("bank_sample_1.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let bank = Bank::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - bank.mint, - pubkey!("J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn") - ); - assert_eq!(bank.mint_decimals, 9); - assert_eq!( - bank.group, - pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") - ); - assert_eq!( - I80F48::from(bank.asset_share_value), - I80F48::from_str("1.000561264812955").unwrap() - ); - assert_eq!( - I80F48::from(bank.liability_share_value), - I80F48::from_str("1.00737674726716").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_insurance_fees_outstanding), - I80F48::from_str("61174.580321107215052").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_group_fees_outstanding), - I80F48::from_str("35660072279.35465946938668").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_liability_shares), - I80F48::from_str("79763493059362.858709822356737").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_asset_shares), - I80F48::from_str("998366336320727.44918120920092").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_init), - I80F48::from_str("0.649999976158142").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_maint), - I80F48::from_str("0.80000001192093").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_init), - I80F48::from_str("1.3").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_maint), - I80F48::from_str("1.2").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), - I80F48::from_str("0.8").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), - I80F48::from_str("0.1").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.max_interest_rate), - I80F48::from_str("3").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), - I80F48::from_str("0.01").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), - I80F48::from_str("0.05").unwrap() - ); - - // Sample 2 (META) - - let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); - path.push("bank_sample_2.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let bank = Bank::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - bank.mint, - pubkey!("METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr") - ); - assert_eq!(bank.mint_decimals, 9); - assert_eq!( - bank.group, - pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") - ); - assert_eq!( - I80F48::from(bank.asset_share_value), - I80F48::from_str("1").unwrap() - ); - assert_eq!( - I80F48::from(bank.liability_share_value), - I80F48::from_str("1").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_insurance_fees_outstanding), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_group_fees_outstanding), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_liability_shares), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_asset_shares), - I80F48::from_str("698503862367").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_init), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_maint), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_init), - I80F48::from_str("2.5").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_maint), - I80F48::from_str("1.5").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), - I80F48::from_str("0.8").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), - I80F48::from_str("0.1").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.max_interest_rate), - I80F48::from_str("3").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), - I80F48::from_str("0.01").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), - I80F48::from_str("0.05").unwrap() - ); - - // Sample 3 (USDT) - - let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); - path.push("bank_sample_3.json"); - let mut file = File::open(&path).unwrap(); - let mut account_info_raw = String::new(); - file.read_to_string(&mut account_info_raw).unwrap(); - - let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); - let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { - bail!("Expecting Binary format for fixtures") - }; - let bank = Bank::try_deserialize(&mut STANDARD.decode(data)?.as_slice())?; - - assert_eq!( - bank.mint, - pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB") - ); - assert_eq!(bank.mint_decimals, 6); - assert_eq!( - bank.group, - pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") - ); - assert_eq!( - I80F48::from(bank.asset_share_value), - I80F48::from_str("1.063003765188338").unwrap() - ); - assert_eq!( - I80F48::from(bank.liability_share_value), - I80F48::from_str("1.12089611736063").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_insurance_fees_outstanding), - I80F48::from_str("45839.746526861401865").unwrap() - ); - assert_eq!( - I80F48::from(bank.collected_group_fees_outstanding), - I80F48::from_str("28634360131.219557095675654").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_liability_shares), - I80F48::from_str("32109684419718.204607882232235").unwrap() - ); - assert_eq!( - I80F48::from(bank.total_asset_shares), - I80F48::from_str("43231381120800.339303417329994").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_init), - I80F48::from_str("1").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.asset_weight_maint), - I80F48::from_str("1").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_init), - I80F48::from_str("1.25").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.liability_weight_maint), - I80F48::from_str("1.1").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), - I80F48::from_str("0.8").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), - I80F48::from_str("0.2").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.max_interest_rate), - I80F48::from_str("4").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), - I80F48::from_str("0").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), - I80F48::from_str("0.01").unwrap() - ); - assert_eq!( - I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), - I80F48::from_str("0.05").unwrap() - ); - - Ok(()) -} diff --git a/programs/marginfi/tests/bank_ignore_stale_isolated_banks.rs b/programs/marginfi/tests/misc/bank_ignore_stale_isolated_banks.rs similarity index 86% rename from programs/marginfi/tests/bank_ignore_stale_isolated_banks.rs rename to programs/marginfi/tests/misc/bank_ignore_stale_isolated_banks.rs index bcc4ab65c..878b45a08 100644 --- a/programs/marginfi/tests/bank_ignore_stale_isolated_banks.rs +++ b/programs/marginfi/tests/misc/bank_ignore_stale_isolated_banks.rs @@ -15,11 +15,12 @@ use solana_program_test::tokio; async fn stale_bank_should_error() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); // Make SOLE feed stale + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f .set_pyth_oracle_timestamp(PYTH_SOL_EQUIVALENT_FEED, 0) @@ -47,7 +48,7 @@ async fn stale_bank_should_error() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 500) @@ -73,11 +74,12 @@ async fn stale_bank_should_error() -> anyhow::Result<()> { async fn non_stale_bank_should_error() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let sol_bank = test_f.get_bank(&BankMint::Sol); // Make USDC feed stale + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 0).await; test_f.set_pyth_oracle_timestamp(PYTH_SOL_FEED, 120).await; test_f @@ -105,7 +107,7 @@ async fn non_stale_bank_should_error() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 15) @@ -128,12 +130,13 @@ async fn non_stale_bank_should_error() -> anyhow::Result<()> { #[tokio::test] /// Borrowing with deposits to a non isolated stale bank should error async fn isolated_stale_should_not_error() -> anyhow::Result<()> { - let test_f = TestFixture::new(Some(TestSettings::all_banks_one_isolated())).await; + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); - let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + let sol_eq_iso_bank = test_f.get_bank(&BankMint::SolEqIsolated); + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_SOL_FEED, 120).await; test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f.advance_time(120).await; @@ -158,14 +161,14 @@ async fn isolated_stale_should_not_error() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) .await?; borrower_mfi_account_f - .try_bank_deposit(borrower_token_account_f_sol_eq.key, sol_eq_bank, 1_000) + .try_bank_deposit(borrower_token_account_f_sol_eq.key, sol_eq_iso_bank, 1_000) .await?; // Borrow SOL diff --git a/programs/marginfi/tests/bank_variable_oracle_staleness.rs b/programs/marginfi/tests/misc/bank_variable_oracle_staleness.rs similarity index 92% rename from programs/marginfi/tests/bank_variable_oracle_staleness.rs rename to programs/marginfi/tests/misc/bank_variable_oracle_staleness.rs index 2a612e4c1..bba006d14 100644 --- a/programs/marginfi/tests/bank_variable_oracle_staleness.rs +++ b/programs/marginfi/tests/misc/bank_variable_oracle_staleness.rs @@ -13,11 +13,12 @@ use solana_program_test::tokio; async fn bank_oracle_staleness_test() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); // Make SOLE feed stale + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f .set_pyth_oracle_timestamp(PYTH_SOL_EQUIVALENT_FEED, 0) @@ -45,7 +46,7 @@ async fn bank_oracle_staleness_test() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 500) diff --git a/programs/marginfi/tests/misc/collateral_value_cap.rs b/programs/marginfi/tests/misc/collateral_value_cap.rs new file mode 100644 index 000000000..129c07622 --- /dev/null +++ b/programs/marginfi/tests/misc/collateral_value_cap.rs @@ -0,0 +1,88 @@ +use fixtures::{assert_custom_error, prelude::*}; +use marginfi::{ + constants::TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE, prelude::MarginfiError, + state::marginfi_group::BankConfigOpt, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; + +#[tokio::test] +async fn marginfi_group_init_limit_0() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + usdc_bank + .update_config(BankConfigOpt { + total_asset_value_init_limit: Some(101), + ..BankConfigOpt::default() + }) + .await?; + + let sol_depositor = test_f.create_marginfi_account().await; + let usdc_depositor = test_f.create_marginfi_account().await; + + let sol_token_account = test_f.sol_mint.create_token_account_and_mint_to(100).await; + + sol_depositor + .try_bank_deposit(sol_token_account.key, sol_bank, 100) + .await?; + + let usdc_token_account = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + + sol_depositor + .try_bank_deposit(usdc_token_account.key, usdc_bank, 1900) + .await?; + + usdc_depositor + .try_bank_deposit(usdc_token_account.key, usdc_bank, 100) + .await?; + + // Borrowing 10 SOL should fail bc of init limit + let depositor_sol_account = sol_bank.mint.create_empty_token_account().await; + let res = usdc_depositor + .try_bank_borrow(depositor_sol_account.key, sol_bank, 9.9) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); + + sol_depositor + .try_bank_withdraw(usdc_token_account.key, usdc_bank, 1900, Some(true)) + .await?; + + // Borrowing 10 SOL should succeed now + let res = usdc_depositor + .try_bank_borrow(depositor_sol_account.key, sol_bank, 10) + .await; + + usdc_bank + .update_config(BankConfigOpt { + total_asset_value_init_limit: Some(TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE), + ..BankConfigOpt::default() + }) + .await?; + + assert!(res.is_ok()); + + sol_depositor + .try_bank_deposit(usdc_token_account.key, usdc_bank, 1901) + .await?; + + usdc_depositor + .try_bank_deposit(usdc_token_account.key, usdc_bank, 100) + .await?; + + // Borrowing 10 SOL should succeed now + let res = usdc_depositor + .try_bank_borrow(depositor_sol_account.key, sol_bank, 10) + .await; + + assert!(res.is_ok()); + + Ok(()) +} diff --git a/programs/marginfi/tests/misc/mod.rs b/programs/marginfi/tests/misc/mod.rs new file mode 100644 index 000000000..252269a9f --- /dev/null +++ b/programs/marginfi/tests/misc/mod.rs @@ -0,0 +1,9 @@ +mod bank_ignore_stale_isolated_banks; +mod bank_variable_oracle_staleness; +mod collateral_value_cap; +mod operational_state; +mod pyth_push; +mod real_oracle_data; +mod regression; +mod risk_engine_flexible_oracle_checks; +mod token_extensions; diff --git a/programs/marginfi/tests/misc/operational_state.rs b/programs/marginfi/tests/misc/operational_state.rs new file mode 100644 index 000000000..657ddce82 --- /dev/null +++ b/programs/marginfi/tests/misc/operational_state.rs @@ -0,0 +1,236 @@ +use fixed_macro::types::I80F48; +use fixtures::{assert_custom_error, prelude::*}; +use marginfi::{ + prelude::{GroupConfig, MarginfiError}, + state::marginfi_group::{BankConfig, BankConfigOpt, BankOperationalState}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; + +#[tokio::test] +async fn marginfi_group_bank_paused_should_error() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![TestBankSetting { + mint: BankMint::Usdc, + config: None, + }], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + + test_f + .marginfi_group + .try_lending_pool_configure_bank( + usdc_bank_f, + BankConfigOpt { + operational_state: Some(BankOperationalState::Paused), + ..BankConfigOpt::default() + }, + ) + .await?; + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + let res = lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::BankPaused); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_bank_reduce_only_withdraw_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![TestBankSetting { + mint: BankMint::Usdc, + config: None, + }], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) + .await?; + + usdc_bank_f + .update_config(BankConfigOpt { + operational_state: Some(BankOperationalState::ReduceOnly), + ..Default::default() + }) + .await?; + + let res = lender_mfi_account_f + .try_bank_withdraw(lender_token_account_usdc.key, usdc_bank_f, 0, Some(true)) + .await; + + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_bank_reduce_only_deposit_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: None, + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_1_mfi_account = test_f.create_marginfi_account().await; + let lender_1_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + lender_1_mfi_account + .try_bank_deposit(lender_1_token_account_sol.key, sol_bank_f, 100) + .await?; + + let lender_2_mfi_account = test_f.create_marginfi_account().await; + let lender_2_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + lender_2_mfi_account + .try_bank_deposit(lender_2_token_account_usdc.key, usdc_bank_f, 100_000) + .await?; + + let lender_2_token_account_sol = test_f.sol_mint.create_empty_token_account().await; + lender_2_mfi_account + .try_bank_borrow(lender_2_token_account_sol.key, sol_bank_f, 1) + .await?; + + usdc_bank_f + .update_config(BankConfigOpt { + operational_state: Some(BankOperationalState::ReduceOnly), + ..Default::default() + }) + .await?; + + let res = lender_2_mfi_account + .try_bank_repay(lender_2_token_account_sol.key, sol_bank_f, 1, None) + .await; + + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_bank_reduce_only_borrow_failure() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: None, + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + lender_mfi_account + .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 100) + .await?; + + let borrower_mfi_account = test_f.create_marginfi_account().await; + let borrower_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + borrower_mfi_account + .try_bank_deposit(borrower_token_account_usdc.key, usdc_bank_f, 100_000) + .await?; + + sol_bank_f + .update_config(BankConfigOpt { + operational_state: Some(BankOperationalState::ReduceOnly), + ..Default::default() + }) + .await?; + + let borrower_token_account_sol = test_f.sol_mint.create_empty_token_account().await; + let res = borrower_mfi_account + .try_bank_borrow(borrower_token_account_sol.key, sol_bank_f, 1) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::BankReduceOnly); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_group_bank_reduce_only_deposit_failure() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![TestBankSetting { + mint: BankMint::Usdc, + config: None, + }], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + + usdc_bank_f + .update_config(BankConfigOpt { + operational_state: Some(BankOperationalState::ReduceOnly), + ..Default::default() + }) + .await?; + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(100_000) + .await; + + let res = lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 100_000) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::BankReduceOnly); + + Ok(()) +} diff --git a/programs/marginfi/tests/misc/pyth_push.rs b/programs/marginfi/tests/misc/pyth_push.rs new file mode 100644 index 000000000..7f7bcf986 --- /dev/null +++ b/programs/marginfi/tests/misc/pyth_push.rs @@ -0,0 +1,261 @@ +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{ + assert_custom_error, assert_eq_noise, native, + test::{ + BankMint, TestBankSetting, TestFixture, TestSettings, + DEFAULT_SOL_TEST_PYTH_PUSH_FULLV_BANK_CONFIG, DEFAULT_SOL_TEST_PYTH_PUSH_PARTV_BANK_CONFIG, + DEFAULT_USDC_TEST_BANK_CONFIG, + }, +}; +use marginfi::{ + errors::MarginfiError, + state::marginfi_group::{Bank, BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, +}; +use solana_program_test::tokio; + +#[tokio::test] +async fn pyth_push_fullv_borrow() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(*DEFAULT_USDC_TEST_BANK_CONFIG), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(*DEFAULT_SOL_TEST_PYTH_PUSH_FULLV_BANK_CONFIG), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 101) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 100) + .await; + + assert!(res.is_ok()); + + // Check token balances are correct + assert_eq!( + borrower_token_account_f_usdc.balance().await, + native!(0, "USDC") + ); + + assert_eq!( + borrower_token_account_f_sol.balance().await, + native!(100, "SOL") + ); + + Ok(()) +} + +#[tokio::test] +async fn pyth_push_partv_borrow() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(*DEFAULT_USDC_TEST_BANK_CONFIG), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(*DEFAULT_SOL_TEST_PYTH_PUSH_PARTV_BANK_CONFIG), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 101) + .await; + + // TODO: Bad error, need to improve the flexible risk engine logic to correctly pass the + // unerlying error. + assert_custom_error!(res.unwrap_err(), MarginfiError::StaleOracle); + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 100) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::StaleOracle); + + Ok(()) +} + +#[tokio::test] +async fn pyth_push_fullv_liquidate() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + asset_weight_maint: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_PYTH_PUSH_FULLV_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(2_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + + // Borrower deposits 100 SOL worth of $1000 + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) + .await?; + + // Borrower borrows $999 + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) + .await?; + + // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await?; + + // Checks + let sol_bank: Bank = sol_bank_f.load().await; + let usdc_bank: Bank = usdc_bank_f.load().await; + + let depositor_ma = lender_mfi_account_f.load().await; + let borrower_ma = borrower_mfi_account_f.load().await; + + // Depositors should have 1 SOL + assert_eq!( + sol_bank + .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1, "SOL")) + ); + + // Depositors should have 1990.25 USDC + assert_eq_noise!( + usdc_bank + .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1990.25, "USDC", f64)), + native!(0.00001, "USDC", f64) + ); + + // Borrower should have 99 SOL + assert_eq!( + sol_bank + .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(99, "SOL")) + ); + + // Borrower should have 989.50 USDC + assert_eq_noise!( + usdc_bank + .get_liability_amount( + borrower_ma.lending_account.balances[1] + .liability_shares + .into() + ) + .unwrap(), + I80F48::from(native!(989.50, "USDC", f64)), + native!(0.00001, "USDC", f64) + ); + + // Check insurance fund fee + let insurance_fund_usdc = usdc_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await; + + assert_eq_noise!( + insurance_fund_usdc.balance().await as i64, + native!(0.25, "USDC", f64) as i64, + 1 + ); + + Ok(()) +} diff --git a/programs/marginfi/tests/misc/real_oracle_data.rs b/programs/marginfi/tests/misc/real_oracle_data.rs new file mode 100644 index 000000000..1bbeb005b --- /dev/null +++ b/programs/marginfi/tests/misc/real_oracle_data.rs @@ -0,0 +1,156 @@ +use fixtures::{ + assert_custom_error, native, + test::{ + BankMint, TestBankSetting, TestFixture, TestSettings, + DEFAULT_PYTH_PUSH_SOL_TEST_REAL_BANK_CONFIG, DEFAULT_SOL_TEST_REAL_BANK_CONFIG, + DEFAULT_USDC_TEST_REAL_BANK_CONFIG, + }, +}; +use marginfi::{errors::MarginfiError, state::marginfi_group::GroupConfig}; +use solana_program_test::tokio; + +#[tokio::test] +async fn real_oracle_marginfi_account_borrow_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(*DEFAULT_USDC_TEST_REAL_BANK_CONFIG), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(*DEFAULT_SOL_TEST_REAL_BANK_CONFIG), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + test_f.set_time(1720094628); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 9) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 7) + .await; + + assert!(res.is_ok()); + + // Check token balances are correct + assert_eq!( + borrower_token_account_f_usdc.balance().await, + native!(0, "USDC") + ); + + assert_eq!( + borrower_token_account_f_sol.balance().await, + native!(7, "SOL") + ); + + // TODO: check health is sane + + Ok(()) +} + +#[tokio::test] +async fn real_oracle_pyth_push_marginfi_account_borrow_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(*DEFAULT_USDC_TEST_REAL_BANK_CONFIG), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(*DEFAULT_PYTH_PUSH_SOL_TEST_REAL_BANK_CONFIG), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + test_f.set_time(1720094628); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 7) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 6) + .await; + + assert!(res.is_ok()); + + // Check token balances are correct + assert_eq!( + borrower_token_account_f_usdc.balance().await, + native!(0, "USDC") + ); + + assert_eq!( + borrower_token_account_f_sol.balance().await, + native!(6, "SOL") + ); + + Ok(()) +} diff --git a/programs/marginfi/tests/misc/regression.rs b/programs/marginfi/tests/misc/regression.rs new file mode 100644 index 000000000..7fae0d7d6 --- /dev/null +++ b/programs/marginfi/tests/misc/regression.rs @@ -0,0 +1,433 @@ +use std::{fs::File, io::Read, path::PathBuf, str::FromStr}; + +use anchor_lang::AccountDeserialize; +use anyhow::bail; +use base64::{prelude::BASE64_STANDARD, Engine}; +use fixed::types::I80F48; +use marginfi::state::{marginfi_account::MarginfiAccount, marginfi_group::Bank}; +use solana_account_decoder::UiAccountData; +use solana_cli_output::CliAccount; +use solana_program::pubkey; +use solana_program_test::tokio; + +#[tokio::test] +async fn account_field_values_reg() -> anyhow::Result<()> { + let account_fixtures_path = "tests/fixtures/marginfi_account"; + + // Sample 1 + + let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); + path.push("marginfi_account_sample_1.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let account = MarginfiAccount::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + account.authority, + pubkey!("Dq7wypbedtaqQK9QqEFvfrxc4ppfRGXCeTVd7ee7n2jw") + ); + + let balance_1 = account.lending_account.balances[0]; + assert!(balance_1.active); + assert_eq!( + I80F48::from(balance_1.asset_shares), + I80F48::from_str("1650216221.466876226897366").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.liability_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.emissions_outstanding), + I80F48::from_str("0").unwrap() + ); + + let balance_2 = account.lending_account.balances[1]; + assert!(balance_2.active); + assert_eq!( + I80F48::from(balance_2.asset_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_2.liability_shares), + I80F48::from_str("3806372611.588862122556122").unwrap() + ); + assert_eq!( + I80F48::from(balance_2.emissions_outstanding), + I80F48::from_str("0").unwrap() + ); + + // Sample 2 + + let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); + path.push("marginfi_account_sample_2.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let account = MarginfiAccount::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + account.authority, + pubkey!("3T1kGHp7CrdeW9Qj1t8NMc2Ks233RyvzVhoaUPWoBEFK") + ); + + let balance_1 = account.lending_account.balances[0]; + assert!(balance_1.active); + assert_eq!( + I80F48::from(balance_1.asset_shares), + I80F48::from_str("470.952530958931234").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.liability_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.emissions_outstanding), + I80F48::from_str("26891413.388324654086347").unwrap() + ); + + let balance_2 = account.lending_account.balances[1]; + assert!(!balance_2.active); + assert_eq!( + I80F48::from(balance_2.asset_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_2.liability_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_2.emissions_outstanding), + I80F48::from_str("0").unwrap() + ); + + // Sample 3 + + let mut path = PathBuf::from_str(account_fixtures_path).unwrap(); + path.push("marginfi_account_sample_3.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let account = MarginfiAccount::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + account.authority, + pubkey!("7hmfVTuXc7HeX3YQjpiCXGVQuTeXonzjp795jorZukVR") + ); + + let balance_1 = account.lending_account.balances[0]; + assert!(!balance_1.active); + assert_eq!( + I80F48::from(balance_1.asset_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.liability_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(balance_1.emissions_outstanding), + I80F48::from_str("0").unwrap() + ); + + Ok(()) +} + +#[tokio::test] +async fn bank_field_values_reg() -> anyhow::Result<()> { + let bank_fixtures_path = "tests/fixtures/bank"; + + // Sample 1 (Jito) + + let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); + path.push("bank_sample_1.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let bank = Bank::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + bank.mint, + pubkey!("J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn") + ); + assert_eq!(bank.mint_decimals, 9); + assert_eq!( + bank.group, + pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") + ); + assert_eq!( + I80F48::from(bank.asset_share_value), + I80F48::from_str("1.000561264812955").unwrap() + ); + assert_eq!( + I80F48::from(bank.liability_share_value), + I80F48::from_str("1.00737674726716").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_insurance_fees_outstanding), + I80F48::from_str("61174.580321107215052").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_group_fees_outstanding), + I80F48::from_str("35660072279.35465946938668").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_liability_shares), + I80F48::from_str("79763493059362.858709822356737").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_asset_shares), + I80F48::from_str("998366336320727.44918120920092").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_init), + I80F48::from_str("0.649999976158142").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_maint), + I80F48::from_str("0.80000001192093").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_init), + I80F48::from_str("1.3").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_maint), + I80F48::from_str("1.2").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), + I80F48::from_str("0.8").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), + I80F48::from_str("0.1").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.max_interest_rate), + I80F48::from_str("3").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), + I80F48::from_str("0.01").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), + I80F48::from_str("0.05").unwrap() + ); + + // Sample 2 (META) + + let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); + path.push("bank_sample_2.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let bank = Bank::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + bank.mint, + pubkey!("METADDFL6wWMWEoKTFJwcThTbUmtarRJZjRpzUvkxhr") + ); + assert_eq!(bank.mint_decimals, 9); + assert_eq!( + bank.group, + pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") + ); + assert_eq!( + I80F48::from(bank.asset_share_value), + I80F48::from_str("1").unwrap() + ); + assert_eq!( + I80F48::from(bank.liability_share_value), + I80F48::from_str("1").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_insurance_fees_outstanding), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_group_fees_outstanding), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_liability_shares), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_asset_shares), + I80F48::from_str("698503862367").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_init), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_maint), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_init), + I80F48::from_str("2.5").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_maint), + I80F48::from_str("1.5").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), + I80F48::from_str("0.8").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), + I80F48::from_str("0.1").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.max_interest_rate), + I80F48::from_str("3").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), + I80F48::from_str("0.01").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), + I80F48::from_str("0.05").unwrap() + ); + + // Sample 3 (USDT) + + let mut path = PathBuf::from_str(bank_fixtures_path).unwrap(); + path.push("bank_sample_3.json"); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let UiAccountData::Binary(data, _) = account.keyed_account.account.data else { + bail!("Expecting Binary format for fixtures") + }; + let bank = Bank::try_deserialize(&mut BASE64_STANDARD.decode(data)?.as_slice())?; + + assert_eq!( + bank.mint, + pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB") + ); + assert_eq!(bank.mint_decimals, 6); + assert_eq!( + bank.group, + pubkey!("4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8") + ); + assert_eq!( + I80F48::from(bank.asset_share_value), + I80F48::from_str("1.063003765188338").unwrap() + ); + assert_eq!( + I80F48::from(bank.liability_share_value), + I80F48::from_str("1.12089611736063").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_insurance_fees_outstanding), + I80F48::from_str("45839.746526861401865").unwrap() + ); + assert_eq!( + I80F48::from(bank.collected_group_fees_outstanding), + I80F48::from_str("28634360131.219557095675654").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_liability_shares), + I80F48::from_str("32109684419718.204607882232235").unwrap() + ); + assert_eq!( + I80F48::from(bank.total_asset_shares), + I80F48::from_str("43231381120800.339303417329994").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_init), + I80F48::from_str("1").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.asset_weight_maint), + I80F48::from_str("1").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_init), + I80F48::from_str("1.25").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.liability_weight_maint), + I80F48::from_str("1.1").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.optimal_utilization_rate), + I80F48::from_str("0.8").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.plateau_interest_rate), + I80F48::from_str("0.2").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.max_interest_rate), + I80F48::from_str("4").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_fee_fixed_apr), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.insurance_ir_fee), + I80F48::from_str("0").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_fixed_fee_apr), + I80F48::from_str("0.01").unwrap() + ); + assert_eq!( + I80F48::from(bank.config.interest_rate_config.protocol_ir_fee), + I80F48::from_str("0.05").unwrap() + ); + + Ok(()) +} diff --git a/programs/marginfi/tests/risk_engine_flexible_oracle_checks.rs b/programs/marginfi/tests/misc/risk_engine_flexible_oracle_checks.rs similarity index 89% rename from programs/marginfi/tests/risk_engine_flexible_oracle_checks.rs rename to programs/marginfi/tests/misc/risk_engine_flexible_oracle_checks.rs index 587ea3fdd..e1d1fe4de 100644 --- a/programs/marginfi/tests/risk_engine_flexible_oracle_checks.rs +++ b/programs/marginfi/tests/misc/risk_engine_flexible_oracle_checks.rs @@ -17,11 +17,12 @@ use solana_program_test::tokio; async fn re_one_oracle_stale_failure() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); // Make SOLE feed stale + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_SOL_FEED, 120).await; test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f.advance_time(120).await; @@ -46,7 +47,7 @@ async fn re_one_oracle_stale_failure() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 500) @@ -100,10 +101,11 @@ async fn re_one_oracle_stale_failure() -> anyhow::Result<()> { async fn re_one_oracle_stale_success() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_SOL_FEED, 120).await; test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f.advance_time(120).await; @@ -128,7 +130,7 @@ async fn re_one_oracle_stale_success() -> anyhow::Result<()> { .sol_equivalent_mint .create_token_account_and_mint_to(1_000) .await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 500) @@ -161,8 +163,10 @@ async fn re_one_oracle_stale_success() -> anyhow::Result<()> { async fn re_one_oracle_stale_failure_2() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; - let usdc_bank = test_f.get_bank(&BankMint::USDC); - let sol_bank = test_f.get_bank(&BankMint::SOL); + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + test_f.set_time(0); // Fund SOL lender let lender_mfi_account_f = test_f.create_marginfi_account().await; @@ -178,13 +182,14 @@ async fn re_one_oracle_stale_failure_2() -> anyhow::Result<()> { let borrower_mfi_account_f = test_f.create_marginfi_account().await; let borrower_token_account_f_usdc = test_f.usdc_mint.create_token_account_and_mint_to(500).await; - let borrower_token_account_f_sol = test_f.sol_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; borrower_mfi_account_f .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 500) .await?; // Make SOL oracle stale + test_f.set_time(0); test_f.set_pyth_oracle_timestamp(PYTH_USDC_FEED, 120).await; test_f.set_pyth_oracle_timestamp(PYTH_SOL_FEED, 0).await; test_f.advance_time(120).await; @@ -218,7 +223,7 @@ async fn re_liquidaiton_fail() -> anyhow::Result<()> { let test_f = TestFixture::new(Some(TestSettings { banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, ..Default::default() }, TestBankSetting { @@ -226,7 +231,7 @@ async fn re_liquidaiton_fail() -> anyhow::Result<()> { ..Default::default() }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, config: Some(BankConfig { asset_weight_init: I80F48!(1).into(), asset_weight_maint: I80F48!(1).into(), @@ -238,8 +243,10 @@ async fn re_liquidaiton_fail() -> anyhow::Result<()> { })) .await; - let usdc_bank_f = test_f.get_bank(&BankMint::USDC); - let sol_bank_f = test_f.get_bank(&BankMint::SOL); + test_f.set_time(0); + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); let sole_bank_f = test_f.get_bank(&BankMint::SolEquivalent); let lender_mfi_account_f = test_f.create_marginfi_account().await; @@ -260,7 +267,7 @@ async fn re_liquidaiton_fail() -> anyhow::Result<()> { let borrower_mfi_account_f = test_f.create_marginfi_account().await; let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; - let borrower_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; // Borrower deposits 100 SOL worth $1000 borrower_mfi_account_f @@ -319,11 +326,11 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { group_config: Some(GroupConfig { admin: None }), banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, config: None, }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, config: Some(BankConfig { asset_weight_init: I80F48!(1).into(), ..*DEFAULT_SOL_TEST_BANK_CONFIG @@ -333,6 +340,8 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { })) .await; + test_f.set_time(0); + let lender_mfi_account_f = test_f.create_marginfi_account().await; let lender_token_account_usdc = test_f .usdc_mint @@ -341,7 +350,7 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { lender_mfi_account_f .try_bank_deposit( lender_token_account_usdc.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 100_000, ) .await?; @@ -355,17 +364,17 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { borrower_account .try_bank_deposit( borrower_deposit_account.key, - test_f.get_bank(&BankMint::SOL), + test_f.get_bank(&BankMint::Sol), 1_001, ) .await?; - let borrower_borrow_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; + let borrower_borrow_account = test_f.usdc_mint.create_empty_token_account().await; borrower_account .try_bank_borrow( borrower_borrow_account.key, - test_f.get_bank(&BankMint::USDC), + test_f.get_bank(&BankMint::Usdc), 10_000, ) .await?; @@ -378,10 +387,10 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { { let (insurance_vault, _) = test_f - .get_bank(&BankMint::USDC) + .get_bank(&BankMint::Usdc) .get_vault(BankVaultType::Insurance); test_f - .get_bank_mut(&BankMint::USDC) + .get_bank_mut(&BankMint::Usdc) .mint .mint_to(&insurance_vault, 10_000) .await; @@ -392,7 +401,7 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { let res = test_f .marginfi_group - .try_handle_bankruptcy_with_nonce(test_f.get_bank(&BankMint::USDC), &borrower_account, 1) + .try_handle_bankruptcy_with_nonce(test_f.get_bank(&BankMint::Usdc), &borrower_account, 1) .await; assert!(res.is_err()); @@ -403,7 +412,7 @@ async fn re_bankruptcy_fail() -> anyhow::Result<()> { let res = test_f .marginfi_group - .try_handle_bankruptcy_with_nonce(test_f.get_bank(&BankMint::USDC), &borrower_account, 2) + .try_handle_bankruptcy_with_nonce(test_f.get_bank(&BankMint::Usdc), &borrower_account, 2) .await; assert!(res.is_ok()); diff --git a/programs/marginfi/tests/misc/token_extensions.rs b/programs/marginfi/tests/misc/token_extensions.rs new file mode 100644 index 000000000..a9eb06259 --- /dev/null +++ b/programs/marginfi/tests/misc/token_extensions.rs @@ -0,0 +1,189 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{ + assert_eq_noise, native, + spl::SupportedExtension, + test::{BankMint, TestBankSetting, TestFixture, TestSettings, DEFAULT_SOL_TEST_BANK_CONFIG}, + ui_to_native, +}; +use marginfi::state::marginfi_group::{ + Bank, BankConfig, BankConfigOpt, BankVaultType, GroupConfig, +}; +use solana_program_test::tokio; +use test_case::test_case; + +#[test_case(vec![])] +#[test_case(vec![SupportedExtension::TransferFee])] +#[test_case(vec![SupportedExtension::TransferHook])] +#[test_case(vec![SupportedExtension::PermanentDelegate])] +#[test_case(vec![SupportedExtension::InterestBearing])] +#[test_case(vec![SupportedExtension::MintCloseAuthority])] +#[test_case(vec![SupportedExtension::PermanentDelegate, SupportedExtension::InterestBearing])] +#[test_case(vec![SupportedExtension::MintCloseAuthority, SupportedExtension::InterestBearing])] +#[test_case(vec![SupportedExtension::PermanentDelegate,SupportedExtension::MintCloseAuthority])] +#[test_case(vec![SupportedExtension::InterestBearing, SupportedExtension::MintCloseAuthority])] +#[test_case(vec![SupportedExtension::PermanentDelegate, SupportedExtension::InterestBearing, SupportedExtension::MintCloseAuthority])] +#[tokio::test] +async fn marginfi_account_liquidation_success_with_extension( + extensions: Vec, +) -> anyhow::Result<()> { + let test_f = TestFixture::new_with_t22_extension( + Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::UsdcT22, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::PyUSD, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + asset_weight_maint: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + }), + &extensions, + ) + .await; + + let usdc_t22_bank_f = test_f.get_bank(&BankMint::UsdcT22); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc_t22 = test_f + .usdc_t22_mint + .create_token_account_and_mint_to(2_500) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc_t22.key, usdc_t22_bank_f, 2_000) + .await + .unwrap(); + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_usdc_t22 = test_f.usdc_t22_mint.create_empty_token_account().await; + + // Borrower deposits 100 SOL worth of $1000 + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) + .await?; + + // Borrower borrows $999 + // u32 is fine for this test.. not in production. Needed for Into + let usdc_t22_mint_state = usdc_t22_bank_f.mint.load_state().await; + let transfer_fee_offset: u32 = usdc_t22_mint_state + .get_extension::() + .map(|config| { + config + .calculate_inverse_epoch_fee(0, native!(900, "USDC")) + .unwrap_or(0) as u32 + }) + .unwrap_or(0); + + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc_t22.key, usdc_t22_bank_f, 900) + .await + .unwrap(); + assert_eq!( + borrower_token_account_usdc_t22.balance().await, + native!(900, "USDC") + ); + + // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_t22_bank_f) + .await + .unwrap(); + + // Checks + let sol_bank: Bank = sol_bank_f.load().await; + let usdc_t22_bank: Bank = usdc_t22_bank_f.load().await; + + let depositor_ma = lender_mfi_account_f.load().await; + let borrower_ma = borrower_mfi_account_f.load().await; + + // Depositors should have 1 SOL + assert_eq!( + sol_bank + .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1, "SOL")) + ); + + // Depositors should have 1990.25 USDC + assert_eq_noise!( + usdc_t22_bank + .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1990.25, "USDC", f64)), + native!(0.00001, "USDC", f64) + ); + + // Borrower should have 99 SOL + assert_eq!( + sol_bank + .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(99, "SOL")) + ); + + // Borrower should have 890.50 USDC + assert_eq_noise!( + usdc_t22_bank + .get_liability_amount( + borrower_ma.lending_account.balances[1] + .liability_shares + .into() + ) + .unwrap(), + I80F48::from(native!( + 890.50 + transfer_fee_offset as f64 / 1e6, + "USDC", + f64 + )), + native!(0.00001, "USDC", f64) + ); + + // Check insurance fund fee + let insurance_fund_usdc = usdc_t22_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await; + + let fee = usdc_t22_mint_state + .get_extension::() + .map(|config| { + config + .calculate_epoch_fee(0, ui_to_native!(0.25, 6)) + .unwrap_or(0) as u32 + }) + .unwrap_or(0); + assert_eq_noise!( + insurance_fund_usdc.balance().await as i64, + native!(0.25 - fee as f64 / 1e6, "USDC", f64) as i64, + 1 + ); + + Ok(()) +} diff --git a/programs/marginfi/tests/tests.rs b/programs/marginfi/tests/tests.rs new file mode 100644 index 000000000..3f13d8b0e --- /dev/null +++ b/programs/marginfi/tests/tests.rs @@ -0,0 +1,3 @@ +mod admin_actions; +mod misc; +mod user_actions; diff --git a/programs/marginfi/tests/user_actions/borrow.rs b/programs/marginfi/tests/user_actions/borrow.rs new file mode 100644 index 000000000..cdefaebf8 --- /dev/null +++ b/programs/marginfi/tests/user_actions/borrow.rs @@ -0,0 +1,413 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixtures::{assert_custom_error, native, prelude::*, ui_to_native}; +use marginfi::{ + assert_eq_with_tolerance, + prelude::*, + state::marginfi_group::{BankConfigOpt, BankVaultType}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use test_case::test_case; + +#[test_case(100., 9., BankMint::Usdc, BankMint::Sol)] +#[test_case(123456.0, 12345.599999999, BankMint::Usdc, BankMint::Sol)] +#[test_case(123456.0, 10000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(1.0, 5.0, BankMint::Sol, BankMint::Usdc)] +#[test_case(128932.0, 9834.0, BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(240., 0.092, BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(36., 1.7, BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_borrow_success( + deposit_amount: f64, + borrow_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank(&debt_mint) + .mint + .create_empty_token_account() + .await; + let collateral_bank = test_f.get_bank(&collateral_mint); + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + collateral_bank, + deposit_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank_f = test_f.get_bank(&debt_mint); + + let pre_vault_balance = debt_bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let pre_user_debt_accounted = I80F48::ZERO; + + let res = user_mfi_account_f + .try_bank_borrow(user_debt_token_account_f.key, debt_bank_f, borrow_amount) + .await; + assert!(res.is_ok()); + + let post_vault_balance = debt_bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let marginfi_account = user_mfi_account_f.load().await; + let balance = marginfi_account + .lending_account + .get_balance(&debt_bank_f.key) + .unwrap(); + let post_user_debt_accounted = debt_bank_f + .load() + .await + .get_asset_amount(balance.liability_shares.into()) + .unwrap(); + + let borrow_amount_native = ui_to_native!(borrow_amount, debt_bank_f.mint.mint.decimals); + let borrow_fee = debt_bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_inverse_epoch_fee(0, borrow_amount_native) + .unwrap_or(0) + }) + .unwrap_or(0); + let borrow_amount_pre_fee = borrow_amount_native + borrow_fee; + + let active_balance_count = marginfi_account + .lending_account + .get_active_balances_iter() + .count(); + assert_eq!(2, active_balance_count); + + let expected_liquidity_vault_delta = -(borrow_amount_pre_fee as i64); + let actual_liquidity_vault_delta = post_vault_balance as i64 - pre_vault_balance as i64; + let accounted_user_balance_delta = post_user_debt_accounted - pre_user_debt_accounted; + + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + assert_eq_with_tolerance!( + I80F48::from(expected_liquidity_vault_delta), + -accounted_user_balance_delta, + 1 + ); + + Ok(()) +} + +#[test_case(100., 9., 10.000000001, BankMint::Usdc, BankMint::Sol)] +#[test_case(123_456., 12_345.6, 12_345.9, BankMint::Usdc, BankMint::Sol)] +#[test_case(123_456., 10_000., 15_000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(1., 5., 11.98224, BankMint::Sol, BankMint::Usdc)] +#[test_case(128_932., 10_000., 15_000.0, BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(240., 0.092, 500., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(36., 1.7, 1.9, BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_borrow_failure_not_enough_collateral( + deposit_amount: f64, + borrow_amount_ok: f64, + borrow_amount_failed: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount_failed; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_token_account_f_sol = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_token_account_f_sol.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let borrower_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + let borrower_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let collateral_bank = test_f.get_bank(&collateral_mint); + borrower_mfi_account_f + .try_bank_deposit( + borrower_collateral_token_account_f.key, + collateral_bank, + deposit_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank_f = test_f.get_bank(&debt_mint); + + let res = borrower_mfi_account_f + .try_bank_borrow( + borrower_debt_token_account_f.key, + debt_bank_f, + borrow_amount_failed, + ) + .await; + assert_custom_error!(res.unwrap_err(), MarginfiError::RiskEngineInitRejected); + + let res = borrower_mfi_account_f + .try_bank_borrow( + borrower_debt_token_account_f.key, + debt_bank_f, + borrow_amount_ok, + ) + .await; + assert!(res.is_ok()); + + Ok(()) +} + +#[test_case(505., 500., 505.0000000001, BankMint::Usdc, BankMint::Sol)] +#[test_case(12_345.6, 12_345.5, 12_345.9, BankMint::Usdc, BankMint::Sol)] +#[test_case(11_000., 10_000., 15_000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(0.91, 0.1, 0.98, BankMint::Sol, BankMint::Usdc)] +#[test_case(11_000., 10_000., 15_000., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(505., 0.092, 500., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(1.8, 1.7, 1.9, BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_borrow_failure_borrow_limit( + borrow_cap: f64, + borrow_amount_ok: f64, + borrow_amount_failed: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount_failed; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank_mut(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await + .unwrap(); + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount_failed, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_mint_decimals = test_f.get_bank(&debt_mint).mint.mint.decimals; + test_f + .get_bank_mut(&debt_mint) + .update_config(BankConfigOpt { + borrow_limit: Some(native!(borrow_cap, debt_mint_decimals, f64)), + ..Default::default() + }) + .await?; + + let debt_bank_f = test_f.get_bank(&debt_mint); + + let res = user_mfi_account_f + .try_bank_borrow( + user_debt_token_account_f.key, + debt_bank_f, + borrow_amount_failed, + ) + .await; + assert!(res.is_err()); + assert_custom_error!( + res.unwrap_err(), + MarginfiError::BankLiabilityCapacityExceeded + ); + + let res = user_mfi_account_f + .try_bank_borrow(user_debt_token_account_f.key, debt_bank_f, borrow_amount_ok) + .await; + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn isolated_borrows() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_eq_iso_bank = test_f.get_bank(&BankMint::SolEqIsolated); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_eq_iso_bank, 1_000) + .await?; + + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f + .sol_equivalent_mint + .create_empty_token_account() + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + // Borrow SOL EQ + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_eq_iso_bank, 10) + .await; + + assert!(res.is_ok()); + + // Repay isolated SOL EQ borrow and borrow SOL successfully, + let borrower_sol_account = test_f.sol_mint.create_empty_token_account().await; + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_sol_account.key, sol_bank, 10) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IsolatedAccountIllegalState); + + borrower_mfi_account_f + .try_bank_repay( + borrower_token_account_f_sol.key, + sol_eq_iso_bank, + 0, + Some(true), + ) + .await?; + + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_sol_account.key, sol_bank, 10) + .await; + + assert!(res.is_ok()); + + // Borrowing SOL EQ again fails + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_eq_iso_bank, 10) + .await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IsolatedAccountIllegalState); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/close_account.rs b/programs/marginfi/tests/user_actions/close_account.rs new file mode 100644 index 000000000..13795680a --- /dev/null +++ b/programs/marginfi/tests/user_actions/close_account.rs @@ -0,0 +1,64 @@ +use fixtures::{ + assert_custom_error, + spl::TokenAccountFixture, + test::{BankMint, TestFixture, TestSettings}, +}; +use marginfi::errors::MarginfiError; +use solana_program_test::tokio; + +#[tokio::test] +async fn close_marginfi_account() -> anyhow::Result<()> { + let mut test_f: TestFixture = + TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let marginfi_account_f = test_f.create_marginfi_account().await; + + let owner = test_f.payer(); + let token_account_f = + TokenAccountFixture::new(test_f.context.clone(), &test_f.usdc_mint, &owner).await; + test_f.usdc_mint.mint_to(&token_account_f.key, 1_000).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + + marginfi_account_f + .try_bank_deposit(token_account_f.key, usdc_bank_f, 1_000) + .await?; + + let res = marginfi_account_f.try_close_account(0).await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalAction); + + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + let sol_account = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let depositor = test_f.create_marginfi_account().await; + depositor + .try_bank_deposit(sol_account.key, sol_bank_f, 100) + .await?; + + let sol_account_2 = test_f.sol_mint.create_token_account_and_mint_to(0).await; + + marginfi_account_f + .try_bank_borrow(sol_account_2.key, sol_bank_f, 10) + .await?; + + let res = marginfi_account_f.try_close_account(0).await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalAction); + + // Repay the loan + marginfi_account_f + .try_bank_repay(sol_account_2.key, sol_bank_f, 10, Some(true)) + .await?; + + marginfi_account_f + .try_bank_withdraw(token_account_f.key, usdc_bank_f, 1_000, Some(true)) + .await?; + + let res = marginfi_account_f.try_close_account(1).await; + + assert!(res.is_ok()); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/close_balance.rs b/programs/marginfi/tests/user_actions/close_balance.rs new file mode 100644 index 000000000..23d0a05a7 --- /dev/null +++ b/programs/marginfi/tests/user_actions/close_balance.rs @@ -0,0 +1,113 @@ +use fixtures::{ + assert_custom_error, + test::{BankMint, TestFixture, TestSettings}, +}; +use marginfi::errors::MarginfiError; +use solana_program_test::tokio; +use switchboard_solana::Clock; + +#[tokio::test] +async fn lending_account_close_balance() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_eq_bank = test_f.get_bank(&BankMint::SolEquivalent); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_eq_bank, 1_000) + .await?; + + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank, 1_000) + .await?; + + let res = lender_mfi_account_f.try_balance_close(sol_bank).await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalBalanceState); + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_f_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + let borrower_token_account_f_sol_eq = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(1_000) + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_f_usdc.key, usdc_bank, 1_000) + .await?; + + // Borrow SOL EQ + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol_eq.key, sol_eq_bank, 0.01) + .await; + + assert!(res.is_ok()); + + // Borrow SOL + let res = borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_f_sol.key, sol_bank, 0.01) + .await; + + assert!(res.is_ok()); + + // This issue is not that bad, because the user can still borrow other assets (isolated liab < empty threshold) + let res = borrower_mfi_account_f.try_balance_close(sol_bank).await; + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalBalanceState); + + // Let a second go b + { + let mut ctx = test_f.context.borrow_mut(); + let mut clock: Clock = ctx.banks_client.get_sysvar().await?; + // Advance clock by 1 second + clock.unix_timestamp += 1; + ctx.set_sysvar(&clock); + } + + // Repay isolated SOL EQ borrow successfully + borrower_mfi_account_f + .try_bank_repay( + borrower_token_account_f_sol_eq.key, + sol_eq_bank, + 0.01, + Some(false), + ) + .await?; + + // Liability share in balance is smaller than 0.0001, so repay all should fail + let res = borrower_mfi_account_f + .try_bank_repay( + borrower_token_account_f_sol_eq.key, + sol_eq_bank, + 1, + Some(true), + ) + .await; + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::NoLiabilityFound); + + // This issue is not that bad, because the user can still borrow other assets (isolated liab < empty threshold) + let res = borrower_mfi_account_f.try_balance_close(sol_eq_bank).await; + assert!(res.is_ok()); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/create_account.rs b/programs/marginfi/tests/user_actions/create_account.rs new file mode 100644 index 000000000..a5bee889f --- /dev/null +++ b/programs/marginfi/tests/user_actions/create_account.rs @@ -0,0 +1,57 @@ +use anchor_lang::{InstructionData, ToAccountMetas}; +use fixtures::test::TestFixture; +use marginfi::state::marginfi_account::MarginfiAccount; +use solana_program_test::tokio; +use solana_sdk::{ + instruction::Instruction, signature::Keypair, signer::Signer, system_program, + transaction::Transaction, +}; + +#[tokio::test] +async fn marginfi_account_create_success() -> anyhow::Result<()> { + let test_f = TestFixture::new(None).await; + + let marginfi_account_key = Keypair::new(); + let accounts = marginfi::accounts::MarginfiAccountInitialize { + marginfi_group: test_f.marginfi_group.key, + marginfi_account: marginfi_account_key.pubkey(), + authority: test_f.payer(), + fee_payer: test_f.payer(), + system_program: system_program::id(), + }; + let init_marginfi_account_ix = Instruction { + program_id: marginfi::id(), + accounts: accounts.to_account_metas(Some(true)), + data: marginfi::instruction::MarginfiAccountInitialize {}.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[init_marginfi_account_ix], + Some(&test_f.payer()), + &[&test_f.payer_keypair(), &marginfi_account_key], + test_f.get_latest_blockhash().await, + ); + + let res = test_f + .context + .borrow_mut() + .banks_client + .process_transaction(tx) + .await; + + assert!(res.is_ok()); + + let marginfi_account: MarginfiAccount = test_f + .load_and_deserialize(&marginfi_account_key.pubkey()) + .await; + + assert_eq!(marginfi_account.group, test_f.marginfi_group.key); + assert_eq!(marginfi_account.authority, test_f.payer()); + assert!(marginfi_account + .lending_account + .balances + .iter() + .all(|bank| !bank.active)); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/deposit.rs b/programs/marginfi/tests/user_actions/deposit.rs new file mode 100644 index 000000000..ac98cb3d7 --- /dev/null +++ b/programs/marginfi/tests/user_actions/deposit.rs @@ -0,0 +1,218 @@ +use anchor_lang::{InstructionData, ToAccountMetas}; +use anchor_spl::token::spl_token; +use fixed::types::I80F48; +use fixtures::prelude::*; +use fixtures::{assert_custom_error, native}; +use marginfi::state::marginfi_group::{BankConfigOpt, BankVaultType}; +use marginfi::{assert_eq_with_tolerance, prelude::*}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use solana_sdk::transaction::Transaction; +use solana_sdk::{instruction::Instruction, signer::Signer}; +use test_case::test_case; + +#[test_case(0.0, BankMint::Usdc)] +#[test_case(0.05, BankMint::UsdcSwb)] +#[test_case(1_000.0, BankMint::Usdc)] +#[test_case(0.05, BankMint::Sol)] +#[test_case(15_002.0, BankMint::SolSwb)] +#[test_case(0.05, BankMint::PyUSD)] +#[test_case(15_002.0, BankMint::PyUSD)] +#[test_case(0.0, BankMint::T22WithFee)] +#[test_case(0.05, BankMint::T22WithFee)] +#[test_case(15_002.0, BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_account_deposit_success( + deposit_amount: f64, + bank_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let token_account_f = TokenAccountFixture::new( + test_f.context.clone(), + &test_f.get_bank(&bank_mint).mint, + &test_f.payer(), + ) + .await; + let bank_f = test_f.get_bank_mut(&bank_mint); + bank_f + .mint + .mint_to(&token_account_f.key, user_wallet_balance) + .await; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let pre_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + + let res = user_mfi_account_f + .try_bank_deposit(token_account_f.key, &bank_f, deposit_amount) + .await; + assert!(res.is_ok()); + + let post_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + + let marginfi_account = user_mfi_account_f.load().await; + let active_balance_count = marginfi_account + .lending_account + .get_active_balances_iter() + .count(); + assert_eq!(1, active_balance_count); + + let maybe_balance = marginfi_account.lending_account.get_balance(&bank_f.key); + assert!(maybe_balance.is_some()); + + let balance = maybe_balance.unwrap(); + + let expected_liquidity_vault_delta = + I80F48::from(native!(deposit_amount, bank_f.mint.mint.decimals, f64)); + let actual_liquidity_vault_delta = I80F48::from(post_vault_balance - pre_vault_balance); + let accounted_user_balance_delta = bank_f + .load() + .await + .get_asset_amount(balance.asset_shares.into()) + .unwrap(); + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + assert_eq_with_tolerance!( + expected_liquidity_vault_delta, + accounted_user_balance_delta, + 1 + ); + + Ok(()) +} + +#[test_case(1_000., 456., 2345., BankMint::Usdc)] +#[test_case(1_000., 456., 2345., BankMint::UsdcSwb)] +#[test_case(1_000., 456., 2345., BankMint::Sol)] +#[test_case(1_000., 456., 2345., BankMint::SolSwb)] +#[test_case(1_000., 456., 2345., BankMint::PyUSD)] +#[test_case(1_000., 456., 2345., BankMint::T22WithFee)] +#[test_case(1_000., 999.999999, 1000., BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_account_deposit_failure_capacity_exceeded( + deposit_cap: f64, + deposit_amount_ok: f64, + deposit_amount_failed: f64, + bank_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount_failed); + let bank_f = test_f.get_bank(&bank_mint); + let user_token_account = bank_f + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + bank_f + .update_config(BankConfigOpt { + deposit_limit: Some(native!(deposit_cap, bank_f.mint.mint.decimals, f64)), + ..Default::default() + }) + .await?; + + let res = user_mfi_account_f + .try_bank_deposit(user_token_account.key, bank_f, deposit_amount_failed) + .await; + assert_custom_error!(res.unwrap_err(), MarginfiError::BankAssetCapacityExceeded); + + let res = user_mfi_account_f + .try_bank_deposit(user_token_account.key, bank_f, deposit_amount_ok) + .await; + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_deposit_failure_wrong_token_program() -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let deposit_amount = 1_000.; + let bank_mint = BankMint::T22WithFee; + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let bank_f = test_f.get_bank(&bank_mint); + let user_token_account = bank_f + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let marginfi_account = user_mfi_account_f.load().await; + + let accounts = marginfi::accounts::LendingAccountDeposit { + marginfi_group: marginfi_account.group, + marginfi_account: user_mfi_account_f.key, + signer: test_f.context.borrow().payer.pubkey(), + bank: bank_f.key, + signer_token_account: user_token_account.key, + bank_liquidity_vault: bank_f.get_vault(BankVaultType::Liquidity).0, + token_program: spl_token::ID, + } + .to_account_metas(Some(true)); + + let deposit_ix = Instruction { + program_id: marginfi::id(), + accounts, + data: marginfi::instruction::LendingAccountDeposit { + amount: native!(deposit_amount, bank_f.mint.mint.decimals, f64), + } + .data(), + }; + + let tx = { + let ctx = test_f.context.borrow(); + Transaction::new_signed_with_payer( + &[deposit_ix], + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ) + }; + + let mut ctx = test_f.context.borrow_mut(); + let res = ctx.banks_client.process_transaction(tx).await; + assert!(res.is_err()); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/flash_loan.rs b/programs/marginfi/tests/user_actions/flash_loan.rs new file mode 100644 index 000000000..bf7b0f296 --- /dev/null +++ b/programs/marginfi/tests/user_actions/flash_loan.rs @@ -0,0 +1,537 @@ +use anchor_lang::{InstructionData, ToAccountMetas}; +use fixtures::{assert_custom_error, prelude::*}; +use marginfi::{prelude::*, state::marginfi_account::FLASHLOAN_ENABLED_FLAG}; +use pretty_assertions::assert_eq; +use solana_program::{instruction::Instruction, pubkey::Pubkey}; +use solana_program_test::*; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, signer::Signer, transaction::Transaction, +}; + +// Flashloan tests +// 1. Flashloan success (1 action) +// 2. Flashloan success (3 actions) +// 3. Flashloan fails because of bad account health +// 4. Flashloan fails because of non whitelisted account +// 5. Flashloan fails because of missing `end_flashloan` ix +// 6. Flashloan fails because of invalid instructions sysvar +// 7. Flashloan fails because of invalid `end_flashloan` ix order +// 8. Flashloan fails because `end_flashloan` ix is for another account +// 9. Flashloan fails because account is already in a flashloan + +#[tokio::test] +async fn flashloan_success_1op() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let repay_ix = borrower_mfi_account_f + .make_bank_repay_ix( + borrower_token_account_f_sol.key, + sol_bank, + 1_000, + Some(true), + ) + .await; + + let flash_loan_result = borrower_mfi_account_f + .try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![]) + .await; + + assert!(flash_loan_result.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_success_3op() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + + // Create borrow and repay instructions + let mut ixs = Vec::new(); + for _ in 0..3 { + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + ixs.push(borrow_ix); + + let repay_ix = borrower_mfi_account_f + .make_bank_repay_ix( + borrower_token_account_f_sol.key, + sol_bank, + 1_000, + Some(true), + ) + .await; + ixs.push(repay_ix); + } + + ixs.push(ComputeBudgetInstruction::set_compute_unit_limit(1_400_000)); + + let flash_loan_result = borrower_mfi_account_f + .try_flashloan(ixs, vec![], vec![]) + .await; + + assert!(flash_loan_result.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_account_health() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let flash_loan_result = borrower_mfi_account_f + .try_flashloan(vec![borrow_ix], vec![], vec![sol_bank.key]) + .await; + + assert_custom_error!( + flash_loan_result.unwrap_err(), + MarginfiError::RiskEngineInitRejected + ); + + Ok(()) +} + +#[tokio::test] +// Note: The flashloan flag is now deprecated +async fn flashloan_ok_missing_flag() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let repay_ix = borrower_mfi_account_f + .make_bank_repay_ix( + borrower_token_account_f_sol.key, + sol_bank, + 1_000, + Some(true), + ) + .await; + + let flash_loan_result = borrower_mfi_account_f + .try_flashloan(vec![borrow_ix, repay_ix], vec![], vec![]) + .await; + + assert!(flash_loan_result.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_missing_fe_ix() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let repay_ix = borrower_mfi_account_f + .make_bank_repay_ix( + borrower_token_account_f_sol.key, + sol_bank, + 1_000, + Some(true), + ) + .await; + + let mut ixs = vec![borrow_ix, repay_ix]; + + let start_ix = borrower_mfi_account_f + .make_lending_account_start_flashloan_ix(ixs.len() as u64) + .await; + + ixs.insert(0, start_ix); + + let mut ctx = test_f.context.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + let res = ctx.banks_client.process_transaction(tx).await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_missing_invalid_sysvar_ixs() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let repay_ix = borrower_mfi_account_f + .make_bank_repay_ix( + borrower_token_account_f_sol.key, + sol_bank, + 1_000, + Some(true), + ) + .await; + + let mut ixs = vec![borrow_ix, repay_ix]; + + let start_ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::LendingAccountStartFlashloan { + marginfi_account: borrower_mfi_account_f.key, + signer: test_f.context.borrow().payer.pubkey(), + ixs_sysvar: Pubkey::default(), + } + .to_account_metas(Some(true)), + data: marginfi::instruction::LendingAccountStartFlashloan { + end_index: ixs.len() as u64 + 1, + } + .data(), + }; + + let end_ix = borrower_mfi_account_f + .make_lending_account_end_flashloan_ix(vec![], vec![]) + .await; + + ixs.insert(0, start_ix); + ixs.push(end_ix); + + let mut ctx = test_f.context.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + let res = ctx.banks_client.process_transaction(tx).await; + + assert!(res.is_err()); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_invalid_end_fl_order() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let mut ixs = vec![borrow_ix]; + + let start_ix = borrower_mfi_account_f + .make_lending_account_start_flashloan_ix(0) + .await; + + let end_ix = borrower_mfi_account_f + .make_lending_account_end_flashloan_ix(vec![], vec![]) + .await; + + ixs.insert(0, start_ix); + ixs.insert(0, end_ix); + + let mut ctx = test_f.context.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + let res = ctx.banks_client.process_transaction(tx).await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_invalid_end_fl_different_m_account() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let mut ixs = vec![borrow_ix]; + + let start_ix = borrower_mfi_account_f + .make_lending_account_start_flashloan_ix(ixs.len() as u64 + 1) + .await; + + let end_ix = lender_mfi_account_f + .make_lending_account_end_flashloan_ix(vec![], vec![]) + .await; + + ixs.insert(0, start_ix); + ixs.push(end_ix); + + let mut ctx = test_f.context.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + let res = ctx.banks_client.process_transaction(tx).await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); + + Ok(()) +} + +#[tokio::test] +async fn flashloan_fail_already_in_flashloan() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Fund SOL lender + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_f_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_f_sol.key, sol_bank, 1_000) + .await?; + + // Fund SOL borrower + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + + borrower_mfi_account_f + .try_set_flag(FLASHLOAN_ENABLED_FLAG) + .await?; + + let borrower_token_account_f_sol = test_f.sol_mint.create_empty_token_account().await; + // Borrow SOL + + let borrow_ix = borrower_mfi_account_f + .make_bank_borrow_ix(borrower_token_account_f_sol.key, sol_bank, 1_000) + .await; + + let mut ixs = vec![borrow_ix]; + + let start_ix = borrower_mfi_account_f + .make_lending_account_start_flashloan_ix(ixs.len() as u64 + 2) + .await; + + let end_ix = borrower_mfi_account_f + .make_lending_account_end_flashloan_ix(vec![], vec![]) + .await; + + ixs.insert(0, start_ix.clone()); + ixs.insert(0, start_ix.clone()); + ixs.push(end_ix); + + let mut ctx = test_f.context.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + let res = ctx.banks_client.process_transaction(tx).await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlashloan); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/liquidate.rs b/programs/marginfi/tests/user_actions/liquidate.rs new file mode 100644 index 000000000..613f8a7a8 --- /dev/null +++ b/programs/marginfi/tests/user_actions/liquidate.rs @@ -0,0 +1,735 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{assert_custom_error, assert_eq_noise, native, prelude::*}; +use marginfi::{ + prelude::*, + state::marginfi_group::{Bank, BankConfig, BankConfigOpt, BankVaultType}, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use test_case::test_case; + +#[test_case(100., 9.9, 1., BankMint::Usdc, BankMint::Sol)] +#[test_case(123., 122., 10., BankMint::SolEquivalent, BankMint::SolEqIsolated)] +#[test_case(1_000., 999., 10., BankMint::Usdc, BankMint::T22WithFee)] +#[test_case(2_000., 99., 1_000., BankMint::T22WithFee, BankMint::SolEquivalent)] +#[test_case(2_000., 1_999., 2_000., BankMint::Usdc, BankMint::PyUSD)] +#[tokio::test] +async fn marginfi_account_liquidation_success( + deposit_amount: f64, + borrow_amount: f64, + liquidate_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + { + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + } + + // Liquidatee + + let (liquidatee_mfi_account_f, borrow_amount_actual) = { + let liquidatee_mfi_account_f = test_f.create_marginfi_account().await; + let liquidatee_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let liquidatee_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(liquidatee_wallet_balance) + .await; + let liquidatee_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + liquidatee_mfi_account_f + .try_bank_deposit( + liquidatee_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + deposit_amount, + ) + .await?; + liquidatee_mfi_account_f + .try_bank_borrow( + liquidatee_debt_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount, + ) + .await?; + + let liquidatee_mfi_ma = liquidatee_mfi_account_f.load().await; + let debt_bank = test_f.get_bank(&debt_mint).load().await; + let borrow_amount_actual_native = debt_bank.get_liability_amount( + liquidatee_mfi_ma.lending_account.balances[1] + .liability_shares + .into(), + )?; + let borrow_amount_actual = borrow_amount_actual_native.to_num::() + / 10_f64.powf(debt_bank.mint_decimals as f64); + + (liquidatee_mfi_account_f, borrow_amount_actual) + }; + + // Liquidator + + let liquidator_mfi_account_f = { + let liquidator_mfi_account_f = test_f.create_marginfi_account().await; + let liquidator_wallet_balance = get_max_deposit_amount_pre_fee(borrow_amount_actual); + let liquidator_collateral_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_token_account_and_mint_to(liquidator_wallet_balance) + .await; + liquidator_mfi_account_f + .try_bank_deposit( + liquidator_collateral_token_account_f.key, + test_f.get_bank(&debt_mint), + borrow_amount_actual, + ) + .await?; + + liquidator_mfi_account_f + }; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + // Synthetically bring down the borrower account health by reducing the asset weights of the collateral bank + test_f + .get_bank_mut(&collateral_mint) + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + let collateral_bank_f = test_f.get_bank(&collateral_mint); + let debt_bank_f = test_f.get_bank(&debt_mint); + + liquidator_mfi_account_f + .try_liquidate( + &liquidatee_mfi_account_f, + collateral_bank_f, + liquidate_amount, + debt_bank_f, + ) + .await?; + + let collateral_bank = collateral_bank_f.load().await; + let debt_bank = debt_bank_f.load().await; + + let liquidator_mfi_ma = liquidator_mfi_account_f.load().await; + let liquidatee_mfi_ma = liquidatee_mfi_account_f.load().await; + + // Check liquidator collateral balances + + let collateral_mint_liquidator_balance = collateral_bank.get_asset_amount( + liquidator_mfi_ma.lending_account.balances[1] + .asset_shares + .into(), + )?; + let expected_collateral_mint_liquidator_balance = + native!(liquidate_amount, collateral_bank_f.mint.mint.decimals, f64); + assert_eq!( + expected_collateral_mint_liquidator_balance, + collateral_mint_liquidator_balance + ); + + let debt_paid_out = liquidate_amount * 0.975 * collateral_bank_f.get_price().await + / debt_bank_f.get_price().await; + + let expected_debt_mint_liquidator_balance = I80F48::from(native!( + borrow_amount_actual - debt_paid_out, + debt_bank_f.mint.mint.decimals, + f64 + )); + let debt_mint_liquidator_balance = debt_bank.get_asset_amount( + liquidator_mfi_ma.lending_account.balances[0] + .asset_shares + .into(), + )?; + assert_eq_noise!( + expected_debt_mint_liquidator_balance, + debt_mint_liquidator_balance, + 1. + ); + + // Check liquidatee collateral and debt balances + + let debt_covered = liquidate_amount * 0.95 * collateral_bank_f.get_price().await + / debt_bank_f.get_price().await; + let expected_debt_mint_liquidatee_balance = I80F48::from(native!( + borrow_amount_actual - debt_covered, + debt_bank_f.mint.mint.decimals, + f64 + )); + let debt_mint_liquidatee_balance = debt_bank.get_liability_amount( + liquidatee_mfi_ma.lending_account.balances[1] + .liability_shares + .into(), + )?; + assert_eq_noise!( + expected_debt_mint_liquidatee_balance, + debt_mint_liquidatee_balance, + 1. + ); + + let expected_collateral_mint_liquidatee_balance = I80F48::from(native!( + deposit_amount - liquidate_amount, + collateral_bank_f.mint.mint.decimals, + f64 + )); + let collateral_mint_liquidatee_balance = collateral_bank + .get_liability_amount( + liquidatee_mfi_ma.lending_account.balances[0] + .asset_shares + .into(), + ) + .unwrap(); + assert_eq_noise!( + collateral_mint_liquidatee_balance, + expected_collateral_mint_liquidatee_balance, + 1. + ); + + let insurance_fund_fee = liquidate_amount * 0.025 * collateral_bank_f.get_price().await + / debt_bank_f.get_price().await; + let expected_insurance_fund_usdc_pre_fee = + native!(insurance_fund_fee, debt_bank_f.mint.mint.decimals, f64); + let if_transfer_fee = debt_bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_epoch_fee(0, expected_insurance_fund_usdc_pre_fee) + .unwrap_or(0) + }) + .unwrap_or(0); + let expected_insurance_fund_usdc = + (expected_insurance_fund_usdc_pre_fee - if_transfer_fee) as i64; + + let insurance_fund_usdc = debt_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await + .balance() + .await as i64; + assert_eq_noise!(expected_insurance_fund_usdc, insurance_fund_usdc, 1); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_success_many_balances() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::many_banks_10())).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); + let sol_eq1_bank_f = test_f.get_bank(&BankMint::SolEquivalent1); + let sol_eq2_bank_f = test_f.get_bank(&BankMint::SolEquivalent2); + let sol_eq3_bank_f = test_f.get_bank(&BankMint::SolEquivalent3); + let sol_eq4_bank_f = test_f.get_bank(&BankMint::SolEquivalent4); + let sol_eq5_bank_f = test_f.get_bank(&BankMint::SolEquivalent5); + let sol_eq6_bank_f = test_f.get_bank(&BankMint::SolEquivalent6); + let sol_eq7_bank_f = test_f.get_bank(&BankMint::SolEquivalent7); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(2_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + let borrower_token_account_sol_eq = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(5000) + .await; + + // Borrower deposits 100 SOL worth of $1000 + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) + .await?; + + // Borrower borrows $999 + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) + .await?; + + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq1_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq2_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq3_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq4_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq5_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq6_bank_f, 0) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq7_bank_f, 0) + .await?; + + // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await?; + + // Checks + let sol_bank: Bank = sol_bank_f.load().await; + let usdc_bank: Bank = usdc_bank_f.load().await; + + let depositor_ma = lender_mfi_account_f.load().await; + let borrower_ma = borrower_mfi_account_f.load().await; + + // Depositors should have 1 SOL + assert_eq!( + sol_bank + .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1, "SOL")) + ); + + // Depositors should have 1990.25 USDC + assert_eq_noise!( + usdc_bank + .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1990.25, "USDC", f64)), + native!(0.00001, "USDC", f64) + ); + + // Borrower should have 99 SOL + assert_eq!( + sol_bank + .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(99, "SOL")) + ); + + // Borrower should have 989.50 USDC + assert_eq_noise!( + usdc_bank + .get_liability_amount( + borrower_ma.lending_account.balances[1] + .liability_shares + .into() + ) + .unwrap(), + I80F48::from(native!(989.50, "USDC", f64)), + native!(0.00001, "USDC", f64) + ); + + // Check insurance fund fee + let insurance_fund_usdc = usdc_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await; + + assert_eq_noise!( + insurance_fund_usdc.balance().await as i64, + native!(0.25, "USDC", f64) as i64, + 1 + ); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_success_swb() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + asset_weight_maint: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_SW_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(2_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + + // Borrower deposits 100 SOL worth of $1000 + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) + .await?; + + // Borrower borrows $999 + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 999) + .await?; + + // Synthetically bring down the borrower account health by reducing the asset weights of the SOL bank + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await?; + + // Checks + let sol_bank: Bank = sol_bank_f.load().await; + let usdc_bank: Bank = usdc_bank_f.load().await; + + let depositor_ma = lender_mfi_account_f.load().await; + let borrower_ma = borrower_mfi_account_f.load().await; + + // Depositors should have 1 SOL + assert_eq!( + sol_bank + .get_asset_amount(depositor_ma.lending_account.balances[1].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1, "SOL")) + ); + + // Depositors should have 1990.25 USDC + assert_eq_noise!( + usdc_bank + .get_asset_amount(depositor_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(1990.25, "USDC", f64)), + native!(0.01, "USDC", f64) + ); + + // Borrower should have 99 SOL + assert_eq!( + sol_bank + .get_asset_amount(borrower_ma.lending_account.balances[0].asset_shares.into()) + .unwrap(), + I80F48::from(native!(99, "SOL")) + ); + + // Borrower should have 989.50 USDC + assert_eq_noise!( + usdc_bank + .get_liability_amount( + borrower_ma.lending_account.balances[1] + .liability_shares + .into() + ) + .unwrap(), + I80F48::from(native!(989.50, "USDC", f64)), + native!(0.01, "USDC", f64) + ); + + // Check insurance fund fee + let insurance_fund_usdc = usdc_bank_f + .get_vault_token_account(BankVaultType::Insurance) + .await; + + assert_eq_noise!( + insurance_fund_usdc.balance().await as i64, + native!(0.25, "USDC", f64) as i64, + native!(0.001, "USDC", f64) as i64 + ); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_failure_liquidatee_not_unhealthy() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(BankConfig { + asset_weight_maint: I80F48!(1).into(), + ..*DEFAULT_USDC_TEST_BANK_CONFIG + }), + }, + TestBankSetting { + mint: BankMint::Sol, + config: Some(BankConfig { + asset_weight_init: I80F48!(1).into(), + asset_weight_maint: I80F48!(1).into(), + ..*DEFAULT_SOL_TEST_BANK_CONFIG + }), + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 100) + .await?; + + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 100) + .await?; + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await; + + assert!(res.is_err()); + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_failure_liquidation_too_severe() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(10).await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) + .await?; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 61) + .await?; + + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.5).into()), + ..Default::default() + }) + .await?; + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 10, usdc_bank_f) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await; + + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_failure_liquidator_no_collateral() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings { + banks: vec![ + TestBankSetting { + mint: BankMint::Usdc, + config: Some(BankConfig { + liability_weight_init: I80F48!(1.2).into(), + liability_weight_maint: I80F48!(1.1).into(), + ..*DEFAULT_USDC_TEST_BANK_CONFIG + }), + }, + TestBankSetting { + mint: BankMint::Sol, + config: None, + }, + TestBankSetting { + mint: BankMint::SolEquivalent, + config: None, + }, + ], + group_config: Some(GroupConfig { admin: None }), + })) + .await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_sol_eq = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(100) + .await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 1) + .await?; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 60) + .await?; + + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.3).into()), + ..Default::default() + }) + .await?; + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 2, usdc_bank_f) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 1, usdc_bank_f) + .await; + + assert!(res.is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn marginfi_account_liquidation_failure_bank_not_liquidatable() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + let sol_eq_bank_f = test_f.get_bank(&BankMint::SolEquivalent); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(200).await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 200) + .await?; + + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_sol = test_f.sol_mint.create_token_account_and_mint_to(100).await; + let borrower_token_account_sol_eq = test_f + .sol_equivalent_mint + .create_token_account_and_mint_to(100) + .await; + let borrower_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol.key, sol_bank_f, 10) + .await?; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_sol_eq.key, sol_eq_bank_f, 1) + .await?; + borrower_mfi_account_f + .try_bank_borrow(borrower_token_account_usdc.key, usdc_bank_f, 60) + .await?; + + sol_bank_f + .update_config(BankConfigOpt { + asset_weight_init: Some(I80F48!(0.25).into()), + asset_weight_maint: Some(I80F48!(0.4).into()), + ..Default::default() + }) + .await?; + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_eq_bank_f, 1, sol_bank_f) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalLiquidation); + + let res = lender_mfi_account_f + .try_liquidate(&borrower_mfi_account_f, sol_bank_f, 1, usdc_bank_f) + .await; + + assert!(res.is_ok()); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/mod.rs b/programs/marginfi/tests/user_actions/mod.rs new file mode 100644 index 000000000..f1c67ea06 --- /dev/null +++ b/programs/marginfi/tests/user_actions/mod.rs @@ -0,0 +1,505 @@ +mod borrow; +mod close_account; +mod close_balance; +mod create_account; +mod deposit; +mod flash_loan; +mod liquidate; +mod repay; +mod withdraw; + +use anchor_lang::prelude::Clock; +use fixed::types::I80F48; +use fixtures::{assert_custom_error, assert_eq_noise, native, prelude::*}; +use marginfi::{ + assert_eq_with_tolerance, + constants::{ + EMISSIONS_FLAG_BORROW_ACTIVE, EMISSIONS_FLAG_LENDING_ACTIVE, MIN_EMISSIONS_START_TIME, + }, + prelude::*, + state::marginfi_account::{ + BankAccountWrapper, DISABLED_FLAG, FLASHLOAN_ENABLED_FLAG, IN_FLASHLOAN_FLAG, + }, +}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use solana_sdk::timing::SECONDS_PER_YEAR; + +#[tokio::test] +async fn automatic_interest_payments() -> anyhow::Result<()> { + // Setup test executor with non-admin payer + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + // Create lender user accounts and deposit SOL asset + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 1_000) + .await?; + + // Create borrower user accounts and deposit USDC asset + let borrower_mfi_account_f = test_f.create_marginfi_account().await; + let borrower_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(1_000) + .await; + borrower_mfi_account_f + .try_bank_deposit(borrower_token_account_usdc.key, usdc_bank_f, 1_000) + .await?; + + // Borrow SOL from borrower mfi account + borrower_mfi_account_f + .try_bank_borrow(lender_token_account_sol.key, sol_bank_f, 99) + .await?; + + // Let a year go by + { + let mut ctx = test_f.context.borrow_mut(); + let mut clock: Clock = ctx.banks_client.get_sysvar().await?; + // Advance clock by 1 year + clock.unix_timestamp += 365 * 24 * 60 * 60; + ctx.set_sysvar(&clock); + } + + // Repay principal, leaving only the accrued interest + borrower_mfi_account_f + .try_bank_repay(lender_token_account_sol.key, sol_bank_f, 99, None) + .await?; + + let sol_bank = sol_bank_f.load().await; + let borrower_mfi_account = borrower_mfi_account_f.load().await; + let lender_mfi_account = lender_mfi_account_f.load().await; + + // Verify that interest accrued matches on both sides + assert_eq_noise!( + sol_bank + .get_liability_amount( + borrower_mfi_account.lending_account.balances[1] + .liability_shares + .into() + ) + .unwrap(), + I80F48::from(native!(11.761, "SOL", f64)), + native!(0.0002, "SOL", f64) + ); + + assert_eq_noise!( + sol_bank + .get_asset_amount( + lender_mfi_account.lending_account.balances[0] + .asset_shares + .into() + ) + .unwrap(), + I80F48::from(native!(1011.761, "SOL", f64)), + native!(0.0002, "SOL", f64) + ); + // TODO: check health is sane + + Ok(()) +} + +// Regression + +#[tokio::test] +async fn marginfi_account_correct_balance_selection_after_closing_position() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank_f = test_f.get_bank(&BankMint::Usdc); + let sol_bank_f = test_f.get_bank(&BankMint::Sol); + + let lender_mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_sol = test_f + .sol_mint + .create_token_account_and_mint_to(1_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_sol.key, sol_bank_f, 1_000) + .await?; + let lender_token_account_usdc = test_f + .usdc_mint + .create_token_account_and_mint_to(2_000) + .await; + lender_mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank_f, 2_000) + .await?; + + lender_mfi_account_f + .try_bank_withdraw(lender_token_account_sol.key, sol_bank_f, 0, Some(true)) + .await + .unwrap(); + + let mut marginfi_account = lender_mfi_account_f.load().await; + let mut usdc_bank = usdc_bank_f.load().await; + + let bank_account = BankAccountWrapper::find( + &usdc_bank_f.key, + &mut usdc_bank, + &mut marginfi_account.lending_account, + ); + + assert!(bank_account.is_ok()); + + let bank_account = bank_account.unwrap(); + + assert_eq!( + bank_account + .bank + .get_asset_amount(bank_account.balance.asset_shares.into()) + .unwrap() + .to_num::(), + native!(2_000, "USDC") + ); + + Ok(()) +} + +#[tokio::test] +async fn emissions_test() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + let sol_bank = test_f.get_bank(&BankMint::Sol); + + // Setup emissions (Deposit for USDC, Borrow for SOL) + + let funding_account = test_f.usdc_mint.create_token_account_and_mint_to(100).await; + + usdc_bank + .try_setup_emissions( + EMISSIONS_FLAG_LENDING_ACTIVE, + 1_000_000, + native!(50, "USDC"), + usdc_bank.mint.key, + funding_account.key, + usdc_bank.get_token_program(), + ) + .await?; + + // SOL Emissions are not in SOL Bank mint + let sol_emissions_mint = + MintFixture::new_token_22(test_f.context.clone(), None, Some(6), &[]).await; + + let funding_account = sol_emissions_mint + .create_token_account_and_mint_to(200) + .await; + + sol_bank + .try_setup_emissions( + EMISSIONS_FLAG_BORROW_ACTIVE, + 1_000_000, + native!(100, 6), + sol_emissions_mint.key, + funding_account.key, + sol_emissions_mint.token_program, + ) + .await?; + + let sol_emissions_mint_2 = + MintFixture::new_token_22(test_f.context.clone(), None, Some(6), &[]).await; + + let funding_account = sol_emissions_mint_2 + .create_token_account_and_mint_to(200) + .await; + + let res = sol_bank + .try_setup_emissions( + EMISSIONS_FLAG_BORROW_ACTIVE, + 1_000_000, + native!(50, 6), + sol_emissions_mint_2.key, + funding_account.key, + sol_emissions_mint_2.token_program, + ) + .await; + + assert_custom_error!(res.unwrap_err(), MarginfiError::EmissionsAlreadySetup); + + // Fund SOL bank + let sol_lender_account = test_f.create_marginfi_account().await; + let sol_lender_token_account = test_f.sol_mint.create_token_account_and_mint_to(100).await; + + sol_lender_account + .try_bank_deposit(sol_lender_token_account.key, sol_bank, 100) + .await?; + + // Create account and setup positions + test_f.set_time(MIN_EMISSIONS_START_TIME as i64); + test_f + .set_pyth_oracle_timestamp(PYTH_USDC_FEED, MIN_EMISSIONS_START_TIME as i64) + .await; + test_f + .set_pyth_oracle_timestamp(PYTH_SOL_FEED, MIN_EMISSIONS_START_TIME as i64) + .await; + + let mfi_account_f = test_f.create_marginfi_account().await; + let lender_token_account_usdc = test_f.usdc_mint.create_token_account_and_mint_to(50).await; + + mfi_account_f + .try_bank_deposit(lender_token_account_usdc.key, usdc_bank, 50) + .await?; + + let sol_account = test_f.sol_mint.create_empty_token_account().await; + + mfi_account_f + .try_bank_borrow(sol_account.key, sol_bank, 2) + .await?; + + // Advance for half a year and claim half emissions + test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; + + let lender_token_account_usdc = test_f.usdc_mint.create_empty_token_account().await; + + mfi_account_f + .try_withdraw_emissions(usdc_bank, &lender_token_account_usdc) + .await?; + + let sol_emissions_ta = sol_emissions_mint.create_empty_token_account().await; + + mfi_account_f + .try_withdraw_emissions(sol_bank, &sol_emissions_ta) + .await?; + + assert_eq_with_tolerance!( + lender_token_account_usdc.balance().await as i64, + native!(25, "USDC") as i64, + native!(1, "USDC") as i64 + ); + + assert_eq_with_tolerance!( + sol_emissions_ta.balance().await as i64, + native!(1, 6) as i64, + native!(0.1, 6, f64) as i64 + ); + + // Advance for another half a year and claim the rest + test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; + + mfi_account_f + .try_withdraw_emissions(usdc_bank, &lender_token_account_usdc) + .await?; + + assert_eq_with_tolerance!( + lender_token_account_usdc.balance().await as i64, + native!(50, "USDC") as i64, + native!(1, "USDC") as i64 + ); + + mfi_account_f + .try_withdraw_emissions(sol_bank, &sol_emissions_ta) + .await?; + + assert_eq_with_tolerance!( + sol_emissions_ta.balance().await as i64, + native!(2, 6) as i64, + native!(0.1, 6, f64) as i64 + ); + + // Advance a year, and no more USDC emissions can be claimed (drained), SOL emissions can be claimed + + test_f.advance_time((SECONDS_PER_YEAR / 2.0) as i64).await; + + mfi_account_f + .try_withdraw_emissions(usdc_bank, &lender_token_account_usdc) + .await?; + + mfi_account_f + .try_withdraw_emissions(sol_bank, &sol_emissions_ta) + .await?; + + assert_eq_with_tolerance!( + lender_token_account_usdc.balance().await as i64, + native!(50, "USDC") as i64, + native!(1, "USDC") as i64 + ); + + assert_eq_with_tolerance!( + sol_emissions_ta.balance().await as i64, + native!(3, 6) as i64, + native!(0.1, 6, f64) as i64 + ); + + // SOL lendeing account can't claim emissions, bc SOL is borrow only emissions + let sol_lender_emissions = sol_emissions_mint.create_empty_token_account().await; + + sol_lender_account + .try_withdraw_emissions(sol_bank, &sol_lender_emissions) + .await?; + + assert_eq!(sol_lender_emissions.balance().await as i64, 0); + + Ok(()) +} + +#[tokio::test] +async fn emissions_test_2() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let usdc_bank = test_f.get_bank(&BankMint::Usdc); + + let funding_account = test_f.usdc_mint.create_token_account_and_mint_to(100).await; + + usdc_bank + .try_setup_emissions( + EMISSIONS_FLAG_LENDING_ACTIVE, + 1_000_000, + native!(50, "USDC"), + usdc_bank.mint.key, + funding_account.key, + usdc_bank.get_token_program(), + ) + .await?; + + let usdc_bank_data = usdc_bank.load().await; + + assert_eq!(usdc_bank_data.flags, EMISSIONS_FLAG_LENDING_ACTIVE); + + assert_eq!(usdc_bank_data.emissions_rate, 1_000_000); + + assert_eq!( + I80F48::from(usdc_bank_data.emissions_remaining), + I80F48::from_num(native!(50, "USDC")) + ); + + usdc_bank + .try_update_emissions( + Some(EMISSIONS_FLAG_BORROW_ACTIVE), + Some(500_000), + Some((native!(25, "USDC"), funding_account.key)), + usdc_bank.get_token_program(), + ) + .await?; + + let usdc_bank_data = usdc_bank.load().await; + + assert_eq!(usdc_bank_data.flags, EMISSIONS_FLAG_BORROW_ACTIVE); + + assert_eq!(usdc_bank_data.emissions_rate, 500_000); + + assert_eq!( + I80F48::from(usdc_bank_data.emissions_remaining), + I80F48::from_num(native!(75, "USDC")) + ); + + Ok(()) +} + +#[tokio::test] +async fn emissions_setup_t22_with_fee() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let collateral_mint = BankMint::T22WithFee; + let bank_f = test_f.get_bank(&collateral_mint); + + let funding_account = bank_f.mint.create_token_account_and_mint_to(100).await; + + let emissions_vault = get_emissions_token_account_address(bank_f.key, bank_f.mint.key).0; + + let pre_vault_balance = 0; + + bank_f + .try_setup_emissions( + EMISSIONS_FLAG_LENDING_ACTIVE, + 1_000_000, + native!(50, bank_f.mint.mint.decimals), + bank_f.mint.key, + funding_account.key, + bank_f.get_token_program(), + ) + .await?; + + let post_vault_balance = TokenAccountFixture::fetch(test_f.context.clone(), emissions_vault) + .await + .balance() + .await; + + let bank = bank_f.load().await; + + assert_eq!(bank.flags, EMISSIONS_FLAG_LENDING_ACTIVE); + + assert_eq!(bank.emissions_rate, 1_000_000); + + assert_eq!( + I80F48::from(bank.emissions_remaining), + I80F48::from_num(native!(50, bank_f.mint.mint.decimals)) + ); + + let expected_vault_balance_delta = native!(50, bank_f.mint.mint.decimals) as u64; + let actual_vault_balance_delta = post_vault_balance - pre_vault_balance; + assert_eq!(expected_vault_balance_delta, actual_vault_balance_delta); + + let pre_vault_balance = TokenAccountFixture::fetch(test_f.context.clone(), emissions_vault) + .await + .balance() + .await; + + bank_f + .try_update_emissions( + Some(EMISSIONS_FLAG_BORROW_ACTIVE), + Some(500_000), + Some((native!(25, bank_f.mint.mint.decimals), funding_account.key)), + bank_f.get_token_program(), + ) + .await?; + + let post_vault_balance = TokenAccountFixture::fetch(test_f.context.clone(), emissions_vault) + .await + .balance() + .await; + + let bank_data = bank_f.load().await; + + assert_eq!(bank_data.flags, EMISSIONS_FLAG_BORROW_ACTIVE); + + assert_eq!(bank_data.emissions_rate, 500_000); + + assert_eq!( + I80F48::from(bank_data.emissions_remaining), + I80F48::from_num(native!(75, bank_f.mint.mint.decimals)) + ); + + let expected_vault_balance_delta = native!(25, bank_f.mint.mint.decimals) as u64; + let actual_vault_balance_delta = post_vault_balance - pre_vault_balance; + assert_eq!(expected_vault_balance_delta, actual_vault_balance_delta); + + Ok(()) +} + +#[tokio::test] +async fn account_flags() -> anyhow::Result<()> { + let test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + let mfi_account_f = test_f.create_marginfi_account().await; + + mfi_account_f.try_set_flag(FLASHLOAN_ENABLED_FLAG).await?; + + let mfi_account_data = mfi_account_f.load().await; + + assert_eq!(mfi_account_data.account_flags, FLASHLOAN_ENABLED_FLAG); + + assert!(mfi_account_data.get_flag(FLASHLOAN_ENABLED_FLAG)); + + mfi_account_f.try_unset_flag(FLASHLOAN_ENABLED_FLAG).await?; + + let mfi_account_data = mfi_account_f.load().await; + + assert_eq!(mfi_account_data.account_flags, 0); + + let res = mfi_account_f.try_set_flag(DISABLED_FLAG).await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlag); + + let res = mfi_account_f.try_unset_flag(IN_FLASHLOAN_FLAG).await; + + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::IllegalFlag); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/repay.rs b/programs/marginfi/tests/user_actions/repay.rs new file mode 100644 index 000000000..64a2b761a --- /dev/null +++ b/programs/marginfi/tests/user_actions/repay.rs @@ -0,0 +1,355 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; +use fixtures::{assert_custom_error, native, prelude::*}; +use marginfi::{assert_eq_with_tolerance, prelude::*, state::marginfi_group::BankVaultType}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use test_case::test_case; + +#[test_case(100., 9., BankMint::Usdc, BankMint::Sol)] +#[test_case(123456., 12345.599999999, BankMint::Usdc, BankMint::Sol)] +#[test_case(123456., 10000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(1., 1., BankMint::Sol, BankMint::Usdc)] +#[test_case(128932., 9834., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(240., 0.092, BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(36., 20., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_repay_success( + borrow_amount: f64, + repay_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_empty_token_account() + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank = test_f.get_bank(&debt_mint); + + user_mfi_account_f + .try_bank_borrow(user_debt_token_account_f.key, debt_bank, borrow_amount) + .await?; + + let pre_vault_balance = debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let marginfi_account = user_mfi_account_f.load().await; + let balance = marginfi_account + .lending_account + .get_balance(&debt_bank.key) + .unwrap(); + let pre_accounted = debt_bank + .load() + .await + .get_asset_amount(balance.liability_shares.into()) + .unwrap(); + + let res = user_mfi_account_f + .try_bank_repay(user_debt_token_account_f.key, debt_bank, repay_amount, None) + .await; + assert!(res.is_ok()); + + let post_vault_balance = debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + + let marginfi_account = user_mfi_account_f.load().await; + let balance = marginfi_account + .lending_account + .get_balance(&debt_bank.key) + .unwrap(); + let post_accounted = debt_bank + .load() + .await + .get_asset_amount(balance.liability_shares.into()) + .unwrap(); + + let expected_liquidity_vault_delta = + I80F48::from(native!(repay_amount, debt_bank.mint.mint.decimals, f64)); + let actual_liquidity_vault_delta = I80F48::from(post_vault_balance - pre_vault_balance); + let accounted_user_balance_delta = post_accounted - pre_accounted; + + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + assert_eq_with_tolerance!( + expected_liquidity_vault_delta, + -accounted_user_balance_delta, + 1 + ); + + Ok(()) +} + +#[test_case(100., BankMint::Usdc, BankMint::Sol)] +#[test_case(123456., BankMint::Usdc, BankMint::Sol)] +#[test_case(123456., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(1., BankMint::Sol, BankMint::Usdc)] +#[test_case(128932., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(240., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(36., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_repay_all_success( + borrow_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_token_account_and_mint_to(2. * borrow_amount + 1.) // to ensure user has enough to repay given interest and fees taken during borrow and repay + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank = test_f.get_bank(&debt_mint); + + user_mfi_account_f + .try_bank_borrow(user_debt_token_account_f.key, debt_bank, borrow_amount) + .await + .unwrap(); + + let (pre_vault_balance, pre_accounted_vault_balance) = { + let pre_vault_balance = debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let marginfi_account = user_mfi_account_f.load().await; + let balance = marginfi_account + .lending_account + .get_balance(&debt_bank.key) + .unwrap(); + let pre_accounted_vault_balance = debt_bank + .load() + .await + .get_asset_amount(balance.liability_shares.into()) + .unwrap(); + + (pre_vault_balance, pre_accounted_vault_balance) + }; + + let res = user_mfi_account_f + .try_bank_repay(user_debt_token_account_f.key, debt_bank, 0, Some(true)) + .await; + assert!(res.is_ok()); + + let (post_vault_balance, post_accounted_vault_balance) = { + let post_vault_balance = debt_bank + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let marginfi_account = user_mfi_account_f.load().await; + let balance = marginfi_account.lending_account.get_balance(&debt_bank.key); + assert!(balance.is_none()); + let post_accounted_vault_balance = I80F48!(0); + + (post_vault_balance, post_accounted_vault_balance) + }; + + let borrow_fee = debt_bank + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + tf.calculate_inverse_epoch_fee( + 0, + native!(borrow_amount, debt_bank.mint.mint.decimals, f64), + ) + .unwrap_or(0) + }) + .unwrap_or(0); + + let expected_liquidity_delta = + I80F48::from(native!(borrow_amount, debt_bank.mint.mint.decimals, f64) + borrow_fee); + let actual_liquidity_delta = I80F48::from(post_vault_balance) - I80F48::from(pre_vault_balance); + let accounted_liquidity_delta = post_accounted_vault_balance - pre_accounted_vault_balance; + + assert_eq!(expected_liquidity_delta, actual_liquidity_delta); + assert_eq_with_tolerance!(expected_liquidity_delta, -accounted_liquidity_delta, 1); + + Ok(()) +} + +#[test_case(100., 110., BankMint::Usdc, BankMint::Sol)] +#[test_case(123456., 123457., BankMint::Usdc, BankMint::Sol)] +#[test_case(3000., 10000., BankMint::UsdcSwb, BankMint::Sol)] +#[test_case(1., 1.000002, BankMint::Sol, BankMint::Usdc)] +#[test_case(9834., 234749., BankMint::PyUSD, BankMint::SolSwb)] +#[test_case(0.092, 240., BankMint::PyUSD, BankMint::T22WithFee)] +#[test_case(1.7, 36., BankMint::T22WithFee, BankMint::Sol)] +#[tokio::test] +async fn marginfi_account_repay_failure_repaying_too_much( + borrow_amount: f64, + repay_amount: f64, + collateral_mint: BankMint, + debt_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // LP + + let lp_deposit_amount = 2. * borrow_amount; + let lp_wallet_balance = get_max_deposit_amount_pre_fee(lp_deposit_amount); + let lp_mfi_account_f = test_f.create_marginfi_account().await; + let lp_collateral_token_account = test_f + .get_bank(&debt_mint) + .mint + .create_token_account_and_mint_to(lp_wallet_balance) + .await; + lp_mfi_account_f + .try_bank_deposit( + lp_collateral_token_account.key, + test_f.get_bank(&debt_mint), + lp_deposit_amount, + ) + .await?; + + // User + + let user_mfi_account_f = test_f.create_marginfi_account().await; + let sufficient_collateral_amount = test_f + .get_sufficient_collateral_for_outflow(borrow_amount, &collateral_mint, &debt_mint) + .await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(sufficient_collateral_amount); + let user_collateral_token_account_f = test_f + .get_bank_mut(&collateral_mint) + .mint + .create_token_account_and_mint_to(user_wallet_balance) + .await; + let user_debt_token_account_f = test_f + .get_bank_mut(&debt_mint) + .mint + .create_token_account_and_mint_to(2. * borrow_amount + 1.) // to ensure user has enough to repay given interest and fees taken during borrow and repay + .await; + user_mfi_account_f + .try_bank_deposit( + user_collateral_token_account_f.key, + test_f.get_bank(&collateral_mint), + sufficient_collateral_amount, + ) + .await?; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let debt_bank = test_f.get_bank(&debt_mint); + + user_mfi_account_f + .try_bank_borrow(user_debt_token_account_f.key, debt_bank, borrow_amount) + .await?; + + let res = user_mfi_account_f + .try_bank_repay(user_debt_token_account_f.key, debt_bank, repay_amount, None) + .await; + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::OperationRepayOnly); + + Ok(()) +} diff --git a/programs/marginfi/tests/user_actions/withdraw.rs b/programs/marginfi/tests/user_actions/withdraw.rs new file mode 100644 index 000000000..f48c49362 --- /dev/null +++ b/programs/marginfi/tests/user_actions/withdraw.rs @@ -0,0 +1,302 @@ +use anchor_spl::token_2022::spl_token_2022::extension::{ + transfer_fee::TransferFeeConfig, BaseStateWithExtensions, +}; +use fixed::types::I80F48; +use fixtures::{assert_custom_error, prelude::*, ui_to_native}; +use marginfi::{assert_eq_with_tolerance, prelude::*, state::marginfi_group::BankVaultType}; +use pretty_assertions::assert_eq; +use solana_program_test::*; +use test_case::test_case; + +#[test_case(0.03, 0.012, BankMint::Usdc)] +#[test_case(100.0, 100.0, BankMint::UsdcSwb)] +#[test_case(100.0, 100.0, BankMint::SolSwb)] +#[test_case(128932.0, 9834.0, BankMint::PyUSD)] +#[test_case(0.1, 0.092, BankMint::T22WithFee)] +#[test_case(100.0, 92.0, BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_account_withdraw_success( + deposit_amount: f64, + withdraw_amount: f64, + bank_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let marginfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let token_account_f = TokenAccountFixture::new( + test_f.context.clone(), + &test_f.get_bank(&bank_mint).mint, + &test_f.payer(), + ) + .await; + test_f + .get_bank_mut(&bank_mint) + .mint + .mint_to(&token_account_f.key, user_wallet_balance) + .await; + let bank_f = test_f.get_bank(&bank_mint); + marginfi_account_f + .try_bank_deposit(token_account_f.key, bank_f, deposit_amount) + .await + .unwrap(); + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let marginfi_account = marginfi_account_f.load().await; + let pre_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let balance = marginfi_account + .lending_account + .get_balance(&bank_f.key) + .unwrap(); + let pre_accounted = bank_f + .load() + .await + .get_asset_amount(balance.asset_shares.into()) + .unwrap(); + + let deposit_amount_native = ui_to_native!(deposit_amount, bank_f.mint.mint.decimals); + let withdraw_amount_native = ui_to_native!(withdraw_amount, bank_f.mint.mint.decimals); + let withdraw_fee_to_use; + let (withdraw_fee, withdraw_fee_if_excessive) = bank_f + .mint + .load_state() + .await + .get_extension::() + .map(|tf| { + ( + // withdraw <= available case + tf.calculate_inverse_epoch_fee(0, withdraw_amount_native) + .unwrap_or(0), + // withdraw all case, if withdraw > available + tf.calculate_epoch_fee(0, deposit_amount_native) + .unwrap_or(0), + ) + }) + .unwrap_or((0, 0)); + + // If exceeds available, clamp to available. + // If it does not, use specified withdraw amount + let adjusted_withdraw_amount = if withdraw_amount_native + withdraw_fee > deposit_amount_native + { + // Clamp to deposit amount minus fee if excessive + withdraw_fee_to_use = withdraw_fee_if_excessive; + deposit_amount + - withdraw_fee_if_excessive as f64 / 10_f64.powi(bank_f.mint.mint.decimals as i32) + } else { + // Use specified withdraw amount + withdraw_fee_to_use = withdraw_fee; + withdraw_amount + }; + + let res = marginfi_account_f + .try_bank_withdraw(token_account_f.key, bank_f, adjusted_withdraw_amount, None) + .await; + assert!(res.is_ok()); + + let post_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let marginfi_account = marginfi_account_f.load().await; + let balance = marginfi_account + .lending_account + .get_balance(&bank_f.key) + .unwrap(); + let post_accounted = bank_f + .load() + .await + .get_asset_amount(balance.asset_shares.into()) + .unwrap(); + + let active_balance_count = marginfi_account + .lending_account + .get_active_balances_iter() + .count(); + assert_eq!(1, active_balance_count); + + let expected_liquidity_vault_delta = -I80F48::from( + ui_to_native!(adjusted_withdraw_amount, bank_f.mint.mint.decimals) + withdraw_fee_to_use, + ); + let actual_liquidity_vault_delta = + I80F48::from(post_vault_balance) - I80F48::from(pre_vault_balance); + + let accounted_user_balance_delta = post_accounted - pre_accounted; + + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + assert_eq_with_tolerance!( + expected_liquidity_vault_delta, + accounted_user_balance_delta, + 1 + ); + + Ok(()) +} + +#[test_case(0.03, BankMint::Usdc)] +#[test_case(100.0, BankMint::Usdc)] +#[test_case(100.0, BankMint::Sol)] +#[test_case(128932.0, BankMint::PyUSD)] +#[test_case(0.1, BankMint::T22WithFee)] +#[test_case(100.0, BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_account_withdraw_all_success( + deposit_amount: f64, + bank_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let marginfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let token_account_f = TokenAccountFixture::new( + test_f.context.clone(), + &test_f.get_bank(&bank_mint).mint, + &test_f.payer(), + ) + .await; + test_f + .get_bank_mut(&bank_mint) + .mint + .mint_to(&token_account_f.key, user_wallet_balance) + .await; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let bank_f = test_f.get_bank(&bank_mint); + + marginfi_account_f + .try_bank_deposit(token_account_f.key, bank_f, deposit_amount) + .await + .unwrap(); + + let marginfi_account = marginfi_account_f.load().await; + let pre_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + let balance = marginfi_account + .lending_account + .get_balance(&bank_f.key) + .unwrap(); + let pre_accounted = bank_f + .load() + .await + .get_asset_amount(balance.asset_shares.into()) + .unwrap(); + + let res = marginfi_account_f + .try_bank_withdraw(token_account_f.key, bank_f, 0, Some(true)) + .await; + assert!(res.is_ok()); + + let marginfi_account = marginfi_account_f.load().await; + + let active_balance_count = marginfi_account + .lending_account + .get_active_balances_iter() + .count(); + assert_eq!(0, active_balance_count); + + let post_vault_balance = bank_f + .get_vault_token_account(BankVaultType::Liquidity) + .await + .balance() + .await; + assert!(marginfi_account + .lending_account + .get_balance(&bank_f.key) + .is_none()); + let post_accounted = I80F48::ZERO; + + let deposit_amount_native = ui_to_native!(deposit_amount, bank_f.mint.mint.decimals); + + let expected_liquidity_vault_delta = -I80F48::from(deposit_amount_native); + let actual_liquidity_vault_delta = + I80F48::from(post_vault_balance) - I80F48::from(pre_vault_balance); + let accounted_user_balance_delta = post_accounted - pre_accounted; + + assert_eq!(expected_liquidity_vault_delta, actual_liquidity_vault_delta); + assert_eq_with_tolerance!( + expected_liquidity_vault_delta, + accounted_user_balance_delta, + 1 + ); + + Ok(()) +} + +#[test_case(0.03, 0.030001, BankMint::Usdc)] +#[test_case(100., 101., BankMint::UsdcSwb)] +#[test_case(100., 102., BankMint::Sol)] +#[test_case(100., 102., BankMint::SolSwb)] +#[test_case(109247394., 109247394.000001, BankMint::PyUSD)] +#[test_case(16., 16., BankMint::T22WithFee)] +#[test_case(100., 98., BankMint::T22WithFee)] +#[tokio::test] +async fn marginfi_account_withdraw_failure_withdrawing_too_much( + deposit_amount: f64, + withdraw_amount: f64, + bank_mint: BankMint, +) -> anyhow::Result<()> { + // ------------------------------------------------------------------------- + // Setup + // ------------------------------------------------------------------------- + + let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; + + // User + + let marginfi_account_f = test_f.create_marginfi_account().await; + let user_wallet_balance = get_max_deposit_amount_pre_fee(deposit_amount); + let token_account_f = TokenAccountFixture::new( + test_f.context.clone(), + &test_f.get_bank(&bank_mint).mint, + &test_f.payer(), + ) + .await; + test_f + .get_bank_mut(&bank_mint) + .mint + .mint_to(&token_account_f.key, user_wallet_balance) + .await; + + // ------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------- + + let bank_f = test_f.get_bank(&bank_mint); + + marginfi_account_f + .try_bank_deposit(token_account_f.key, bank_f, deposit_amount) + .await?; + + let res = marginfi_account_f + .try_bank_withdraw(token_account_f.key, bank_f, withdraw_amount, None) + .await; + assert!(res.is_err()); + assert_custom_error!(res.unwrap_err(), MarginfiError::OperationWithdrawOnly); + + Ok(()) +} diff --git a/programs/marginfi/tests/withdraw_fees.rs b/programs/marginfi/tests/withdraw_fees.rs index b34aafed6..62905c606 100644 --- a/programs/marginfi/tests/withdraw_fees.rs +++ b/programs/marginfi/tests/withdraw_fees.rs @@ -1,3 +1,4 @@ +use fixed_macro::types::I80F48; use fixtures::test::{BankMint, TestFixture, TestSettings}; use marginfi::state::marginfi_group::GroupConfig; use solana_program_test::tokio; @@ -9,15 +10,16 @@ async fn marginfi_group_withdraw_fees_and_insurance() -> anyhow::Result<()> { let mut test_f = TestFixture::new(Some(TestSettings::all_banks_payer_not_admin())).await; // Mint 1000 USDC to the insurance vault - let bank_f = test_f.banks.get(&BankMint::USDC).unwrap(); + let bank_f = test_f.banks.get(&BankMint::Usdc).unwrap(); let bank = bank_f.load().await; test_f.usdc_mint.mint_to(&bank.insurance_vault, 1000).await; // Create a receiving account and try to withdraw 1000 USDC from the insurance vault let receiving_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; bank_f - .try_withdraw_insurance(&receiving_account, 1000) - .await?; + .try_admin_withdraw_insurance(&receiving_account, I80F48!(1000)) + .await + .unwrap(); assert_eq!(receiving_account.balance().await, 1000); // Verifies that the receiving account balance is 1000 USDC // Mint 750 USDC to the fee vault @@ -42,7 +44,7 @@ async fn marginfi_group_withdraw_fees_and_insurance() -> anyhow::Result<()> { // Create a receiving account and try to withdraw 1000 USDC from the insurance vault let receiving_account = test_f.usdc_mint.create_token_account_and_mint_to(0).await; let res = bank_f - .try_withdraw_insurance(&receiving_account, 1000) + .try_admin_withdraw_insurance(&receiving_account, I80F48!(1000)) .await; assert!(res.is_err()); // Unable to withdraw 1000 USDC from the insurance vault, because the signer is not the admin diff --git a/programs/test_transfer_hook/Cargo.toml b/programs/test_transfer_hook/Cargo.toml new file mode 100644 index 000000000..b23f23a7d --- /dev/null +++ b/programs/test_transfer_hook/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "test_transfer_hook" +version = "0.1.0" +edition = "2021" + +[features] +idl-build = [] +no-entrypoint = [] + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +solana-program = { workspace = true } +spl-token-2022 = { workspace = true, features = ["no-entrypoint"] } +spl-transfer-hook-interface = { workspace = true } +spl-tlv-account-resolution = { workspace = true } diff --git a/programs/test_transfer_hook/src/lib.rs b/programs/test_transfer_hook/src/lib.rs new file mode 100644 index 000000000..ddda3069f --- /dev/null +++ b/programs/test_transfer_hook/src/lib.rs @@ -0,0 +1,260 @@ +//! Program state processor +use { + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + program::invoke_signed, + program_error::ProgramError, + pubkey, + pubkey::Pubkey, + system_instruction, + }, + spl_tlv_account_resolution::{account::ExtraAccountMeta, state::ExtraAccountMetaList}, + spl_token_2022::{ + extension::{ + transfer_hook::TransferHookAccount, BaseStateWithExtensions, StateWithExtensions, + }, + state::{Account, Mint}, + }, + spl_transfer_hook_interface::{ + collect_extra_account_metas_signer_seeds, + error::TransferHookError, + get_extra_account_metas_address, get_extra_account_metas_address_and_bump_seed, + instruction::{ExecuteInstruction, TransferHookInstruction}, + }, +}; + +pub static TEST_HOOK_ID: Pubkey = pubkey!("TRANSFERHKTRANSFERHKTRANSFERHKTRANSFERHKTRA"); + +#[cfg(not(feature = "no-entrypoint"))] +solana_program::entrypoint!(process); + +#[allow(unused)] +fn check_token_account_is_transferring(account_info: &AccountInfo) -> Result<(), ProgramError> { + let account_data = account_info.try_borrow_data()?; + let token_account = StateWithExtensions::::unpack(&account_data)?; + let extension = token_account.get_extension::()?; + if bool::from(extension.transferring) { + Ok(()) + } else { + Err(TransferHookError::ProgramCalledOutsideOfTransfer.into()) + } +} + +/// Processes an [Execute](enum.TransferHookInstruction.html) instruction. +pub fn process_execute( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _amount: u64, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + 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)?; + + Ok(()) +} + +/// 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(); + + let extra_account_metas_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + let _system_program_info = next_account_info(account_info_iter)?; + + // check that the one mint we want to target is trying to create extra + // account metas + #[cfg(feature = "forbid-additional-mints")] + if *mint_info.key != crate::mint::id() { + return Err(ProgramError::InvalidArgument); + } + + // check that the mint authority is valid without fully deserializing + let mint_data = mint_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + let mint_authority = mint + .base + .mint_authority + .ok_or(TransferHookError::MintHasNoMintAuthority)?; + + // Check signers + if !authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if *authority_info.key != mint_authority { + return Err(TransferHookError::IncorrectMintAuthority.into()); + } + + // Check validation account + let (expected_validation_address, bump_seed) = + get_extra_account_metas_address_and_bump_seed(mint_info.key, program_id); + if expected_validation_address != *extra_account_metas_info.key { + return Err(ProgramError::InvalidSeeds); + } + + // Create the account + let bump_seed = [bump_seed]; + let signer_seeds = collect_extra_account_metas_signer_seeds(mint_info.key, &bump_seed); + 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()], + &[&signer_seeds], + )?; + invoke_signed( + &system_instruction::assign(extra_account_metas_info.key, program_id), + &[extra_account_metas_info.clone()], + &[&signer_seeds], + )?; + + // Write the data + let mut data = extra_account_metas_info.try_borrow_mut_data()?; + ExtraAccountMetaList::init::(&mut data, extra_account_metas)?; + + Ok(()) +} + +/// Processes a +/// [UpdateExtraAccountMetaList](enum.TransferHookInstruction.html) +/// instruction. +pub fn process_update_extra_account_meta_list( + program_id: &Pubkey, + accounts: &[AccountInfo], + extra_account_metas: &[ExtraAccountMeta], +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let extra_account_metas_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + // check that the mint authority is valid without fully deserializing + let mint_data = mint_info.try_borrow_data()?; + let mint = StateWithExtensions::::unpack(&mint_data)?; + let mint_authority = mint + .base + .mint_authority + .ok_or(TransferHookError::MintHasNoMintAuthority)?; + + // Check signers + if !authority_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + if *authority_info.key != mint_authority { + return Err(TransferHookError::IncorrectMintAuthority.into()); + } + + // Check validation account + 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); + } + + // Check if the extra metas have been initialized + let min_account_size = ExtraAccountMetaList::size_of(0)?; + let original_account_size = extra_account_metas_info.data_len(); + if program_id != extra_account_metas_info.owner || original_account_size < min_account_size { + return Err(ProgramError::UninitializedAccount); + } + + // If the new extra_account_metas length is different, resize the account and + // update + let length = extra_account_metas.len(); + let account_size = ExtraAccountMetaList::size_of(length)?; + if account_size >= original_account_size { + extra_account_metas_info.realloc(account_size, false)?; + let mut data = extra_account_metas_info.try_borrow_mut_data()?; + ExtraAccountMetaList::update::(&mut data, extra_account_metas)?; + } else { + { + let mut data = extra_account_metas_info.try_borrow_mut_data()?; + ExtraAccountMetaList::update::(&mut data, extra_account_metas)?; + } + extra_account_metas_info.realloc(account_size, false)?; + } + + Ok(()) +} + +/// Processes an [Instruction](enum.Instruction.html). +pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = TransferHookInstruction::unpack(input)?; + + match instruction { + TransferHookInstruction::Execute { amount } => { + msg!("Instruction: Execute"); + process_execute(program_id, accounts, amount) + } + TransferHookInstruction::InitializeExtraAccountMetaList { + extra_account_metas, + } => { + msg!("Instruction: InitializeExtraAccountMetaList"); + process_initialize_extra_account_meta_list(program_id, accounts, &extra_account_metas) + } + TransferHookInstruction::UpdateExtraAccountMetaList { + extra_account_metas, + } => { + msg!("Instruction: UpdateExtraAccountMetaList"); + process_update_extra_account_meta_list(program_id, accounts, &extra_account_metas) + } + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use solana_program_test::{processor, tokio, ProgramTest}; +// use solana_sdk::{ +// instruction::{AccountMeta, Instruction}, +// signer::Signer, +// transaction::Transaction, +// }; +// use spl_discriminator::SplDiscriminate; +// use spl_transfer_hook_interface::instruction::ExecuteInstruction; + +// #[tokio::test] +// async fn invoke_hook() { +// let mut ctx = ProgramTest::new("transfer_hook", TEST_HOOK_ID, processor!(super::process)) +// .start_with_context() +// .await; + +// let mut data = ExecuteInstruction::SPL_DISCRIMINATOR_SLICE.to_vec(); +// data.extend_from_slice(&0_u64.to_le_bytes()); + +// let ix = Instruction { +// program_id: TEST_HOOK_ID, +// accounts: vec![ +// AccountMeta::new_readonly(Pubkey::new_unique(), false), +// AccountMeta::new_readonly(Pubkey::new_unique(), false), +// AccountMeta::new_readonly(Pubkey::new_unique(), false), +// AccountMeta::new_readonly(Pubkey::new_unique(), false), +// AccountMeta::new_readonly(Pubkey::new_unique(), false), +// ], +// data, +// }; +// let transaction = Transaction::new_signed_with_payer( +// &[ix], +// Some(&ctx.payer.pubkey()), +// &[&ctx.payer], +// ctx.last_blockhash, +// ); + +// ctx.banks_client +// .process_transaction(transaction) +// .await +// .unwrap(); +// } +// } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..7897a24d1 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.75.0" diff --git a/scripts/build-program-verifiable.sh b/scripts/build-program-verifiable.sh index af9d7beeb..5ea69ea65 100755 --- a/scripts/build-program-verifiable.sh +++ b/scripts/build-program-verifiable.sh @@ -9,22 +9,24 @@ if [ "$?" != "0" ]; then fi program_lib_name=$1 -cluster=$2 +deployment=$2 -if [ -z "$program_lib_name" ] || [ -z "$cluster" ]; then - echo "Usage: $0 " +if [ -z "$program_lib_name" ] || [ -z "$deployment" ]; then + echo "Usage: $0 " exit 1 fi -if [ "$cluster" = "mainnet" ]; then - cluster_feature="mainnet-beta" -elif [ "$cluster" = "devnet" ]; then - cluster_feature=" devnet" +if [ "$deployment" = "mainnet" ]; then + features="--features mainnet-beta" +elif [ "$deployment" = "devnet" ]; then + features="--features devnet" +elif [ "$deployment" = "staging" ]; then + features="--features staging" else - echo "Error: Unknown cluster: $cluster" + echo "Error: Unknown deployment: $deployment" exit 1 fi -cmd="sudo $verify_bin build --library-name $program_lib_name -- --features $cluster_feature" +cmd="sudo $verify_bin build --library-name $program_lib_name -- $features" echo "Running: $cmd" eval "$cmd" diff --git a/scripts/build-workspace.sh b/scripts/build-workspace.sh index bde7360f6..77055eada 100755 --- a/scripts/build-workspace.sh +++ b/scripts/build-workspace.sh @@ -2,7 +2,6 @@ ROOT=$(git rev-parse --show-toplevel) cd $ROOT -cmd="anchor build" - +cmd="anchor build --no-idl" echo "Running: $cmd" eval "$cmd" diff --git a/scripts/deploy-staging-program.sh b/scripts/deploy-staging-program.sh new file mode 100644 index 000000000..c1cc452fd --- /dev/null +++ b/scripts/deploy-staging-program.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env sh + +ROOT=$(git rev-parse --show-toplevel) +cd $ROOT + +set -e + +ask_confirmation() { + while true; do + read -p "Are you sure you want to proceed? (y/n): " yn + case $yn in + [Yy]* ) return 0;; + [Nn]* ) return 1;; + * ) echo "Please answer yes (y) or no (n).";; + esac + done +} + +deployer_key_path=$1 +[ -z "$deployer_key_path" ] && echo "Missing deployer_key_path argument" && exit 1 +[ ! -f "$deployer_key_path" ] && echo "$deployer_key_path is not a file" && exit 1 + +program_address_or_keypair=$2 +[ -z "$program_address_or_keypair" ] && echo "Missing program_address_or_keypair argument" && exit 1 + +deployer_pk=$(solana-keygen pubkey $deployer_key_path) +deployer_balance=$(solana balance $deployer_key_path) +set +e +exist_result=$(solana account $program_address_or_keypair 2>&1) +set -e + +if [ "$exist_result" = *"Error: AccountNotFound:"* ]; then + is_upgrade=0 +else + is_upgrade=1 +fi + +if [ -f "$program_address_or_keypair" ]; then + program_id=$(solana-keygen pubkey $program_address_or_keypair) +else + if [ "$is_upgrade" = "0" ]; then + echo "You need to provide a private key path for a first deploy." + exit 1 + else + program_id=$program_address_or_keypair + fi +fi + +echo "=========================================================================================" +echo "Deployer: $deployer_pk" +echo "Balance: $deployer_balance" +printf "Deploying to: $program_id" +if [ "$is_upgrade" = "0" ]; then + echo " (first deployment)" +else + echo " (already deployed)" +fi + +if ! ask_confirmation; then + echo "Cancelled." + exit 0 +fi + +if [ "$is_upgrade" = "0" ]; then + echo "Deploying..." +else + echo "Upgrading..." +fi + +solana program deploy \ + --use-rpc \ + --url $url \ + --fee-payer $deployer_key_path \ + --keypair $deployer_key_path \ + --program-id $program_address_or_keypair \ + "$ROOT/target/deploy/marginfi.so" diff --git a/scripts/fuzz-program.sh b/scripts/fuzz-program.sh new file mode 100755 index 000000000..9b4b2f45b --- /dev/null +++ b/scripts/fuzz-program.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e + +ROOT=$(git rev-parse --show-toplevel) +cd $ROOT + +cd $ROOT/programs/marginfi/fuzz +cmd="cargo +nightly fuzz run lend -Zbuild-std --strip-dead-code --no-cfg-fuzzing -- -max_total_time=300 -verbosity=0" +echo "Running: $cmd" +eval "$cmd" diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 000000000..cf5c83b4d --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +cargo clippy --features=test,test-bpf -- -D warnings -A clippy::await_holding_refcell_ref -A clippy::comparison_chain -A clippy::too_many_arguments diff --git a/scripts/test-program.sh b/scripts/test-program.sh index 92f2a7f51..6595e253a 100755 --- a/scripts/test-program.sh +++ b/scripts/test-program.sh @@ -14,12 +14,20 @@ fi if [ "$loglevel" == "--sane" ]; then loglevel=warn - nocapture="" + nocapture="--test-threads=1" else loglevel=debug - nocapture="-- --nocapture" + nocapture="--nocapture" fi -cmd="RUST_LOG=solana_runtime::message_processor::stable_log=$loglevel cargo test --package $program_lib_name --features=test,test-bpf $nocapture" +if [ "$program_lib_name" == "all" ]; then + package_filter="" +else + package_filter="--package $program_lib_name" +fi + +extra_params="${@:3}" + +cmd="SBF_OUT_DIR=$ROOT/target/deploy RUST_LOG=solana_runtime::message_processor::stable_log=$loglevel cargo nextest run --no-fail-fast $package_filter --features=test,test-bpf $nocapture -- $extra_params" echo "Running: $cmd" eval "$cmd" diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 98250d32c..70420d416 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -7,19 +7,24 @@ edition = "2021" [features] lip = ["liquidity-incentive-program"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +solana-cli-output = { workspace = true } solana-program = { workspace = true } solana-logger = { workspace = true } solana-program-test = { workspace = true } solana-sdk = { workspace = true } +spl-token-2022 = { workspace = true } +spl-transfer-hook-interface = { workspace = true } +spl-tlv-account-resolution = { workspace = true } +spl-discriminator = { workspace = true } anchor-lang = { workspace = true } +anchor-lang-29 = { workspace = true } anchor-spl = { workspace = true } pyth-sdk-solana = { workspace = true } -switchboard-v2 = { workspace = true } +pyth-solana-receiver-sdk = { workspace = true } +switchboard-solana = { workspace = true } bytemuck = "1.9.1" fixed = "1.12.0" @@ -32,11 +37,18 @@ assert_matches = "1.5.0" bincode = "1.3.3" futures = "0.3.25" pretty_assertions = "1.2.1" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.115" [dependencies.marginfi] path = "../programs/marginfi" features = ["test-bpf"] +[dependencies.transfer_hook] +path = "../programs/test_transfer_hook" +package = "test_transfer_hook" +features = ["no-entrypoint"] + [dependencies.liquidity-incentive-program] path = "../programs/liquidity-incentive-program" optional = true diff --git a/test-utils/data/7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE.bin b/test-utils/data/7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE.bin new file mode 100644 index 000000000..1661f3e6e Binary files /dev/null and b/test-utils/data/7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE.bin differ diff --git a/test-utils/data/Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD.bin b/test-utils/data/Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD.bin new file mode 100644 index 000000000..020f0a7b9 Binary files /dev/null and b/test-utils/data/Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD.bin differ diff --git a/test-utils/data/H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG.bin b/test-utils/data/H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG.bin new file mode 100644 index 000000000..e1ce71ecf Binary files /dev/null and b/test-utils/data/H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG.bin differ diff --git a/test-utils/src/bank.rs b/test-utils/src/bank.rs index 035244e0f..12c0f5be9 100644 --- a/test-utils/src/bank.rs +++ b/test-utils/src/bank.rs @@ -7,19 +7,24 @@ use anchor_lang::{ prelude::{AccountMeta, Pubkey}, InstructionData, ToAccountMetas, }; -use anchor_spl::token; + use fixed::types::I80F48; use marginfi::{ bank_authority_seed, - state::marginfi_group::{Bank, BankConfigOpt, BankVaultType}, + state::{ + liquid_insurance_fund::{LiquidInsuranceFund, LiquidInsuranceFundAccount}, + marginfi_group::{Bank, BankConfigOpt, BankVaultType}, + price::{OraclePriceFeedAdapter, OraclePriceType, PriceAdapter}, + }, utils::{find_bank_vault_authority_pda, find_bank_vault_pda}, }; +use solana_program::account_info::IntoAccountInfo; use solana_program::instruction::Instruction; +use solana_program::sysvar::clock::Clock; use solana_program_test::BanksClientError; use solana_program_test::ProgramTestContext; -#[cfg(feature = "lip")] use solana_sdk::signature::Keypair; -use solana_sdk::{signer::Signer, transaction::Transaction}; +use solana_sdk::{signer::Signer, system_program, transaction::Transaction}; use std::{cell::RefCell, fmt::Debug, rc::Rc}; #[derive(Clone)] @@ -27,6 +32,7 @@ pub struct BankFixture { ctx: Rc>, pub key: Pubkey, pub mint: MintFixture, + pub lif: Option, } impl BankFixture { @@ -39,9 +45,14 @@ impl BankFixture { ctx, key, mint: mint_fixture.clone(), + lif: None, } } + pub fn get_token_program(&self) -> Pubkey { + self.mint.token_program + } + pub fn get_vault(&self, vault_type: BankVaultType) -> (Pubkey, u8) { find_bank_vault_pda(&self.key, vault_type) } @@ -50,6 +61,28 @@ impl BankFixture { find_bank_vault_authority_pda(&self.key, vault_type) } + pub async fn get_price(&self) -> f64 { + let bank = self.load().await; + let oracle_key = bank.config.oracle_keys[0]; + let mut oracle_account = self + .ctx + .borrow_mut() + .banks_client + .get_account(oracle_key) + .await + .unwrap() + .unwrap(); + let ai = (&oracle_key, &mut oracle_account).into_account_info(); + let oracle_adapter = + OraclePriceFeedAdapter::try_from_bank_config(&bank.config, &[ai], &Clock::default()) + .unwrap(); + + oracle_adapter + .get_price_of_type(OraclePriceType::RealTime, None) + .unwrap() + .to_num() + } + pub async fn load(&self) -> Bank { load_and_deserialize::(self.ctx.clone(), &self.key).await } @@ -122,7 +155,7 @@ impl BankFixture { admin: self.ctx.borrow().payer.pubkey(), funding_account: reward_funding_account, rent: solana_program::sysvar::rent::id(), - token_program: anchor_spl::token::ID, + token_program: self.get_token_program(), system_program: solana_program::system_program::id(), } .to_account_metas(Some(true)), @@ -165,6 +198,7 @@ impl BankFixture { total_emissions: u64, emissions_mint: Pubkey, funding_account: Pubkey, + token_program: Pubkey, ) -> Result<(), BanksClientError> { let ix = Instruction { program_id: marginfi::id(), @@ -180,7 +214,7 @@ impl BankFixture { emissions_mint, ) .0, - token_program: anchor_spl::token::ID, + token_program, system_program: solana_program::system_program::id(), } .to_account_metas(Some(true)), @@ -217,6 +251,7 @@ impl BankFixture { emissions_flags: Option, emissions_rate: Option, additional_emissions: Option<(u64, Pubkey)>, + token_program: Pubkey, ) -> Result<(), BanksClientError> { let bank = self.load().await; @@ -233,7 +268,7 @@ impl BankFixture { bank.emissions_mint, ) .0, - token_program: anchor_spl::token::ID, + token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingPoolUpdateEmissionsParameters { @@ -277,18 +312,23 @@ impl BankFixture { &marginfi::id(), ); + let mut accounts = marginfi::accounts::LendingPoolWithdrawFees { + marginfi_group: bank.group, + token_program: receiving_account.token_program, + bank: self.key, + admin: signer_pk, + fee_vault: bank.fee_vault, + fee_vault_authority, + dst_token_account: receiving_account.key, + } + .to_account_metas(Some(true)); + if self.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(self.mint.key, false)); + } + let ix = Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingPoolWithdrawFees { - marginfi_group: bank.group, - token_program: token::ID, - bank: self.key, - admin: signer_pk, - fee_vault: bank.fee_vault, - fee_vault_authority, - dst_token_account: receiving_account.key, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingPoolWithdrawFees { amount }.data(), }; @@ -304,10 +344,10 @@ impl BankFixture { Ok(()) } - pub async fn try_withdraw_insurance( + pub async fn try_admin_withdraw_insurance( &self, receiving_account: &TokenAccountFixture, - amount: u64, + amount: I80F48, ) -> Result<(), BanksClientError> { let bank = self.load().await; let mut ctx = self.ctx.borrow_mut(); @@ -316,20 +356,75 @@ impl BankFixture { bank_authority_seed!(BankVaultType::Insurance, self.key), &marginfi::id(), ); + let mut accounts = marginfi::accounts::LendingPoolAdminDepositWithdrawInsurance { + marginfi_group: bank.group, + token_program: self.get_token_program(), + bank: self.key, + admin: signer_pk, + insurance_vault: bank.insurance_vault, + insurance_vault_authority, + admin_token_account: receiving_account.key, + liquid_insurance_fund: LiquidInsuranceFund::address(&self.key), + } + .to_account_metas(Some(true)); + + if self.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(self.mint.key, false)); + } let ix = Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingPoolWithdrawInsurance { - marginfi_group: bank.group, - token_program: token::ID, - bank: self.key, - admin: signer_pk, - insurance_vault: bank.insurance_vault, - insurance_vault_authority, - dst_token_account: receiving_account.key, + accounts, + data: marginfi::instruction::LendingPoolWithdrawInsurance { + amount: amount.into(), } - .to_account_metas(Some(true)), - data: marginfi::instruction::LendingPoolWithdrawInsurance { amount }.data(), + .data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(()) + } + + pub async fn try_admin_deposit_insurance( + &self, + source_account: &TokenAccountFixture, + amount: u64, + ) -> Result<(), BanksClientError> { + let bank = self.load().await; + let mut ctx = self.ctx.borrow_mut(); + let signer_pk = ctx.payer.pubkey(); + let (insurance_vault_authority, _) = Pubkey::find_program_address( + bank_authority_seed!(BankVaultType::Insurance, self.key), + &marginfi::id(), + ); + + let mut accounts = marginfi::accounts::LendingPoolAdminDepositWithdrawInsurance { + marginfi_group: bank.group, + token_program: source_account.token_program, + bank: self.key, + admin: signer_pk, + insurance_vault: bank.insurance_vault, + insurance_vault_authority, + admin_token_account: source_account.key, + liquid_insurance_fund: LiquidInsuranceFund::address(&self.key), + } + .to_account_metas(Some(true)); + if self.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(self.mint.key, false)); + } + + let ix = Instruction { + program_id: marginfi::id(), + accounts, + data: marginfi::instruction::LendingPoolDepositInsurance { amount }.data(), }; let tx = Transaction::new_signed_with_payer( @@ -344,6 +439,32 @@ impl BankFixture { Ok(()) } + pub async fn try_create_lif_account(&self, user: Keypair) -> Result<(), BanksClientError> { + let mut ctx = self.ctx.borrow_mut(); + + let ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::CreateLiquidInsuranceFundAccount { + user_insurance_fund_account: LiquidInsuranceFundAccount::address(&user.pubkey()), + signer: user.pubkey(), + system_program: system_program::ID, + } + .to_account_metas(Some(true)), + data: marginfi::instruction::CreateLiquidInsuranceFundAccount {}.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &user], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(()) + } + pub async fn get_vault_token_account(&self, vault_type: BankVaultType) -> TokenAccountFixture { let (vault, _) = self.get_vault(vault_type); diff --git a/test-utils/src/fixtures/pyUSD.json b/test-utils/src/fixtures/pyUSD.json new file mode 100644 index 000000000..2a67fc921 --- /dev/null +++ b/test-utils/src/fixtures/pyUSD.json @@ -0,0 +1,14 @@ +{ + "pubkey": "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", + "account": { + "lamports": 8903240, + "data": [ + "AQAAAEWL8jQUbMmHNpVOADdvZA1z7a1jyDebZa3bW4Jv0uMVjawAc+0oAAAGAQEAAAAXhTJh72q4Uypn8FOGWq0xKT/PB88SCrW5oVcGVI3AKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMAIAAXhTJh72q4Uypn8FOGWq0xKT/PB88SCrW5oVcGVI3AKwwAIAAXhTJh72q4Uypn8FOGWq0xKT/PB88SCrW5oVcGVI3AKwEAbAAXhTJh72q4Uypn8FOGWq0xKT/PB88SCrW5oVcGVI3AKxeFMmHvarhTKmfwU4ZarTEpP88HzxIKtbmhVwZUjcArAAAAAAAAAABdAgAAAAAAAAAAAAAAAAAAAABdAgAAAAAAAAAAAAAAAAAAAAAEAEEAF4UyYe9quFMqZ/BThlqtMSk/zwfPEgq1uaFXBlSNwCsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAIEAF4UyYe9quFMqZ/BThlqtMSk/zwfPEgq1uaFXBlSNwCscN+ZDO3ME3YJzeuQNm4vzxJ9bDmxJqNUzKLPlBpAcVwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgBAABeFMmHvarhTKmfwU4ZarTEpP88HzxIKtbmhVwZUjcArAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAEAAgnQVMyO1m+9u94xUbiBsQYh7lCdtZO+gj0jz14I76NgXkkg7bIoqh7dHHYFPlZH5OVyECpzj2fTVun06S4p0nhMArgCCdBUzI7Wb7273jFRuIGxBiHuUJ21k76CPSPPXgjvo2BeSSDtsiiqHt0cdgU+Vkfk5XIQKnOPZ9NW6fTpLinSeCgAAAFBheVBhbCBVU0QFAAAAUFlVU0RPAAAAaHR0cHM6Ly90b2tlbi1tZXRhZGF0YS5wYXhvcy5jb20vcHl1c2RfbWV0YWRhdGEvcHJvZC9zb2xhbmEvcHl1c2RfbWV0YWRhdGEuanNvbgAAAAA=", + "base64" + ], + "owner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 866 + } +} \ No newline at end of file diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index 9a3f76a8e..ec6714c32 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -1,4 +1,5 @@ pub mod bank; +pub mod lif; #[cfg(feature = "lip")] pub mod lip; pub mod marginfi_account; @@ -6,4 +7,6 @@ pub mod marginfi_group; pub mod prelude; pub mod spl; pub mod test; +// pub mod transfer_hook; pub mod utils; +pub use transfer_hook; diff --git a/test-utils/src/lif.rs b/test-utils/src/lif.rs new file mode 100644 index 000000000..898f6ae8b --- /dev/null +++ b/test-utils/src/lif.rs @@ -0,0 +1,237 @@ +use std::{cell::RefCell, rc::Rc}; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use fixed::types::I80F48; +use marginfi::state::{ + liquid_insurance_fund::{LiquidInsuranceFund, LiquidInsuranceFundAccount}, + marginfi_group::BankVaultType, +}; +use solana_program_test::{BanksClientError, ProgramTestContext}; +use solana_sdk::{ + account::AccountSharedData, compute_budget::ComputeBudgetInstruction, instruction::Instruction, + pubkey::Pubkey, signature::Keypair, signer::Signer, system_program, transaction::Transaction, +}; +use switchboard_solana::AccountMeta; + +use crate::{bank::BankFixture, prelude::TokenAccountFixture}; + +pub struct LiquidInsuranceFundFixture { + ctx: Rc>, + pub key: Pubkey, + data: Vec, +} + +impl LiquidInsuranceFundFixture { + pub fn new(ctx: Rc>, key: Pubkey) -> LiquidInsuranceFundFixture { + LiquidInsuranceFundFixture { + ctx, + key, + data: vec![], + } + } + + pub async fn load<'a>(&'a mut self) -> &'a LiquidInsuranceFund { + let mut ctx = self.ctx.borrow_mut(); + + // Fetch account + let account = ctx + .banks_client + .get_account(self.key) + .await + .unwrap() + .unwrap(); + + self.data = account.data; + + // Deserialize + bytemuck::from_bytes(&self.data[8..]) + } + + pub async fn create_lif_user_account( + &self, + ) -> Result { + let mut ctx = self.ctx.borrow_mut(); + let user = Keypair::new(); + // Give user some SOL + ctx.set_account( + &user.pubkey(), + &AccountSharedData::new(1_000_000_000, 0, &system_program::ID), + ); + + let lif_account = LiquidInsuranceFundAccount::address(&user.pubkey()); + let ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::CreateLiquidInsuranceFundAccount { + user_insurance_fund_account: lif_account, + signer: user.pubkey(), + system_program: system_program::ID, + } + .to_account_metas(Some(true)), + data: marginfi::instruction::CreateLiquidInsuranceFundAccount {}.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &user], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(LiquidInsuranceFundAccountFixture { + ctx: self.ctx.clone(), + user, + key: lif_account, + data: vec![], + }) + } +} + +pub struct LiquidInsuranceFundAccountFixture { + pub ctx: Rc>, + user: Keypair, + key: Pubkey, + data: Vec, +} + +impl LiquidInsuranceFundAccountFixture { + pub fn user(&self) -> &Keypair { + &self.user + } + + pub async fn load<'a>(&'a mut self) -> &'a mut LiquidInsuranceFundAccount { + let mut ctx = self.ctx.borrow_mut(); + + // Fetch account + let account = ctx + .banks_client + .get_account(self.key) + .await + .unwrap() + .unwrap(); + + self.data = account.data; + + // Deserialize + bytemuck::from_bytes_mut(&mut self.data[8..]) + } + + pub async fn try_user_deposit_insurance( + &self, + bank: &BankFixture, + source_account: &TokenAccountFixture, + amount: u64, + ) -> Result<(), BanksClientError> { + let mut ctx = self.ctx.borrow_mut(); + + let mut accounts = marginfi::accounts::DepositIntoLiquidInsuranceFund { + token_program: bank.get_token_program(), + liquid_insurance_fund: LiquidInsuranceFund::address(&bank.key), + signer: self.user.pubkey(), + signer_token_account: source_account.key, + system_program: system_program::ID, + bank_insurance_vault: bank.get_vault(BankVaultType::Insurance).0, + user_insurance_fund_account: LiquidInsuranceFundAccount::address(&self.user.pubkey()), + } + .to_account_metas(Some(true)); + + if source_account.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new(source_account.token.mint, false)); + } + + let ix = Instruction { + program_id: marginfi::id(), + accounts, + data: marginfi::instruction::DepositIntoLiquidInsuranceFund { amount }.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&self.user.pubkey()), + &[&self.user], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(()) + } + + pub async fn try_user_withdraw_insurance_request( + &self, + bank: &BankFixture, + shares: Option, + nonce: u64, + ) -> Result<(), BanksClientError> { + let mut ctx = self.ctx.borrow_mut(); + + let ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::WithdrawRequestLiquidInsuranceFund { + user_insurance_fund_account: self.key, + liquid_insurance_fund: LiquidInsuranceFund::address(&bank.key), + signer: self.user.pubkey(), + } + .to_account_metas(Some(true)), + data: marginfi::instruction::CreateWithdrawRequestFromLiquidTokenFund { + amount: shares.map(|s| s.into()), + } + .data(), + }; + + let nonce_ix = ComputeBudgetInstruction::set_compute_unit_price(nonce); + let tx = Transaction::new_signed_with_payer( + &[ix, nonce_ix], + Some(&self.user.pubkey()), + &[&self.user], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(()) + } + + pub async fn try_user_withdraw_insurance_claim( + &self, + bank: &BankFixture, + dest_token_account: &TokenAccountFixture, + nonce: u64, + ) -> Result<(), BanksClientError> { + let mut ctx = self.ctx.borrow_mut(); + + let mut accounts = marginfi::accounts::SettleWithdrawClaimInLiquidInsuranceFund { + user_insurance_fund_account: self.key, + signer_token_account: dest_token_account.key, + bank_insurance_vault: bank.get_vault(BankVaultType::Insurance).0, + bank_insurance_vault_authority: bank.get_vault_authority(BankVaultType::Insurance).0, + token_program: dest_token_account.token_program, + liquid_insurance_fund: LiquidInsuranceFund::address(&bank.key), + signer: self.user.pubkey(), + } + .to_account_metas(Some(true)); + + if dest_token_account.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new(dest_token_account.token.mint, false)); + } + + let ix = Instruction { + program_id: marginfi::id(), + accounts, + data: marginfi::instruction::SettleWithdrawClaimInLiquidInsuranceFund {}.data(), + }; + let nonce_ix = ComputeBudgetInstruction::set_compute_unit_price(nonce); + + let tx = Transaction::new_signed_with_payer( + &[nonce_ix, ix], + Some(&self.user.pubkey()), + &[&self.user], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await?; + + Ok(()) + } +} diff --git a/test-utils/src/lip.rs b/test-utils/src/lip.rs index f689ae8a0..c2e22b1e7 100644 --- a/test-utils/src/lip.rs +++ b/test-utils/src/lip.rs @@ -50,7 +50,7 @@ impl LipCampaignFixture { marginfi_account: get_marginfi_account_address(deposit_key.pubkey()).0, marginfi_bank_vault: bank.liquidity_vault, marginfi_program: marginfi::id(), - token_program: anchor_spl::token::ID, + token_program: self.bank_f.get_token_program(), rent: anchor_lang::solana_program::sysvar::rent::id(), system_program: solana_program::system_program::id(), } @@ -108,7 +108,7 @@ impl LipCampaignFixture { .get_vault_authority(marginfi::state::marginfi_group::BankVaultType::Liquidity) .0, marginfi_program: marginfi::id(), - token_program: anchor_spl::token::ID, + token_program: self.bank_f.get_token_program(), system_program: solana_program::system_program::id(), } .to_account_metas(Some(true)), diff --git a/test-utils/src/marginfi_account.rs b/test-utils/src/marginfi_account.rs index 33f4075ca..6eb85468e 100644 --- a/test-utils/src/marginfi_account.rs +++ b/test-utils/src/marginfi_account.rs @@ -1,10 +1,11 @@ use super::{bank::BankFixture, prelude::*}; use crate::ui_to_native; use anchor_lang::{prelude::*, system_program, InstructionData, ToAccountMetas}; -use anchor_spl::token; + use marginfi::state::{ marginfi_account::MarginfiAccount, marginfi_group::{Bank, BankVaultType}, + price::OracleSetup, }; use solana_program::{instruction::Instruction, sysvar}; use solana_program_test::{BanksClientError, ProgramTestContext}; @@ -70,18 +71,23 @@ impl MarginfiAccountFixture { let marginfi_account = self.load().await; let ctx = self.ctx.borrow_mut(); + let mut accounts = marginfi::accounts::LendingAccountDeposit { + marginfi_group: marginfi_account.group, + marginfi_account: self.key, + signer: ctx.payer.pubkey(), + bank: bank.key, + signer_token_account: funding_account, + bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, + token_program: bank.get_token_program(), + } + .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } + Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingAccountDeposit { - marginfi_group: marginfi_account.group, - marginfi_account: self.key, - signer: ctx.payer.pubkey(), - bank: bank.key, - signer_token_account: funding_account, - bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, - token_program: token::ID, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingAccountDeposit { amount: ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), } @@ -89,16 +95,46 @@ impl MarginfiAccountFixture { } } - pub async fn try_bank_deposit>( + pub async fn try_bank_deposit + Copy>( &self, funding_account: Pubkey, bank: &BankFixture, ui_amount: T, ) -> anyhow::Result<(), BanksClientError> { - let ix = self + let mut ix = self .make_bank_deposit_ix(funding_account, bank, ui_amount) .await; + // If t22 with transfer hook, add remaining accounts + let fetch_account_data_fn = |key| async move { + Ok(self + .ctx + .borrow_mut() + .banks_client + .get_account(key) + .await + .map(|acc| acc.map(|a| a.data))?) + }; + let payer = self.ctx.borrow_mut().payer.pubkey(); + if bank.mint.token_program == spl_token_2022::ID { + // TODO: do that only if hook exists + println!( + "[TODO] Adding extra account metas for execute for mint {:?}", + bank.mint.key + ); + let _ = spl_transfer_hook_interface::offchain::add_extra_account_metas_for_execute( + &mut ix, + &super::transfer_hook::TEST_HOOK_ID, + &funding_account, + &bank.mint.key, + &bank.get_vault(BankVaultType::Liquidity).0, + &payer, + ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), + fetch_account_data_fn, + ) + .await; + } + let mut ctx = self.ctx.borrow_mut(); let tx = Transaction::new_signed_with_payer( &[ix], @@ -121,21 +157,24 @@ impl MarginfiAccountFixture { ) -> Instruction { let marginfi_account = self.load().await; + let mut accounts = marginfi::accounts::LendingAccountWithdraw { + marginfi_group: marginfi_account.group, + marginfi_account: self.key, + signer: self.ctx.borrow().payer.pubkey(), + bank: bank.key, + destination_token_account: destination_account, + bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, + bank_liquidity_vault_authority: bank.get_vault_authority(BankVaultType::Liquidity).0, + token_program: bank.get_token_program(), + } + .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } + let mut ix = Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingAccountWithdraw { - marginfi_group: marginfi_account.group, - marginfi_account: self.key, - signer: self.ctx.borrow().payer.pubkey(), - bank: bank.key, - destination_token_account: destination_account, - bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, - bank_liquidity_vault_authority: bank - .get_vault_authority(BankVaultType::Liquidity) - .0, - token_program: token::ID, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingAccountWithdraw { amount: ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), withdraw_all, @@ -187,21 +226,25 @@ impl MarginfiAccountFixture { ui_amount: T, ) -> Instruction { let marginfi_account = self.load().await; + + let mut accounts = marginfi::accounts::LendingAccountBorrow { + marginfi_group: marginfi_account.group, + marginfi_account: self.key, + signer: self.ctx.borrow().payer.pubkey(), + bank: bank.key, + destination_token_account: destination_account, + bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, + bank_liquidity_vault_authority: bank.get_vault_authority(BankVaultType::Liquidity).0, + token_program: bank.get_token_program(), + } + .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } + let mut ix = Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingAccountBorrow { - marginfi_group: marginfi_account.group, - marginfi_account: self.key, - signer: self.ctx.borrow().payer.pubkey(), - bank: bank.key, - destination_token_account: destination_account, - bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, - bank_liquidity_vault_authority: bank - .get_vault_authority(BankVaultType::Liquidity) - .0, - token_program: token::ID, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingAccountBorrow { amount: ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), } @@ -217,7 +260,7 @@ impl MarginfiAccountFixture { ix } - pub async fn try_bank_borrow>( + pub async fn try_bank_borrow + Copy>( &self, destination_account: Pubkey, bank: &BankFixture, @@ -227,17 +270,42 @@ impl MarginfiAccountFixture { .await } - pub async fn try_bank_borrow_with_nonce>( + pub async fn try_bank_borrow_with_nonce + Copy>( &self, destination_account: Pubkey, bank: &BankFixture, ui_amount: T, nonce: u64, ) -> anyhow::Result<(), BanksClientError> { - let ix = self + let mut ix = self .make_bank_borrow_ix(destination_account, bank, ui_amount) .await; + if bank.mint.token_program == spl_token_2022::ID { + let fetch_account_data_fn = |key| async move { + Ok(self + .ctx + .borrow_mut() + .banks_client + .get_account(key) + .await + .map(|acc| acc.map(|a| a.data))?) + }; + + let payer = self.ctx.borrow().payer.pubkey(); + let _ = spl_transfer_hook_interface::offchain::add_extra_account_metas_for_execute( + &mut ix, + &super::transfer_hook::TEST_HOOK_ID, + &bank.get_vault(BankVaultType::Liquidity).0, + &bank.mint.key, + &destination_account, + &payer, + ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), + fetch_account_data_fn, + ) + .await; + } + let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(1_400_000); let nonce_ix = ComputeBudgetInstruction::set_compute_unit_price(nonce); @@ -264,18 +332,23 @@ impl MarginfiAccountFixture { let marginfi_account = self.load().await; let ctx = self.ctx.borrow_mut(); + let mut accounts = marginfi::accounts::LendingAccountRepay { + marginfi_group: marginfi_account.group, + marginfi_account: self.key, + signer: ctx.payer.pubkey(), + bank: bank.key, + signer_token_account: funding_account, + bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, + token_program: bank.get_token_program(), + } + .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } + Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingAccountRepay { - marginfi_group: marginfi_account.group, - marginfi_account: self.key, - signer: ctx.payer.pubkey(), - bank: bank.key, - signer_token_account: funding_account, - bank_liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, - token_program: token::ID, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingAccountRepay { amount: ui_to_native!(ui_amount.into(), bank.mint.mint.decimals), repay_all, @@ -338,7 +411,7 @@ impl MarginfiAccountFixture { Ok(()) } - pub async fn try_liquidate>( + pub async fn try_liquidate + Copy>( &self, liquidatee: &MarginfiAccountFixture, asset_bank_fixture: &BankFixture, @@ -362,14 +435,32 @@ impl MarginfiAccountFixture { .0, bank_liquidity_vault: liab_bank_fixture.get_vault(BankVaultType::Liquidity).0, bank_insurance_vault: liab_bank_fixture.get_vault(BankVaultType::Insurance).0, - token_program: token::ID, + token_program: liab_bank_fixture.get_token_program(), } .to_account_metas(Some(true)); - accounts.extend(vec![ - AccountMeta::new_readonly(asset_bank.config.oracle_keys[0], false), - AccountMeta::new_readonly(liab_bank.config.oracle_keys[0], false), - ]); + if liab_bank_fixture.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(liab_bank_fixture.mint.key, false)); + } + + let oracle_accounts = vec![asset_bank.config, liab_bank.config] + .iter() + .map(|config| { + AccountMeta::new_readonly( + { + match config.oracle_setup { + OracleSetup::PythPushOracle => { + get_oracle_id_from_feed_id(config.oracle_keys[0]).unwrap() + } + _ => config.oracle_keys[0], + } + }, + false, + ) + }) + .collect::>(); + + accounts.extend(oracle_accounts); let mut ix = Instruction { program_id: marginfi::id(), @@ -383,6 +474,31 @@ impl MarginfiAccountFixture { .data(), }; + if liab_bank_fixture.mint.token_program == spl_token_2022::ID { + let payer = self.ctx.borrow().payer.pubkey(); + let fetch_account_data_fn = |key| async move { + Ok(self + .ctx + .borrow_mut() + .banks_client + .get_account(key) + .await + .map(|acc| acc.map(|a| a.data))?) + }; + + let _ = spl_transfer_hook_interface::offchain::add_extra_account_metas_for_execute( + &mut ix, + &super::transfer_hook::TEST_HOOK_ID, + &liab_bank_fixture.mint.key, + &liab_bank_fixture.mint.key, + &liab_bank_fixture.mint.key, + &payer, + 0, + fetch_account_data_fn, + ) + .await; + } + ix.accounts.extend_from_slice( &self .load_observation_account_metas( @@ -414,7 +530,7 @@ impl MarginfiAccountFixture { pub async fn try_withdraw_emissions( &self, bank: &BankFixture, - recv_account: Pubkey, + recv_account: &TokenAccountFixture, ) -> std::result::Result<(), BanksClientError> { let emissions_mint = bank.load().await.emissions_mint; let ix = Instruction { @@ -426,9 +542,9 @@ impl MarginfiAccountFixture { emissions_mint, emissions_auth: get_emissions_authority_address(bank.key, emissions_mint).0, emissions_vault: get_emissions_token_account_address(bank.key, emissions_mint).0, - destination_account: recv_account, + destination_account: recv_account.key, bank: bank.key, - token_program: token::ID, + token_program: recv_account.token_program, } .to_account_metas(Some(true)), data: marginfi::instruction::LendingAccountWithdrawEmissions {}.data(), @@ -570,6 +686,7 @@ impl MarginfiAccountFixture { exclude_banks: Vec, ) -> Vec { let marginfi_account = self.load().await; + // Check all active banks in marginfi account balances let mut bank_pks = marginfi_account .lending_account .balances @@ -583,6 +700,8 @@ impl MarginfiAccountFixture { }) .collect::>(); + // Add bank pks in include_banks if they are not already in bank_pks + // (and exclude the ones contained in exclude_banks) for bank_pk in include_banks { if !bank_pks.contains(&bank_pk) { bank_pks.push(bank_pk); @@ -590,16 +709,28 @@ impl MarginfiAccountFixture { } bank_pks.retain(|bank_pk| !exclude_banks.contains(bank_pk)); + // Load all banks let mut banks = vec![]; for bank_pk in bank_pks.clone() { let bank = load_and_deserialize::(self.ctx.clone(), &bank_pk).await; banks.push(bank); } + // Bank -> AccountMetas let account_metas = banks .iter() .zip(bank_pks.iter()) .flat_map(|(bank, bank_pk)| { + let oracle_key = { + let oracle_key = bank.config.oracle_keys[0]; + match bank.config.oracle_setup { + OracleSetup::PythPushOracle => { + get_oracle_id_from_feed_id(oracle_key).unwrap() + } + _ => oracle_key, + } + }; + vec![ AccountMeta { pubkey: *bank_pk, @@ -607,7 +738,7 @@ impl MarginfiAccountFixture { is_writable: false, }, AccountMeta { - pubkey: bank.config.oracle_keys[0], + pubkey: oracle_key, is_signer: false, is_writable: false, }, @@ -678,4 +809,28 @@ impl MarginfiAccountFixture { ctx.banks_client.process_transaction(tx).await } + + pub async fn try_close_account(&self, nonce: u64) -> std::result::Result<(), BanksClientError> { + let mut ctx: std::cell::RefMut = self.ctx.borrow_mut(); + + let ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::MarginfiAccountClose { + marginfi_account: self.key, + authority: ctx.payer.pubkey(), + fee_payer: ctx.payer.pubkey(), + } + .to_account_metas(Some(true)), + data: marginfi::instruction::MarginfiAccountClose {}.data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ComputeBudgetInstruction::set_compute_unit_price(nonce), ix], + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await + } } diff --git a/test-utils/src/marginfi_group.rs b/test-utils/src/marginfi_group.rs index 9a6b36646..8c73f919c 100644 --- a/test-utils/src/marginfi_group.rs +++ b/test-utils/src/marginfi_group.rs @@ -1,12 +1,16 @@ use super::{bank::BankFixture, marginfi_account::MarginfiAccountFixture}; -use crate::prelude::MintFixture; +use crate::lif::LiquidInsuranceFundFixture; +use crate::prelude::{get_oracle_id_from_feed_id, MintFixture}; use crate::utils::*; use anchor_lang::{prelude::*, solana_program::system_program, InstructionData}; -use anchor_spl::token; + use anyhow::Result; use marginfi::{ prelude::MarginfiGroup, - state::marginfi_group::{BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, + state::{ + liquid_insurance_fund::LiquidInsuranceFund, + marginfi_group::{BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, + }, }; use solana_program::sysvar; use solana_program_test::*; @@ -92,12 +96,22 @@ impl MarginfiGroupFixture { fee_vault_authority: bank_fixture.get_vault_authority(BankVaultType::Fee).0, fee_vault: bank_fixture.get_vault(BankVaultType::Fee).0, rent: sysvar::rent::id(), - token_program: token::ID, + token_program: bank_asset_mint_fixture.token_program, system_program: system_program::id(), } .to_account_metas(Some(true)); - accounts.push(AccountMeta::new_readonly(bank_config.oracle_keys[0], false)); + let oracle_key = { + let oracle_key_or_feed_id = bank_config.oracle_keys[0]; + match bank_config.oracle_setup { + marginfi::state::price::OracleSetup::PythPushOracle => { + get_oracle_id_from_feed_id(oracle_key_or_feed_id).unwrap() + } + _ => oracle_key_or_feed_id, + } + }; + + accounts.push(AccountMeta::new_readonly(oracle_key, false)); let ix = Instruction { program_id: marginfi::id(), @@ -159,7 +173,7 @@ impl MarginfiGroupFixture { fee_vault_authority: bank_fixture.get_vault_authority(BankVaultType::Fee).0, fee_vault: bank_fixture.get_vault(BankVaultType::Fee).0, rent: sysvar::rent::id(), - token_program: token::ID, + token_program: bank_fixture.get_token_program(), system_program: system_program::id(), } .to_account_metas(Some(true)); @@ -297,18 +311,23 @@ impl MarginfiGroupFixture { pub async fn try_collect_fees(&self, bank: &BankFixture) -> Result<()> { let mut ctx = self.ctx.borrow_mut(); + let mut accounts = marginfi::accounts::LendingPoolCollectBankFees { + marginfi_group: self.key, + bank: bank.key, + liquidity_vault_authority: bank.get_vault_authority(BankVaultType::Liquidity).0, + liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, + insurance_vault: bank.get_vault(BankVaultType::Insurance).0, + fee_vault: bank.get_vault(BankVaultType::Fee).0, + token_program: bank.get_token_program(), + } + .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } + let ix = Instruction { program_id: marginfi::id(), - accounts: marginfi::accounts::LendingPoolCollectBankFees { - marginfi_group: self.key, - bank: bank.key, - liquidity_vault_authority: bank.get_vault_authority(BankVaultType::Liquidity).0, - liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, - insurance_vault: bank.get_vault(BankVaultType::Insurance).0, - fee_vault: bank.get_vault(BankVaultType::Fee).0, - token_program: token::ID, - } - .to_account_metas(Some(true)), + accounts, data: marginfi::instruction::LendingPoolCollectBankFees {}.data(), }; @@ -347,9 +366,12 @@ impl MarginfiGroupFixture { liquidity_vault: bank.get_vault(BankVaultType::Liquidity).0, insurance_vault: bank.get_vault(BankVaultType::Insurance).0, insurance_vault_authority: bank.get_vault_authority(BankVaultType::Insurance).0, - token_program: token::ID, + token_program: bank.get_token_program(), } .to_account_metas(Some(true)); + if bank.mint.token_program == spl_token_2022::ID { + accounts.push(AccountMeta::new_readonly(bank.mint.key, false)); + } accounts.append( &mut marginfi_account @@ -388,4 +410,50 @@ impl MarginfiGroupFixture { ) .await } + + pub async fn try_create_liquid_insurance_fund( + &self, + bank_fixture: &BankFixture, + min_withdraw_period: u64, + ) -> Result { + let accounts = marginfi::accounts::CreateLiquidInsuranceFund { + marginfi_group: self.key, + signer: self.ctx.borrow().payer.pubkey(), + bank: bank_fixture.key, + rent: sysvar::rent::id(), + token_program: bank_fixture.get_token_program(), + system_program: system_program::id(), + liquid_insurance_fund: LiquidInsuranceFund::address(&bank_fixture.key), + lif_vault: bank_fixture.get_vault(BankVaultType::Insurance).0, + lif_authority: bank_fixture.get_vault_authority(BankVaultType::Insurance).0, + } + .to_account_metas(None); + + let ix = Instruction { + program_id: marginfi::id(), + accounts, + data: marginfi::instruction::CreateLiquidInsuranceFund { + min_withdraw_period: min_withdraw_period.try_into().unwrap(), + } + .data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&self.ctx.borrow().payer.pubkey().clone()), + &[&self.ctx.borrow().payer], + self.ctx.borrow().last_blockhash, + ); + + self.ctx + .borrow_mut() + .banks_client + .process_transaction(tx) + .await?; + + Ok(LiquidInsuranceFundFixture::new( + self.ctx.clone(), + LiquidInsuranceFund::address(&bank_fixture.key), + )) + } } diff --git a/test-utils/src/spl.rs b/test-utils/src/spl.rs index 4d95920e4..6f686d900 100644 --- a/test-utils/src/spl.rs +++ b/test-utils/src/spl.rs @@ -1,24 +1,44 @@ -use crate::ui_to_native; +use crate::{transfer_hook::TEST_HOOK_ID, ui_to_native}; use anchor_lang::prelude::*; -use anchor_spl::token::{ - spl_token::{ +use anchor_spl::{ + token::{spl_token, Mint, TokenAccount}, + token_2022::{ self, - instruction::{initialize_mint, mint_to}, + spl_token_2022::{ + self, + extension::{ + interest_bearing_mint::InterestBearingConfig, + mint_close_authority::MintCloseAuthority, permanent_delegate::PermanentDelegate, + transfer_fee::TransferFee, transfer_hook::TransferHook, BaseState, + BaseStateWithExtensions, ExtensionType, StateWithExtensionsOwned, + }, + }, }, - Mint, TokenAccount, + token_interface::spl_pod::bytemuck::pod_get_packed_len, }; +use solana_cli_output::CliAccount; use solana_program_test::ProgramTestContext; use solana_sdk::{ - instruction::Instruction, signature::Keypair, signer::Signer, - system_instruction::create_account, transaction::Transaction, + account::{AccountSharedData, ReadableAccount, WritableAccount}, + instruction::Instruction, + native_token::LAMPORTS_PER_SOL, + program_pack::{Pack, Sealed}, + signature::Keypair, + signer::Signer, + system_instruction::{self, create_account}, + transaction::Transaction, }; -use std::{cell::RefCell, rc::Rc}; +use spl_transfer_hook_interface::{ + get_extra_account_metas_address, instruction::initialize_extra_account_meta_list, +}; +use std::{cell::RefCell, fs::File, io::Read, path::PathBuf, rc::Rc, str::FromStr}; #[derive(Clone)] pub struct MintFixture { pub ctx: Rc>, pub key: Pubkey, - pub mint: Mint, + pub mint: spl_token_2022::state::Mint, + pub token_program: Pubkey, } impl MintFixture { @@ -41,7 +61,7 @@ impl MintFixture { Mint::LEN as u64, &spl_token::id(), ); - let init_mint_ix = initialize_mint( + let init_mint_ix = spl_token::instruction::initialize_mint( &spl_token::id(), &keypair.pubkey(), &ctx.payer.pubkey(), @@ -66,13 +86,158 @@ impl MintFixture { .unwrap() .unwrap(); - Mint::try_deserialize(&mut mint_account.data.as_slice()).unwrap() + spl_token_2022::state::Mint::unpack(mint_account.data.as_slice()).unwrap() + }; + + MintFixture { + ctx: ctx_ref, + key: keypair.pubkey(), + mint, + token_program: spl_token::id(), + } + } + + pub async fn new_token_22( + ctx: Rc>, + mint_keypair: Option, + mint_decimals: Option, + extensions: &[SupportedExtension], + ) -> MintFixture { + let ctx_ref = Rc::clone(&ctx); + let keypair = mint_keypair.unwrap_or_else(Keypair::new); + let program = token_2022::ID; + let mint = { + let mut ctx = ctx.borrow_mut(); + + let rent = ctx.banks_client.get_rent().await.unwrap(); + + let extension_types = SupportedExtension::types(extensions.iter()); + let len = ExtensionType::try_calculate_account_len::( + &extension_types, + ) + .unwrap(); + let init_account_ix = create_account( + &ctx.payer.pubkey(), + &keypair.pubkey(), + rent.minimum_balance(len), + len as u64, + &program, + ); + let init_mint_ix = spl_token_2022::instruction::initialize_mint( + &program, + &keypair.pubkey(), + &ctx.payer.pubkey(), + None, + mint_decimals.unwrap_or(6), + ) + .unwrap(); + + let mut ixs = vec![init_account_ix]; + ixs.extend( + extensions + .iter() + .map(|e| e.instruction(&keypair.pubkey(), &ctx.payer.pubkey())), + ); + ixs.push(init_mint_ix); + let extra_metas_address = get_extra_account_metas_address( + &keypair.pubkey(), + &super::transfer_hook::TEST_HOOK_ID, + ); + if extensions.contains(&SupportedExtension::TransferHook) { + ixs.push(system_instruction::transfer( + &ctx.payer.pubkey(), + &extra_metas_address, + 10 * LAMPORTS_PER_SOL, + )); + ixs.push(initialize_extra_account_meta_list( + &super::transfer_hook::TEST_HOOK_ID, + &extra_metas_address, + &keypair.pubkey(), + &ctx.payer.pubkey(), + &[], + )) + } + + let tx = Transaction::new_signed_with_payer( + &ixs, + Some(&ctx.payer.pubkey()), + &[&ctx.payer, &keypair], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await.unwrap(); + + if extensions.contains(&SupportedExtension::TransferHook) { + ctx.banks_client + .get_account(extra_metas_address) + .await + .unwrap() + .unwrap(); + } + + let mint_account = ctx + .banks_client + .get_account(keypair.pubkey()) + .await + .unwrap() + .unwrap(); + + StateWithExtensionsOwned::::unpack(mint_account.data) + .unwrap() + .base }; MintFixture { ctx: ctx_ref, key: keypair.pubkey(), mint, + token_program: token_2022::ID, + } + } + + pub fn new_from_file( + ctx: &Rc>, + relative_path: &str, + ) -> MintFixture { + let ctx_ref = Rc::clone(ctx); + + let (address, account_info) = { + let mut ctx = ctx.borrow_mut(); + + // load cargo workspace path from env + let mut path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); + path.push(relative_path); + let mut file = File::open(&path).unwrap(); + let mut account_info_raw = String::new(); + file.read_to_string(&mut account_info_raw).unwrap(); + + let account: CliAccount = serde_json::from_str(&account_info_raw).unwrap(); + let address = Pubkey::from_str(&account.keyed_account.pubkey).unwrap(); + let mut account_info: AccountSharedData = + account.keyed_account.account.decode().unwrap(); + + let mut mint = + spl_token::state::Mint::unpack(&account_info.data()[..Mint::LEN]).unwrap(); + let payer = ctx.payer.pubkey(); + mint.mint_authority.replace(payer); + + let mint_bytes = &mut [0; Mint::LEN]; + spl_token::state::Mint::pack(mint, mint_bytes).unwrap(); + + account_info.data_as_mut_slice()[..Mint::LEN].copy_from_slice(mint_bytes); + + ctx.set_account(&address, &account_info); + + (address, account_info) + }; + + let mint = spl_token_2022::state::Mint::unpack(&account_info.data()[..Mint::LEN]).unwrap(); + + MintFixture { + ctx: ctx_ref, + key: address, + mint, + token_program: account_info.owner().to_owned(), } } @@ -86,7 +251,10 @@ impl MintFixture { .await .unwrap() .unwrap(); - self.mint = Mint::try_deserialize(&mut mint_account.data.as_slice()).unwrap(); + self.mint = + StateWithExtensionsOwned::::unpack(mint_account.data) + .unwrap() + .base; } pub async fn mint_to>(&mut self, dest: &Pubkey, ui_amount: T) { @@ -114,8 +282,8 @@ impl MintFixture { pub fn make_mint_to_ix(&self, dest: &Pubkey, amount: u64) -> Instruction { let ctx = self.ctx.borrow(); - mint_to( - &spl_token::id(), + spl_token_2022::instruction::mint_to( + &self.token_program, &self.key, dest, &ctx.payer.pubkey(), @@ -125,12 +293,22 @@ impl MintFixture { .unwrap() } + pub async fn create_empty_token_account(&self) -> TokenAccountFixture { + self.create_token_account_and_mint_to(0.0).await + } + pub async fn create_token_account_and_mint_to>( &self, ui_amount: T, ) -> TokenAccountFixture { let payer = self.ctx.borrow().payer.pubkey(); - let token_account_f = TokenAccountFixture::new(self.ctx.clone(), &self.key, &payer).await; + let token_account_f = TokenAccountFixture::new_with_token_program( + self.ctx.clone(), + &self.key, + &payer, + &self.token_program, + ) + .await; let mint_to_ix = self.make_mint_to_ix( &token_account_f.key, @@ -150,39 +328,127 @@ impl MintFixture { token_account_f } + + pub async fn create_token_account_with_owner_and_mint_to>( + &self, + owner: &Keypair, + ui_amount: T, + ) -> TokenAccountFixture { + let token_account_f = TokenAccountFixture::new_with_token_program( + self.ctx.clone(), + &self.key, + &owner.pubkey(), + &self.token_program, + ) + .await; + + let mint_to_ix = self.make_mint_to_ix( + &token_account_f.key, + ui_to_native!(ui_amount.into(), self.mint.decimals), + ); + let mut ctx = self.ctx.borrow_mut(); + + let tx = Transaction::new_signed_with_payer( + &[mint_to_ix], + Some(&ctx.payer.pubkey()), + &[&ctx.payer], + ctx.last_blockhash, + ); + + ctx.banks_client.process_transaction(tx).await.unwrap(); + + token_account_f + } + + pub async fn load_state(&self) -> StateWithExtensionsOwned { + let mint_account = self + .ctx + .borrow_mut() + .banks_client + .get_account(self.key) + .await + .unwrap() + .unwrap(); + + StateWithExtensionsOwned::unpack(mint_account.data).unwrap() + } } pub struct TokenAccountFixture { ctx: Rc>, pub key: Pubkey, - pub token: TokenAccount, + pub token: spl_token_2022::state::Account, + pub token_program: Pubkey, } impl TokenAccountFixture { pub async fn create_ixs( + ctx: &Rc>, rent: Rent, mint_pk: &Pubkey, payer_pk: &Pubkey, owner_pk: &Pubkey, keypair: &Keypair, - ) -> [Instruction; 2] { - let init_account_ix = create_account( - payer_pk, - &keypair.pubkey(), - rent.minimum_balance(TokenAccount::LEN), - TokenAccount::LEN as u64, - &spl_token::id(), - ); + token_program: &Pubkey, + ) -> Vec { + let mut ixs = vec![]; - let init_token_ix = spl_token::instruction::initialize_account( - &spl_token::id(), - &keypair.pubkey(), - mint_pk, - owner_pk, + // Get extensions if t22 (should return no exts if spl_token) + // 1) Fetch mint + let mint_account = ctx + .borrow_mut() + .banks_client + .get_account(*mint_pk) + .await + .unwrap() + .unwrap(); + let mint_exts = + spl_token_2022::extension::StateWithExtensions::::unpack( + &mint_account.data, + ) + .unwrap(); + + let mint_extensions = mint_exts.get_extension_types().unwrap(); + let required_extensions = + ExtensionType::get_required_init_account_extensions(&mint_extensions); + + let space = ExtensionType::try_calculate_account_len::( + &required_extensions, ) .unwrap(); - [init_account_ix, init_token_ix] + // Init account + ixs.push(create_account( + payer_pk, + &keypair.pubkey(), + rent.minimum_balance(space), + space as u64, + token_program, + )); + + // 2) Add instructions + if required_extensions.contains(&ExtensionType::ImmutableOwner) { + ixs.push( + spl_token_2022::instruction::initialize_immutable_owner( + token_program, + &keypair.pubkey(), + ) + .unwrap(), + ) + } + + // Token Account init + ixs.push( + spl_token_2022::instruction::initialize_account( + token_program, + &keypair.pubkey(), + mint_pk, + owner_pk, + ) + .unwrap(), + ); + + ixs } pub async fn new_account(&self) -> Pubkey { @@ -190,11 +456,13 @@ impl TokenAccountFixture { let mut ctx = self.ctx.borrow_mut(); let ixs = Self::create_ixs( + &self.ctx, ctx.banks_client.get_rent().await.unwrap(), &self.token.mint, &ctx.payer.pubkey(), &ctx.payer.pubkey(), &keypair, + &self.token_program, ) .await; @@ -216,60 +484,164 @@ impl TokenAccountFixture { mint_pk: &Pubkey, owner_pk: &Pubkey, keypair: &Keypair, + token_program: &Pubkey, ) -> Self { let ctx_ref = ctx.clone(); { - let mut ctx = ctx.borrow_mut(); + let payer = ctx.borrow().payer.pubkey(); + let rent = ctx.borrow_mut().banks_client.get_rent().await.unwrap(); + let instructions = Self::create_ixs( + &ctx, + rent, + mint_pk, + &payer, + owner_pk, + keypair, + token_program, + ) + .await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - let instructions = - Self::create_ixs(rent, mint_pk, &ctx.payer.pubkey(), owner_pk, keypair).await; + // Token extensions let tx = Transaction::new_signed_with_payer( &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, keypair], - ctx.last_blockhash, + Some(&ctx.borrow().payer.pubkey()), + &[&ctx.borrow().payer, keypair], + ctx.borrow().last_blockhash, ); - ctx.banks_client.process_transaction(tx).await.unwrap(); + ctx.borrow_mut() + .banks_client + .process_transaction(tx) + .await + .unwrap(); } + let mut ctx = ctx.borrow_mut(); + let account = ctx + .banks_client + .get_account(keypair.pubkey()) + .await + .unwrap() + .unwrap(); + Self { ctx: ctx_ref.clone(), key: keypair.pubkey(), - token: get_and_deserialize(ctx_ref.clone(), keypair.pubkey()).await, + token: StateWithExtensionsOwned::::unpack(account.data) + .unwrap() + .base, + token_program: *token_program, } } pub async fn new( + ctx: Rc>, + mint_fixture: &MintFixture, + owner_pk: &Pubkey, + ) -> TokenAccountFixture { + let keypair = Keypair::new(); + let mint_pk = mint_fixture.key; + TokenAccountFixture::new_with_keypair( + ctx, + &mint_pk, + owner_pk, + &keypair, + &mint_fixture.token_program, + ) + .await + } + + pub async fn new_with_token_program( ctx: Rc>, mint_pk: &Pubkey, owner_pk: &Pubkey, + token_program: &Pubkey, ) -> TokenAccountFixture { let keypair = Keypair::new(); - TokenAccountFixture::new_with_keypair(ctx, mint_pk, owner_pk, &keypair).await + TokenAccountFixture::new_with_keypair(ctx, mint_pk, owner_pk, &keypair, token_program).await } pub async fn fetch( ctx: Rc>, address: Pubkey, ) -> TokenAccountFixture { - let token = get_and_deserialize(ctx.clone(), address).await; + let token: spl_token_2022::state::Account = + get_and_deserialize_t22(ctx.clone(), address).await; + let token_program = token.owner; Self { ctx: ctx.clone(), key: address, token, + token_program, } } pub async fn balance(&self) -> u64 { - let token_account: TokenAccount = get_and_deserialize(self.ctx.clone(), self.key).await; + let token_account: spl_token_2022::state::Account = + get_and_deserialize_t22(self.ctx.clone(), self.key).await; token_account.amount } + + pub async fn transfer( + &self, + keypair: &Keypair, + mint_fixture: &MintFixture, + to: &Pubkey, + amount: u64, + ) -> Result<()> { + let transfer_ix = if self.token_program == spl_token::ID { + spl_token::instruction::transfer( + &self.token_program, + &self.key, + to, + &keypair.pubkey(), + &[&keypair.pubkey()], + amount, + ) + .unwrap() + } else { + spl_token_2022::offchain::create_transfer_checked_instruction_with_extra_metas( + &self.token_program, + &self.key, + &self.token.mint, + to, + &keypair.pubkey(), + &[&keypair.pubkey()], + amount, + mint_fixture.mint.decimals, + |key| async move { + if let Ok(Some(account)) = + self.ctx.borrow_mut().banks_client.get_account(key).await + { + Ok(Some(account.data)) + } else { + Err("failed to fetch".to_string().into()) + } + }, + ) + .await + .unwrap() + }; + + let mut ctx = self.ctx.borrow_mut(); + + let transfer_tx = Transaction::new_signed_with_payer( + &[transfer_ix], + Some(&keypair.pubkey()), + &[keypair], + ctx.last_blockhash, + ); + + Ok(ctx + .banks_client + .process_transaction(transfer_tx) + .await + .expect("TODO")) + } } pub async fn get_and_deserialize( @@ -278,11 +650,106 @@ pub async fn get_and_deserialize( ) -> T { let mut ctx = ctx.borrow_mut(); let account = ctx.banks_client.get_account(pubkey).await.unwrap().unwrap(); + T::try_deserialize(&mut account.data.as_slice()).unwrap() } +pub async fn get_and_deserialize_t22( + ctx: Rc>, + pubkey: Pubkey, +) -> T { + let mut ctx = ctx.borrow_mut(); + let account = ctx.banks_client.get_account(pubkey).await.unwrap().unwrap(); + + StateWithExtensionsOwned::::unpack(account.data) + .unwrap() + .base +} pub async fn balance_of(ctx: Rc>, pubkey: Pubkey) -> u64 { let token_account: TokenAccount = get_and_deserialize(ctx, pubkey).await; token_account.amount } + +#[derive(Debug, Clone, PartialEq)] +pub enum SupportedExtension { + MintCloseAuthority, + InterestBearing, + PermanentDelegate, + TransferHook, + TransferFee, +} + +impl SupportedExtension { + pub fn instruction(&self, mint: &Pubkey, key: &Pubkey) -> Instruction { + match self { + SupportedExtension::MintCloseAuthority => { + spl_token_2022::instruction::initialize_mint_close_authority( + &token_2022::ID, + mint, + Some(key), + ) + .unwrap() + } + SupportedExtension::InterestBearing => { + spl_token_2022::extension::interest_bearing_mint::instruction::initialize( + &token_2022::ID, + mint, + Some(*key), + 1, + ) + .unwrap() + } + SupportedExtension::PermanentDelegate => { + spl_token_2022::instruction::initialize_permanent_delegate( + &token_2022::ID, + mint, + key, + ) + .unwrap() + } + Self::TransferHook => { + spl_token_2022::extension::transfer_hook::instruction::initialize( + &token_2022::ID, + mint, + Some(*key), + Some(TEST_HOOK_ID), + ) + .unwrap() + } + Self::TransferFee => { + spl_token_2022::extension::transfer_fee::instruction::initialize_transfer_fee_config( + &token_2022::ID, + mint, + None, + None, + 500, + u64::MAX, + ) + .unwrap() + } + } + } + + pub fn space<'a>(exts: impl Iterator) -> usize { + exts.map(|e| match e { + SupportedExtension::MintCloseAuthority => pod_get_packed_len::(), + SupportedExtension::InterestBearing => pod_get_packed_len::(), + SupportedExtension::PermanentDelegate => pod_get_packed_len::(), + SupportedExtension::TransferHook => pod_get_packed_len::(), + SupportedExtension::TransferFee => pod_get_packed_len::(), + }) + .sum() + } + + fn types<'a>(exts: impl Iterator) -> Vec { + exts.map(|e| match e { + SupportedExtension::MintCloseAuthority => ExtensionType::MintCloseAuthority, + SupportedExtension::InterestBearing => ExtensionType::InterestBearingConfig, + SupportedExtension::PermanentDelegate => ExtensionType::PermanentDelegate, + SupportedExtension::TransferHook => ExtensionType::TransferHook, + SupportedExtension::TransferFee => ExtensionType::TransferFeeConfig, + }) + .collect() + } +} diff --git a/test-utils/src/test.rs b/test-utils/src/test.rs index d04da4c72..9d080b084 100644 --- a/test-utils/src/test.rs +++ b/test-utils/src/test.rs @@ -1,25 +1,30 @@ -use crate::{marginfi_group::*, native, spl::*, utils::*}; +use super::marginfi_account::MarginfiAccountFixture; +use crate::{ + bank::BankFixture, marginfi_group::*, native, spl::*, transfer_hook::TEST_HOOK_ID, utils::*, +}; + use anchor_lang::prelude::*; use bincode::deserialize; -use solana_sdk::account::AccountSharedData; +use pyth_sdk_solana::state::SolanaPriceAccount; +use pyth_solana_receiver_sdk::price_update::VerificationLevel; +use solana_sdk::{account::AccountSharedData, entrypoint::ProgramResult}; -use super::marginfi_account::MarginfiAccountFixture; -use crate::bank::BankFixture; use fixed_macro::types::I80F48; use lazy_static::lazy_static; -use marginfi::state::marginfi_group::{BankConfigOpt, BankOperationalState}; use marginfi::{ constants::MAX_ORACLE_KEYS, state::{ - marginfi_group::{BankConfig, GroupConfig, InterestRateConfig, RiskTier}, + marginfi_group::{ + BankConfig, BankOperationalState, GroupConfig, InterestRateConfig, RiskTier, + }, price::OracleSetup, }, }; use solana_program::{hash::Hash, sysvar}; use solana_program_test::*; use solana_sdk::{account::Account, pubkey, signature::Keypair, signer::Signer}; -use std::collections::HashMap; -use std::{cell::RefCell, rc::Rc}; + +use std::{cell::RefCell, collections::HashMap, rc::Rc}; #[derive(Default, Debug, Clone)] pub struct TestSettings { @@ -29,21 +34,43 @@ pub struct TestSettings { impl TestSettings { pub fn all_banks_payer_not_admin() -> Self { + let banks = vec![ + TestBankSetting { + mint: BankMint::Usdc, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::UsdcSwb, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::Sol, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::SolSwb, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::SolEquivalent, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::PyUSD, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::T22WithFee, + ..TestBankSetting::default() + }, + TestBankSetting { + mint: BankMint::SolEqIsolated, + ..TestBankSetting::default() + }, + ]; + Self { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SOL, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SolEquivalent, - ..TestBankSetting::default() - }, - ], + banks, group_config: Some(GroupConfig { admin: None }), } } @@ -53,11 +80,11 @@ impl TestSettings { Self { banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, config: Some(*DEFAULT_USDC_TEST_SW_BANK_CONFIG), }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, config: Some(*DEFAULT_SOL_TEST_SW_BANK_CONFIG), }, ], @@ -65,40 +92,15 @@ impl TestSettings { } } - pub fn all_banks_one_isolated() -> Self { - Self { - banks: vec![ - TestBankSetting { - mint: BankMint::USDC, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SOL, - ..TestBankSetting::default() - }, - TestBankSetting { - mint: BankMint::SolEquivalent, - config: Some(BankConfig { - risk_tier: RiskTier::Isolated, - asset_weight_maint: I80F48!(0).into(), - asset_weight_init: I80F48!(0).into(), - ..*DEFAULT_SOL_EQUIVALENT_TEST_BANK_CONFIG - }), - }, - ], - group_config: Some(GroupConfig { admin: None }), - } - } - pub fn many_banks_10() -> Self { Self { banks: vec![ TestBankSetting { - mint: BankMint::USDC, + mint: BankMint::Usdc, ..TestBankSetting::default() }, TestBankSetting { - mint: BankMint::SOL, + mint: BankMint::Sol, ..TestBankSetting::default() }, TestBankSetting { @@ -147,8 +149,10 @@ pub struct TestBankSetting { #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum BankMint { - USDC, - SOL, + Usdc, + UsdcSwb, + Sol, + SolSwb, SolEquivalent, SolEquivalent1, SolEquivalent2, @@ -159,11 +163,15 @@ pub enum BankMint { SolEquivalent7, SolEquivalent8, SolEquivalent9, + UsdcT22, + T22WithFee, + PyUSD, + SolEqIsolated, } impl Default for BankMint { fn default() -> Self { - Self::USDC + Self::Usdc } } @@ -175,6 +183,8 @@ pub struct TestFixture { pub sol_mint: MintFixture, pub sol_equivalent_mint: MintFixture, pub mnde_mint: MintFixture, + pub usdc_t22_mint: MintFixture, + pub pyusd_mint: MintFixture, } pub const PYTH_USDC_FEED: Pubkey = pubkey!("PythUsdcPrice111111111111111111111111111111"); @@ -184,6 +194,30 @@ pub const SWITCHBOARD_SOL_FEED: Pubkey = pubkey!("SwchSo1Price111111111111111111 pub const PYTH_SOL_EQUIVALENT_FEED: Pubkey = pubkey!("PythSo1Equiva1entPrice111111111111111111111"); pub const PYTH_MNDE_FEED: Pubkey = pubkey!("PythMndePrice111111111111111111111111111111"); pub const FAKE_PYTH_USDC_FEED: Pubkey = pubkey!("FakePythUsdcPrice11111111111111111111111111"); +pub const PYTH_PUSH_SOL_FULLV_FEED: Pubkey = pubkey!("PythPushFu11So1Price11111111111111111111111"); +pub const PYTH_PUSH_SOL_PARTV_FEED: Pubkey = pubkey!("PythPushHa1fSo1Price11111111111111111111111"); +pub const PYTH_PUSH_FULLV_FEED_ID: [u8; 32] = [17; 32]; +pub const PYTH_PUSH_PARTV_FEED_ID: [u8; 32] = [18; 32]; +pub const PYTH_PUSH_REAL_SOL_FEED_ID: [u8; 32] = [ + 239, 13, 139, 111, 218, 44, 235, 164, 29, 161, 93, 64, 149, 209, 218, 57, 42, 13, 47, 142, 208, + 198, 199, 188, 15, 76, 250, 200, 194, 128, 181, 109, +]; +pub const INEXISTENT_PYTH_USDC_FEED: Pubkey = + pubkey!("FakePythUsdcPrice11111111111111111111111111"); +pub const PYTH_T22_WITH_FEE_FEED: Pubkey = pubkey!("PythT22WithFeePrice111111111111111111111111"); +pub const PYTH_PYUSD_FEED: Pubkey = pubkey!("PythPyusdPrice11111111111111111111111111111"); +pub const PYTH_SOL_REAL_FEED: Pubkey = pubkey!("PythSo1Rea1Price111111111111111111111111111"); +pub const PYTH_USDC_REAL_FEED: Pubkey = pubkey!("PythUsdcRea1Price11111111111111111111111111"); +pub const PYTH_PUSH_SOL_REAL_FEED: Pubkey = pubkey!("PythPushSo1Rea1Price11111111111111111111111"); + +pub fn get_oracle_id_from_feed_id(feed_id: Pubkey) -> Option { + match feed_id.to_bytes() { + PYTH_PUSH_FULLV_FEED_ID => Some(PYTH_PUSH_SOL_FULLV_FEED), + PYTH_PUSH_PARTV_FEED_ID => Some(PYTH_PUSH_SOL_PARTV_FEED), + PYTH_PUSH_REAL_SOL_FEED_ID => Some(PYTH_PUSH_SOL_REAL_FEED), + _ => None, + } +} pub fn create_oracle_key_array(pyth_oracle: Pubkey) -> [Pubkey; MAX_ORACLE_KEYS] { let mut keys = [Pubkey::default(); MAX_ORACLE_KEYS]; @@ -206,7 +240,7 @@ lazy_static! { ..Default::default() }; pub static ref DEFAULT_TEST_BANK_CONFIG: BankConfig = BankConfig { - oracle_setup: OracleSetup::PythEma, + oracle_setup: OracleSetup::PythLegacy, asset_weight_maint: I80F48!(1).into(), asset_weight_init: I80F48!(1).into(), liability_weight_init: I80F48!(1).into(), @@ -234,6 +268,27 @@ lazy_static! { oracle_keys: create_oracle_key_array(PYTH_USDC_FEED), ..*DEFAULT_TEST_BANK_CONFIG }; + pub static ref DEFAULT_PYUSD_TEST_BANK_CONFIG: BankConfig = BankConfig { + deposit_limit: native!(1_000_000_000, "PYUSD"), + borrow_limit: native!(1_000_000_000, "PYUSD"), + oracle_keys: create_oracle_key_array(PYTH_PYUSD_FEED), + ..*DEFAULT_TEST_BANK_CONFIG + }; + pub static ref DEFAULT_SOL_EQ_ISO_TEST_BANK_CONFIG: BankConfig = BankConfig { + deposit_limit: native!(1_000_000, "SOL_EQ_ISO"), + borrow_limit: native!(1_000_000, "SOL_EQ_ISO"), + oracle_keys: create_oracle_key_array(PYTH_SOL_EQUIVALENT_FEED), + risk_tier: RiskTier::Isolated, + asset_weight_maint: I80F48!(0).into(), + asset_weight_init: I80F48!(0).into(), + ..*DEFAULT_TEST_BANK_CONFIG + }; + pub static ref DEFAULT_T22_WITH_FEE_TEST_BANK_CONFIG: BankConfig = BankConfig { + deposit_limit: native!(1_000_000_000, "T22_WITH_FEE"), + borrow_limit: native!(1_000_000_000, "T22_WITH_FEE"), + oracle_keys: create_oracle_key_array(PYTH_T22_WITH_FEE_FEED), + ..*DEFAULT_TEST_BANK_CONFIG + }; pub static ref DEFAULT_SOL_TEST_BANK_CONFIG: BankConfig = BankConfig { deposit_limit: native!(1_000_000, "SOL"), borrow_limit: native!(1_000_000, "SOL"), @@ -266,59 +321,198 @@ lazy_static! { oracle_keys: create_oracle_key_array(SWITCHBOARD_SOL_FEED), ..*DEFAULT_TEST_BANK_CONFIG }; + pub static ref DEFAULT_SOL_TEST_PYTH_PUSH_FULLV_BANK_CONFIG: BankConfig = BankConfig { + oracle_setup: OracleSetup::PythPushOracle, + deposit_limit: native!(1_000_000, "SOL"), + borrow_limit: native!(1_000_000, "SOL"), + oracle_keys: create_oracle_key_array(PYTH_PUSH_FULLV_FEED_ID.into()), + ..*DEFAULT_TEST_BANK_CONFIG + }; + /// This banks orale always has an insufficient verification level. + pub static ref DEFAULT_SOL_TEST_PYTH_PUSH_PARTV_BANK_CONFIG: BankConfig = BankConfig { + oracle_setup: OracleSetup::PythPushOracle, + deposit_limit: native!(1_000_000, "SOL"), + borrow_limit: native!(1_000_000, "SOL"), + oracle_keys: create_oracle_key_array(PYTH_PUSH_PARTV_FEED_ID.into()), + ..*DEFAULT_TEST_BANK_CONFIG + }; + pub static ref DEFAULT_SOL_TEST_REAL_BANK_CONFIG: BankConfig = BankConfig { + oracle_setup: OracleSetup::PythLegacy, + deposit_limit: native!(1_000_000, "SOL"), + borrow_limit: native!(1_000_000, "SOL"), + oracle_keys: create_oracle_key_array(PYTH_SOL_REAL_FEED), + oracle_max_age: 100, + ..*DEFAULT_TEST_BANK_CONFIG + }; + pub static ref DEFAULT_USDC_TEST_REAL_BANK_CONFIG: BankConfig = BankConfig { + oracle_setup: OracleSetup::PythLegacy, + deposit_limit: native!(1_000_000_000, "USDC"), + borrow_limit: native!(1_000_000_000, "USDC"), + oracle_keys: create_oracle_key_array(PYTH_USDC_REAL_FEED), + ..*DEFAULT_TEST_BANK_CONFIG + }; + pub static ref DEFAULT_PYTH_PUSH_SOL_TEST_REAL_BANK_CONFIG: BankConfig = BankConfig { + oracle_setup: OracleSetup::PythPushOracle, + deposit_limit: native!(1_000_000, "SOL"), + borrow_limit: native!(1_000_000, "SOL"), + oracle_keys: create_oracle_key_array(PYTH_PUSH_REAL_SOL_FEED_ID.into()), + oracle_max_age: 100, + ..*DEFAULT_TEST_BANK_CONFIG + }; } pub const USDC_MINT_DECIMALS: u8 = 6; +pub const PYUSD_MINT_DECIMALS: u8 = 6; +pub const T22_WITH_FEE_MINT_DECIMALS: u8 = 6; pub const SOL_MINT_DECIMALS: u8 = 9; pub const MNDE_MINT_DECIMALS: u8 = 9; +pub fn marginfi_entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { + marginfi::entry(program_id, unsafe { core::mem::transmute(accounts) }, data) +} + +#[cfg(feature = "lip")] +pub fn lip_entry<'a, 'b, 'c, 'info>( + program_id: &'a Pubkey, + accounts: &'b [AccountInfo<'info>], + data: &'c [u8], +) -> ProgramResult { + liquidity_incentive_program::entry(program_id, unsafe { core::mem::transmute(accounts) }, data) +} + impl TestFixture { pub async fn new(test_settings: Option) -> TestFixture { - let mut program = ProgramTest::new("marginfi", marginfi::ID, processor!(marginfi::entry)); - + TestFixture::new_with_t22_extension(test_settings, &[]).await + } + pub async fn new_with_t22_extension( + test_settings: Option, + extensions: &[SupportedExtension], + ) -> TestFixture { + let mut program = ProgramTest::default(); + + let mem_map_not_copy_feature_gate = pubkey!("EenyoWx9UMXYKpR8mW5Jmfmy2fRjzUtM7NduYMY8bx33"); + program.deactivate_feature(mem_map_not_copy_feature_gate); + + program.prefer_bpf(true); + program.add_program("marginfi", marginfi::ID, None); + program.add_program("test_transfer_hook", TEST_HOOK_ID, None); #[cfg(feature = "lip")] program.add_program( "liquidity_incentive_program", liquidity_incentive_program::ID, - processor!(liquidity_incentive_program::entry), + None, ); let usdc_keypair = Keypair::new(); + let pyusd_keypair = Keypair::new(); let sol_keypair = Keypair::new(); let sol_equivalent_keypair = Keypair::new(); let mnde_keypair = Keypair::new(); + let usdc_t22_keypair = Keypair::new(); + let t22_with_fee_keypair = Keypair::new(); program.add_account( PYTH_USDC_FEED, - create_pyth_price_account(usdc_keypair.pubkey(), 1, USDC_MINT_DECIMALS.into(), None), + create_pyth_legacy_oracle_account( + usdc_keypair.pubkey(), + 1.0, + USDC_MINT_DECIMALS.into(), + None, + ), // create_pyth_price_account(usdc_keypair.pubkey(), 1.0, USDC_MINT_DECIMALS.into(), None), + ); + program.add_account( + PYTH_PYUSD_FEED, + create_pyth_legacy_oracle_account( + pyusd_keypair.pubkey(), + 1.0, + PYUSD_MINT_DECIMALS.into(), + None, + ), + ); + program.add_account( + PYTH_T22_WITH_FEE_FEED, + create_pyth_legacy_oracle_account( + t22_with_fee_keypair.pubkey(), + 0.5, + T22_WITH_FEE_MINT_DECIMALS.into(), + None, + ), ); program.add_account( PYTH_SOL_FEED, - create_pyth_price_account(sol_keypair.pubkey(), 10, SOL_MINT_DECIMALS.into(), None), + create_pyth_legacy_oracle_account( + sol_keypair.pubkey(), + 10.0, + SOL_MINT_DECIMALS.into(), + None, + ), + // create_pyth_price_account(sol_keypair.pubkey(), 10.0, SOL_MINT_DECIMALS.into(), None), ); program.add_account( PYTH_SOL_EQUIVALENT_FEED, - create_pyth_price_account( + create_pyth_legacy_oracle_account( sol_equivalent_keypair.pubkey(), - 10, + 10.0, SOL_MINT_DECIMALS.into(), None, ), ); program.add_account( PYTH_MNDE_FEED, - create_pyth_price_account(mnde_keypair.pubkey(), 10, MNDE_MINT_DECIMALS.into(), None), + create_pyth_legacy_oracle_account( + mnde_keypair.pubkey(), + 10.0, + MNDE_MINT_DECIMALS.into(), + None, + ), + // create_pyth_price_account(mnde_keypair.pubkey(), 10.0, MNDE_MINT_DECIMALS.into(), None), ); - program.add_account( SWITCHBOARD_USDC_FEED, create_switchboard_price_feed(1, USDC_MINT_DECIMALS.into()), ); - program.add_account( SWITCHBOARD_SOL_FEED, create_switchboard_price_feed(10, SOL_MINT_DECIMALS.into()), ); + program.add_account( + PYTH_PUSH_SOL_FULLV_FEED, + create_pyth_push_oracle_account( + PYTH_PUSH_FULLV_FEED_ID, + 10.0, + SOL_MINT_DECIMALS.into(), + None, + VerificationLevel::Full, + ), + ); + program.add_account( + PYTH_PUSH_SOL_PARTV_FEED, + create_pyth_push_oracle_account( + PYTH_PUSH_PARTV_FEED_ID, + 10.0, + SOL_MINT_DECIMALS.into(), + None, + VerificationLevel::Partial { num_signatures: 5 }, + ), + ); + program.add_account( + PYTH_SOL_REAL_FEED, + create_pyth_legacy_price_account_from_bytes( + include_bytes!("../data/H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG.bin").to_vec(), + ), + ); + program.add_account( + PYTH_USDC_REAL_FEED, + create_pyth_legacy_price_account_from_bytes( + include_bytes!("../data/Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD.bin").to_vec(), + ), + ); + program.add_account( + PYTH_PUSH_SOL_REAL_FEED, + create_pyth_push_oracle_account_from_bytes( + include_bytes!("../data/7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE.bin").to_vec(), + ), + ); let context = Rc::new(RefCell::new(program.start_with_context().await)); @@ -337,6 +531,7 @@ impl TestFixture { Some(USDC_MINT_DECIMALS), ) .await; + let sol_mint_f = MintFixture::new( Rc::clone(&context), Some(sol_keypair), @@ -355,6 +550,21 @@ impl TestFixture { Some(MNDE_MINT_DECIMALS), ) .await; + let usdc_t22_mint_f = MintFixture::new_token_22( + Rc::clone(&context), + Some(usdc_t22_keypair), + Some(USDC_MINT_DECIMALS), + extensions, + ) + .await; + let pyusd_mint_f = MintFixture::new_from_file(&context, "src/fixtures/pyUSD.json"); + let t22_with_fee_mint_f = MintFixture::new_token_22( + Rc::clone(&context), + Some(t22_with_fee_keypair), + Some(T22_WITH_FEE_MINT_DECIMALS), + &[SupportedExtension::TransferFee], + ) + .await; let tester_group = MarginfiGroupFixture::new( Rc::clone(&context), @@ -369,8 +579,10 @@ impl TestFixture { if let Some(test_settings) = test_settings.clone() { for bank in test_settings.banks.iter() { let (bank_mint, default_config) = match bank.mint { - BankMint::USDC => (&usdc_mint_f, *DEFAULT_USDC_TEST_BANK_CONFIG), - BankMint::SOL => (&sol_mint_f, *DEFAULT_SOL_TEST_BANK_CONFIG), + BankMint::Usdc => (&usdc_mint_f, *DEFAULT_USDC_TEST_BANK_CONFIG), + BankMint::UsdcSwb => (&usdc_mint_f, *DEFAULT_USDC_TEST_SW_BANK_CONFIG), + BankMint::Sol => (&sol_mint_f, *DEFAULT_SOL_TEST_BANK_CONFIG), + BankMint::SolSwb => (&sol_mint_f, *DEFAULT_SOL_TEST_SW_BANK_CONFIG), BankMint::SolEquivalent => ( &sol_equivalent_mint_f, *DEFAULT_SOL_EQUIVALENT_TEST_BANK_CONFIG, @@ -411,6 +623,14 @@ impl TestFixture { &sol_equivalent_mint_f, *DEFAULT_SOL_EQUIVALENT_TEST_BANK_CONFIG, ), + BankMint::T22WithFee => { + (&t22_with_fee_mint_f, *DEFAULT_T22_WITH_FEE_TEST_BANK_CONFIG) + } + BankMint::UsdcT22 => (&usdc_t22_mint_f, *DEFAULT_USDC_TEST_BANK_CONFIG), + BankMint::PyUSD => (&pyusd_mint_f, *DEFAULT_PYUSD_TEST_BANK_CONFIG), + BankMint::SolEqIsolated => { + (&sol_equivalent_mint_f, *DEFAULT_SOL_EQ_ISO_TEST_BANK_CONFIG) + } }; banks.insert( @@ -431,6 +651,8 @@ impl TestFixture { sol_mint: sol_mint_f, sol_equivalent_mint: sol_equivalent_mint_f, mnde_mint: mnde_mint_f, + usdc_t22_mint: usdc_t22_mint_f, + pyusd_mint: pyusd_mint_f, } } @@ -438,22 +660,6 @@ impl TestFixture { MarginfiAccountFixture::new(Rc::clone(&self.context), &self.marginfi_group.key).await } - pub async fn set_bank_operational_state( - &self, - bank_fixture: &BankFixture, - state: BankOperationalState, - ) -> anyhow::Result<(), BanksClientError> { - self.marginfi_group - .try_lending_pool_configure_bank( - bank_fixture, - BankConfigOpt { - operational_state: Some(state), - ..BankConfigOpt::default() - }, - ) - .await - } - pub async fn try_load( &self, address: &Pubkey, @@ -516,7 +722,8 @@ impl TestFixture { .unwrap(); let data = account.data.as_mut_slice(); - let mut data = *pyth_sdk_solana::state::load_price_account(data).unwrap(); + let mut data: SolanaPriceAccount = + *pyth_sdk_solana::state::load_price_account(data).unwrap(); data.timestamp = timestamp; data.prev_timestamp = timestamp; @@ -540,6 +747,10 @@ impl TestFixture { .unwrap(); clock.unix_timestamp += seconds; self.context.borrow_mut().set_sysvar(&clock); + self.context + .borrow_mut() + .warp_forward_force_reward_interval_end() + .unwrap(); } pub async fn get_minimum_rent_for_size(&self, size: usize) -> u64 { @@ -584,4 +795,51 @@ impl TestFixture { ) .unwrap() } + + pub async fn get_sufficient_collateral_for_outflow( + &self, + outflow_amount: f64, + outflow_mint: &BankMint, + collateral_mint: &BankMint, + ) -> f64 { + let outflow_bank = self.get_bank(outflow_mint); + let collateral_bank = self.get_bank(collateral_mint); + + let outflow_mint_price = outflow_bank.get_price().await; + let collateral_mint_price = collateral_bank.get_price().await; + + let collateral_amount = get_sufficient_collateral_for_outflow( + outflow_amount, + outflow_mint_price, + collateral_mint_price, + ); + + let decimal_scaling = 10.0_f64.powi(collateral_bank.mint.mint.decimals as i32); + let collateral_amount = + ((collateral_amount * decimal_scaling).round() + 1.) / decimal_scaling; + + get_max_deposit_amount_pre_fee(collateral_amount) + } + pub fn get_mint_fixture(&self, bank_mint: &BankMint) -> &MintFixture { + match bank_mint { + BankMint::Usdc => &self.usdc_mint, + BankMint::UsdcSwb => &self.usdc_mint, + BankMint::Sol => &self.sol_mint, + BankMint::SolSwb => &self.sol_mint, + BankMint::SolEquivalent => &self.sol_equivalent_mint, + BankMint::SolEquivalent1 => &self.sol_equivalent_mint, + BankMint::SolEquivalent2 => &self.sol_equivalent_mint, + BankMint::SolEquivalent3 => &self.sol_equivalent_mint, + BankMint::SolEquivalent4 => &self.sol_equivalent_mint, + BankMint::SolEquivalent5 => &self.sol_equivalent_mint, + BankMint::SolEquivalent6 => &self.sol_equivalent_mint, + BankMint::SolEquivalent7 => &self.sol_equivalent_mint, + BankMint::SolEquivalent8 => &self.sol_equivalent_mint, + BankMint::SolEquivalent9 => &self.sol_equivalent_mint, + BankMint::UsdcT22 => &self.usdc_t22_mint, + BankMint::T22WithFee => &self.usdc_t22_mint, + BankMint::PyUSD => &self.pyusd_mint, + BankMint::SolEqIsolated => &self.sol_equivalent_mint, + } + } } diff --git a/test-utils/src/utils.rs b/test-utils/src/utils.rs index a719c7675..e45c83139 100644 --- a/test-utils/src/utils.rs +++ b/test-utils/src/utils.rs @@ -1,16 +1,21 @@ -use anchor_lang::{prelude::*, Discriminator}; +use anchor_lang::prelude::*; +use anchor_lang_29::Discriminator; +use anchor_spl::token_2022::spl_token_2022::extension::transfer_fee::MAX_FEE_BASIS_POINTS; use marginfi::constants::PYTH_ID; use pyth_sdk_solana::state::{ - AccountType, PriceAccount, PriceInfo, PriceStatus, Rational, MAGIC, VERSION_2, + AccountType, PriceInfo, PriceStatus, Rational, SolanaPriceAccount, MAGIC, VERSION_2, }; +use pyth_solana_receiver_sdk::price_update::FeedId; +use pyth_solana_receiver_sdk::price_update::PriceUpdateV2; +use pyth_solana_receiver_sdk::price_update::VerificationLevel; use solana_program::{instruction::Instruction, pubkey}; use solana_program_test::*; use solana_sdk::{account::Account, signature::Keypair}; use std::mem::size_of; use std::{cell::RefCell, rc::Rc}; -use switchboard_v2::SWITCHBOARD_PROGRAM_ID; -use switchboard_v2::{ +use switchboard_solana::{ AggregatorAccountData, AggregatorResolutionMode, AggregatorRound, SwitchboardDecimal, + SWITCHBOARD_PROGRAM_ID, }; pub const MS_PER_SLOT: u64 = 400; @@ -47,52 +52,105 @@ where } } -pub fn create_pyth_price_account( +pub fn create_pyth_legacy_price_account_from_bytes(data: Vec) -> Account { + Account { + lamports: 1_000_000, + data, + owner: PYTH_ID, + executable: false, + rent_epoch: 361, + } +} + +pub fn create_pyth_legacy_oracle_account( mint: Pubkey, - ui_price: i64, + ui_price: f64, mint_decimals: i32, timestamp: Option, ) -> Account { - let native_price = ui_price * 10_i64.pow(mint_decimals as u32); + let native_price = (ui_price * 10_f64.powf(mint_decimals as f64)) as i64; + let data = bytemuck::bytes_of(&SolanaPriceAccount { + prod: mint, + agg: PriceInfo { + conf: 0, + price: native_price, + status: PriceStatus::Trading, + ..Default::default() + }, + expo: -mint_decimals, + prev_price: native_price, + magic: MAGIC, + ver: VERSION_2, + atype: AccountType::Price as u32, + timestamp: 0, + ema_price: Rational { + val: native_price, + numer: native_price, + denom: 1, + }, + prev_timestamp: timestamp.unwrap_or(0), + ema_conf: Rational { + val: 0, + numer: 0, + denom: 1, + }, + ..Default::default() + }) + .to_vec(); + + create_pyth_legacy_price_account_from_bytes(data) +} + +pub fn create_pyth_push_oracle_account_from_bytes(data: Vec) -> Account { Account { lamports: 1_000_000, - data: bytemuck::bytes_of(&PriceAccount { - prod: mint, - agg: PriceInfo { - conf: 0, - price: native_price, - status: PriceStatus::Trading, - ..Default::default() - }, - expo: -mint_decimals, - prev_price: native_price, - magic: MAGIC, - ver: VERSION_2, - atype: AccountType::Price as u32, - timestamp: 0, - ema_price: Rational { - val: native_price, - numer: native_price, - denom: 1, - }, - prev_timestamp: timestamp.unwrap_or(0), - ema_conf: Rational { - val: 0, - numer: 0, - denom: 1, - }, - ..Default::default() - }) - .to_vec(), - owner: PYTH_ID, + data, + owner: pyth_solana_receiver_sdk::ID, executable: false, rent_epoch: 361, } } +pub fn create_pyth_push_oracle_account( + feed_id: FeedId, + ui_price: f64, + mint_decimals: i32, + timestamp: Option, + verification_level: VerificationLevel, +) -> Account { + let native_price = (ui_price * 10_f64.powf(mint_decimals as f64)) as i64; + + let price_update = PriceUpdateV2 { + write_authority: Pubkey::default(), + verification_level, + price_message: pyth_solana_receiver_sdk::price_update::PriceFeedMessage { + feed_id, + price: native_price, + conf: 0, + exponent: -mint_decimals, + publish_time: timestamp.unwrap_or_default(), + prev_publish_time: timestamp.unwrap_or_default(), + ema_price: native_price, + ema_conf: 0, + }, + posted_slot: 1, + }; + + let mut data = vec![]; + let mut account_data = vec![]; + + data.extend_from_slice(&PriceUpdateV2::DISCRIMINATOR); + + price_update.serialize(&mut account_data).unwrap(); + + data.extend_from_slice(&account_data); + + create_pyth_push_oracle_account_from_bytes(data) +} + pub fn create_switchboard_price_feed(ui_price: i64, mint_decimals: i32) -> Account { let native_price = ui_price * 10_i64.pow(mint_decimals as u32); - let aggregator_account = switchboard_v2::AggregatorAccountData { + let aggregator_account = switchboard_solana::AggregatorAccountData { name: [0; 32], metadata: [0; 128], _reserved1: [0; 32], @@ -311,7 +369,7 @@ pub fn create_switchboard_price_feed(ui_price: i64, mint_decimals: i32) -> Accou ], }, job_pubkeys_data: [Pubkey::default(); 16], - job_hashes: [switchboard_v2::Hash::default(); 16], + job_hashes: [switchboard_solana::Hash::default(); 16], job_pubkeys_size: 5, jobs_checksum: [ 119, 207, 222, 177, 160, 127, 254, 198, 132, 153, 111, 54, 202, 89, 87, 81, 75, 152, @@ -332,12 +390,20 @@ pub fn create_switchboard_price_feed(ui_price: i64, mint_decimals: i32) -> Accou 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, 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, 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, ], + ..Default::default() // base_priority_fee: todo!(), + // priority_fee_bump: todo!(), + // priority_fee_bump_period: todo!(), + // max_priority_fee_multiplier: todo!(), + // parent_function: todo!(), }; - let desc_bytes = ::DISCRIMINATOR; + let desc_bytes = + ::DISCRIMINATOR; let mut data = vec![0u8; 8 + size_of::()]; data[..8].copy_from_slice(&desc_bytes); data[8..].copy_from_slice(bytemuck::bytes_of(&aggregator_account)); @@ -442,6 +508,21 @@ macro_rules! native { (($val) * 10_u64.pow(6) as f64) as u64 }; + ($val: expr, "PYUSD") => { + $val * 10_u64.pow(6) + }; + + ($val: expr, "PYUSD", f64) => { + (($val) * 10_u64.pow(6) as f64) as u64 + }; + ($val: expr, "T22_WITH_FEE") => { + $val * 10_u64.pow(6) + }; + + ($val: expr, "T22_WITH_FEE", f64) => { + (($val) * 10_u64.pow(6) as f64) as u64 + }; + ($val: expr, "SOL") => { $val * 10_u64.pow(9) }; @@ -466,6 +547,14 @@ macro_rules! native { (($val) * 10_u64.pow(9) as f64) as u64 }; + ($val: expr, "SOL_EQ_ISO") => { + $val * 10_u64.pow(9) + }; + + ($val: expr, "SOL_EQ_ISO", f64) => { + (($val) * 10_u64.pow(9) as f64) as u64 + }; + ($val: expr, $decimals: expr) => { $val * 10_u64.pow($decimals as u32) }; @@ -546,6 +635,18 @@ pub fn get_emissions_token_account_address( ) } +pub fn get_max_deposit_amount_pre_fee(amount: f64) -> f64 { + amount * (1f64 + MAX_FEE_BASIS_POINTS as f64 / 10_000f64) +} + +pub fn get_sufficient_collateral_for_outflow( + target_outflow: f64, + collateral_mint_price: f64, + outflow_mint_price: f64, +) -> f64 { + target_outflow * outflow_mint_price / collateral_mint_price +} + #[cfg(feature = "lip")] pub mod lip { use super::*; diff --git a/tools/llama-snapshot-tool/Cargo.toml b/tools/llama-snapshot-tool/Cargo.toml index 87aeb1139..ffd339153 100644 --- a/tools/llama-snapshot-tool/Cargo.toml +++ b/tools/llama-snapshot-tool/Cargo.toml @@ -13,9 +13,9 @@ path = "src/bin/main.rs" solana-sdk = { workspace = true } solana-client = { workspace = true } solana-account-decoder = { workspace = true } -spl-token = { workspace = true } anchor-client = { workspace = true } +anchor-spl = { workspace = true } marginfi = { path = "../../programs/marginfi", version = "0.1.0", features = [ "mainnet-beta", @@ -31,10 +31,11 @@ serde_json = "1.0.81" fixed = "1.12.0" fixed-macro = "1.2.0" bytemuck = "1.12.3" -reqwest = { version = "0.11", features = ["json"] } # reqwest with JSON parsing support +reqwest = { version = "0.11", features = [ + "json", +] } # reqwest with JSON parsing support futures = "0.3" # for our async / await blocks tokio = { version = "1.12.0", features = ["full"] } # for our async runtime anyhow = "1.0" lazy_static = "1.4" rust-s3 = "0.33.0" - diff --git a/tools/llama-snapshot-tool/src/bin/main.rs b/tools/llama-snapshot-tool/src/bin/main.rs index 0cf46988c..7894d7f1e 100644 --- a/tools/llama-snapshot-tool/src/bin/main.rs +++ b/tools/llama-snapshot-tool/src/bin/main.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, env, rc::Rc}; use anchor_client::Client; +use anchor_spl::token::spl_token; use anyhow::Result; use fixed::types::I80F48; use futures::future::join_all; @@ -162,7 +163,7 @@ impl DefiLammaPoolInfo { let (apr_reward, apr_reward_borrow) = if bank.emissions_mint.ne(&Pubkey::default()) { let emissions_token_price = fetch_price_from_birdeye(&bank.emissions_mint).await?; let mint = rpc_client.get_account(&bank.emissions_mint)?; - let mint = spl_token::state::Mint::unpack_from_slice(&mint.data)?; + let mint = spl_token::state::Mint::unpack(&mint.data[..spl_token::state::Mint::LEN])?; // rate / 10 ^ decimals let reward_rate_per_token =