diff --git a/.env.example b/.env.example index 41f1616b3..f6f9f5ab2 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1 @@ -WEBB_EVM_RINKEBY_ENABLED=true -WEBB_EVM_HARMONYMAINNET0_ENABLED=true - -RINKEBY_PRIVATE_KEY=<0X_PREFIXED_PRIVATE_KEY> -HARMONYMAINNET0_PRIVATE_KEY=<0X_PREFIXED_PRIVATE_KEY> - -#add an optional beneficiary to receive rewards for relaying -WEBB_EVM_RINKEBY_BENEFICIARY=<0X_PREFIXED_ADDRESS> -WEBB_EVM_HARMONYMAINNET0_BENEFICIARY=<0X_PREFIXED_ADDRESS> - -ETH1_INFURA_API_KEY= \ No newline at end of file +ETH1_INFURA_API_KEY= diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..e897dd2e2 --- /dev/null +++ b/.envrc @@ -0,0 +1,5 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" +fi +use flake +# vi: ft=sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..4af5966e1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @shekohex @salman01zp diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c9b592c9f..4f23c9e57 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,10 +1,6 @@ name: Tests on: - push: - branches: - - main - - develop pull_request: branches: - develop @@ -16,82 +12,77 @@ env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} jobs: - - macos-build: + macos-check: + name: macOS Check runs-on: macos-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-macos-latest-${{ matrix.target }} + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - uses: actions/checkout@v3 with: fetch-depth: 50 - + - name: Setup | Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable targets: ${{ matrix.target }} - - name: Setup DVC - uses: iterative/setup-dvc@v1 - - name: Rust Cache uses: Swatinem/rust-cache@v2 - - name: Build target - run: cargo build --release --target=${{ matrix.target }} --features native-tls/vendored --locked + with: + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" + + - name: Build Checking on ${{ matrix.target }} + run: cargo check --target=${{ matrix.target }} --features native-tls/vendored --locked strategy: fail-fast: true matrix: - channel: [stable] target: - x86_64-apple-darwin macos-unit-tests: + name: macOS Unit Tests runs-on: macos-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-macos-unit-tests-${{ matrix.target }} + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - uses: actions/checkout@v3 with: fetch-depth: 50 - + - name: Setup | Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable targets: ${{ matrix.target }} - - name: Setup DVC - uses: iterative/setup-dvc@v1 - - name: Rust Cache uses: Swatinem/rust-cache@v2 - - name: Build target - run: cargo test --release --target=${{ matrix.target }} --locked - + with: + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" + + - name: Testing on ${{ matrix.target }} + run: cargo test --target=${{ matrix.target }} --locked + strategy: fail-fast: true matrix: - channel: [stable] target: - x86_64-apple-darwin - - linux-build: + linux-check: + name: Linux Check runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-linux-check-${{ matrix.target }} + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} @@ -99,9 +90,6 @@ jobs: with: fetch-depth: 50 - - name: Setup DVC - uses: iterative/setup-dvc@v1 - - name: Setup | Rust uses: dtolnay/rust-toolchain@stable with: @@ -110,25 +98,24 @@ jobs: - name: Install cross run: cargo install cross --locked - - name: Build target using cross - run: cross build --release --target=${{ matrix.target }} --features native-tls/vendored --locked + + - name: Build Checking on ${{ matrix.target }} using cross + run: cross check --target=${{ matrix.target }} --features native-tls/vendored --locked strategy: fail-fast: true matrix: - channel: [stable] target: - aarch64-unknown-linux-musl - x86_64-unknown-linux-musl linux-unit-tests: + name: Linux Unit Tests runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-linux-unit-tests-${{ matrix.target }} + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} @@ -136,53 +123,39 @@ jobs: with: fetch-depth: 50 - - name: Setup DVC - uses: iterative/setup-dvc@v1 - - name: Setup | Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable targets: ${{ matrix.target }} - - name: Use Node.js - uses: actions/setup-node@v3 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - node-version-file: "./tests/.nvmrc" - cache: "yarn" - cache-dependency-path: "./tests/yarn.lock" - registry-url: "https://npm.pkg.github.com" + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" - - run: yarn global add @webb-tools/dkg-standalone-binary@latest - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Env file run: | - touch .env - echo ETH1_INFURA_API_KEY = ${{ secrets.ETH1_INFURA_API_KEY }} >> .env - - name: Install cross - run: cargo install cross --locked - - name: Build target using cross - run: cross test --release --target=${{ matrix.target }} --features native-tls/vendored --locked + touch .env + echo ETH1_INFURA_API_KEY = ${{ secrets.ETH1_INFURA_API_KEY }} >> .env + + - name: Testing on ${{ matrix.target }} + run: cargo test --target=${{ matrix.target }} --features native-tls/vendored --locked strategy: fail-fast: true matrix: - channel: [stable] target: - - aarch64-unknown-linux-musl - - x86_64-unknown-linux-musl - + - x86_64-unknown-linux-gnu linux-integration-tests: + name: Linux Integration Tests runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-linux-integration-tests-${{ matrix.target }}-${{ matrix.test-type }} + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} @@ -195,6 +168,9 @@ jobs: - name: Rust Cache uses: Swatinem/rust-cache@v2 + with: + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" - name: Use Node.js uses: actions/setup-node@v3 @@ -204,26 +180,23 @@ jobs: cache-dependency-path: "./tests/yarn.lock" registry-url: "https://npm.pkg.github.com" - if: matrix.target == 'x86_64-unknown-linux-musl' - - run: cargo build --features integration-tests,cli,native-tls/vendored - if: matrix.target == 'x86_64-unknown-linux-musl' - shell: bash - - - name: Install NPM Packages. - if: matrix.target == 'x86_64-unknown-linux-musl' + - name: Install Node Packages. run: cd tests && dvc pull && yarn + - name: Build relayer + run: cargo build --features integration-tests,cli,native-tls/vendored + - name: Pull Docker Images used for testing. - run: docker pull ghcr.io/webb-tools/protocol-substrate-standalone-node:stable - + run: docker pull ghcr.io/webb-tools/tangle/tangle-standalone-integration-tests:main + - name: Webb Relayer Integration tests. - if: matrix.target == 'x86_64-unknown-linux-musl' - run: cd tests && yarn test + run: cd tests && yarn ${{ matrix.test-type }} strategy: - fail-fast: true + fail-fast: false matrix: - channel: [stable] target: - - aarch64-unknown-linux-musl - - x86_64-unknown-linux-musl + - x86_64-unknown-linux-gnu + test-type: + - test-evm + - test-substrate diff --git a/.github/workflows/image-publish.yml b/.github/workflows/image-publish.yml index 5cfe6eccd..bf25c1f06 100644 --- a/.github/workflows/image-publish.yml +++ b/.github/workflows/image-publish.yml @@ -2,9 +2,9 @@ name: Container Image Release on: push: - # Publish `main` as Container `edge` image. + # Publish `develop` as Container `edge` image. branches: - - main + - develop # Publish `v1.2.3` tags as releases. tags: - v* @@ -58,16 +58,12 @@ jobs: - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} - uses: actions/checkout@v2 - - name: Cache Cargo - uses: actions/cache@v2 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/.cargo/bin - target/x86_64-unknown-linux-musl - target/release - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-x86_64-unknown-linux-musl + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" + - uses: actions/download-artifact@v1 with: name: cross-linux-musl @@ -97,7 +93,7 @@ jobs: [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') # Use Docker `edge` tag convention - [ "$VERSION" == "main" ] && VERSION=edge + [ "$VERSION" == "develop" ] && VERSION=edge echo IMAGE_ID=$IMAGE_ID echo VERSION=$VERSION diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml index 61f23ba64..e1602fb08 100644 --- a/.github/workflows/lints.yml +++ b/.github/workflows/lints.yml @@ -1,8 +1,4 @@ on: - push: - branches: - - main - - develop pull_request: branches: - develop @@ -14,45 +10,40 @@ env: jobs: clippy: - name: Rust Clippy + name: Clippy + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-clippy + cancel-in-progress: true runs-on: ubuntu-latest steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} + - name: Checkout sources uses: actions/checkout@v2 - - name: Cache Cargo - uses: actions/cache@v2 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/.cargo/bin - target - target/debug - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" + - name: Install stable toolchain with clippy available uses: dtolnay/rust-toolchain@stable with: toolchain: stable components: clippy - name: Run cargo clippy - run: cargo clippy --workspace --all-targets -- -D warnings -D deprecated -D clippy::perf -D clippy::complexity -D clippy::style -D clippy::correctness -D clippy::suspicious + run: ./ci/clippy.sh continue-on-error: false rustfmt: - name: Rust Format + name: Format runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-rustfmt + cancel-in-progress: true steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - - name: Checkout sources uses: actions/checkout@v2 diff --git a/.github/workflows/publish-rust-docs.yml b/.github/workflows/publish-rust-docs.yml index 1e3806f5c..37f06974f 100644 --- a/.github/workflows/publish-rust-docs.yml +++ b/.github/workflows/publish-rust-docs.yml @@ -4,10 +4,6 @@ on: push: branches: - main - - develop - pull_request: - branches: - - develop jobs: publish_docs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 540459b56..2f1692b9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,15 +53,11 @@ jobs: with: access_token: ${{ github.token }} - uses: actions/checkout@v2 - - name: Cache Cargo - uses: actions/cache@v2 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/.cargo/bin - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.target }} + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" - uses: actions/download-artifact@v1 with: name: cross-apple-darwin @@ -107,15 +103,11 @@ jobs: - name: install system build dependencies run: sudo apt-get update && sudo apt-get install ${DEV_PACKAGES} - uses: actions/checkout@v2 - - name: Cache Cargo - uses: actions/cache@v2 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/.cargo/bin - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.target }} + shared-key: "rust" + cache-directories: ".dvc/tmp\n.dvc/cache" - uses: actions/download-artifact@v1 with: name: cross-linux-musl diff --git a/.gitignore b/.gitignore index a139a2ba4..46546d017 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ store/ .DS_Store .vscode/ .idea/ +.env.direnv +.direnv +.env diff --git a/Cargo.lock b/Cargo.lock index 433a978ec..8d685e4be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,18 +42,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if 1.0.0", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - [[package]] name = "aes" version = "0.8.2" @@ -61,7 +49,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ "cfg-if 1.0.0", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -90,9 +78,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -100,7 +88,7 @@ dependencies = [ [[package]] name = "amcl" version = "0.3.0" -source = "git+https://github.com/Snowfork/milagro_bls#2c9e8b383981308a8b4cbbc19e0b897dcf14534b" +source = "git+https://github.com/Snowfork/milagro_bls#bc2b5b5e8d48b7e2e1bfaa56dc2d93e13cb32095" [[package]] name = "android_system_properties" @@ -122,9 +110,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "ark-bls12-381" @@ -148,6 +136,33 @@ dependencies = [ "ark-std", ] +[[package]] +name = "ark-circom" +version = "0.1.0" +source = "git+https://github.com/vacp2p/ark-circom?branch=wasm#0e587145cb05e08b2d1a01509eb578670088eb2f" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-groth16 0.3.0 (git+https://github.com/arkworks-rs/groth16?rev=765817f)", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", + "byteorder", + "cfg-if 1.0.0", + "color-eyre 0.5.11", + "criterion", + "ethers-core 2.0.4", + "fnv", + "hex", + "num", + "num-bigint", + "num-traits", + "thiserror", + "wasmer", +] + [[package]] name = "ark-crypto-primitives" version = "0.3.0" @@ -165,6 +180,7 @@ dependencies = [ "blake2 0.9.2", "derivative", "digest 0.9.0", + "rayon", "tracing", ] @@ -179,6 +195,7 @@ dependencies = [ "ark-std", "derivative", "num-traits", + "rayon", "zeroize", ] @@ -196,6 +213,7 @@ dependencies = [ "num-bigint", "num-traits", "paste", + "rayon", "rustc_version 0.3.3", "zeroize", ] @@ -207,7 +225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -219,7 +237,7 @@ dependencies = [ "num-bigint", "num-traits", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -237,6 +255,21 @@ dependencies = [ "ark-std", ] +[[package]] +name = "ark-groth16" +version = "0.3.0" +source = "git+https://github.com/arkworks-rs/groth16?rev=765817f#765817f77a6e14964c6f264d565b18676b11bd59" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", + "rayon", +] + [[package]] name = "ark-nonnative-field" version = "0.3.0" @@ -266,6 +299,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.11.2", + "rayon", ] [[package]] @@ -293,6 +327,7 @@ dependencies = [ "ark-ff", "ark-std", "tracing", + "tracing-subscriber 0.2.25", ] [[package]] @@ -314,7 +349,7 @@ checksum = "8dd4e5f0bf8285d5ed538d27fab7411f3e297908fd93c62195de8bee3f199e82" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -336,6 +371,7 @@ checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", "rand 0.8.5", + "rayon", ] [[package]] @@ -386,7 +422,7 @@ dependencies = [ "ark-crypto-primitives", "ark-ec", "ark-ff", - "ark-groth16", + "ark-groth16 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ark-r1cs-std", "ark-relations", "ark-serialize", @@ -418,9 +454,9 @@ checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -460,13 +496,13 @@ checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" [[package]] name = "async-trait" -version = "0.1.66" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -497,18 +533,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "auto_impl" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "auto_impl" version = "1.0.1" @@ -518,7 +542,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -529,19 +553,20 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.7" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591" +checksum = "349f8ccfd9221ee7d1f3d4b33e1f8319b3a81ed8f61f2ea40b37b859794b4491" dependencies = [ "async-trait", "axum-core", + "axum-macros", "base64 0.21.0", "bitflags", "bytes 1.4.0", "futures-util", "http", "http-body 0.4.5", - "hyper 0.14.24", + "hyper 0.14.25", "itoa 1.0.6", "matchit", "memchr", @@ -555,10 +580,9 @@ dependencies = [ "serde_urlencoded 0.7.1", "sha1", "sync_wrapper", - "tokio 1.26.0", + "tokio 1.28.1", "tokio-tungstenite 0.18.0", "tower", - "tower-http", "tower-layer", "tower-service", ] @@ -591,6 +615,18 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-macros" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb524613be645939e280b7279f7b017f98cf7f5ef084ec374df373530e73277" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.13", +] + [[package]] name = "backoff" version = "0.4.0" @@ -602,7 +638,7 @@ dependencies = [ "instant", "pin-project-lite 0.2.9", "rand 0.8.5", - "tokio 1.26.0", + "tokio 1.28.1", ] [[package]] @@ -627,10 +663,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] -name = "base58" -version = "0.1.0" +name = "base16ct" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base58" @@ -638,16 +674,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "base58check" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" -dependencies = [ - "base58 0.1.0", - "sha2 0.8.2", -] - [[package]] name = "base64" version = "0.12.3" @@ -777,16 +803,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -819,7 +845,7 @@ dependencies = [ [[package]] name = "bls" version = "0.2.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "eth2_hashing 0.3.0 (git+https://github.com/webb-tools/pallet-eth2-light-client)", "eth2_serde_utils 0.1.1 (git+https://github.com/webb-tools/pallet-eth2-light-client)", @@ -867,7 +893,7 @@ dependencies = [ "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] @@ -878,7 +904,7 @@ checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -889,7 +915,7 @@ checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -909,6 +935,9 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] [[package]] name = "buf_redux" @@ -920,6 +949,17 @@ dependencies = [ "safemem", ] +[[package]] +name = "build-data" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac83c47416b2db78a5a8a45d7d229a730b62806fa41ac6b4dbde6d016798776" +dependencies = [ + "chrono", + "safe-lock", + "safe-regex", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -938,6 +978,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -959,27 +1021,6 @@ dependencies = [ "serde", ] -[[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 = "cached_tree_hash" version = "0.1.0" @@ -996,9 +1037,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -1014,26 +1055,29 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "thiserror", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -1049,29 +1093,20 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.45", + "time", "wasm-bindgen", "winapi 0.3.9", ] -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.6", -] - [[package]] name = "cipher" version = "0.4.4" @@ -1124,7 +1159,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1146,24 +1181,11 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "coingecko" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b24c2d346c689c0b8a27d4c921a35d8cac68cc266130bc82ba879155ca828da0" -dependencies = [ - "chrono", - "reqwest", - "serde", - "serde_json", - "tokio 1.26.0", -] - [[package]] name = "coins-bip32" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" +checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" dependencies = [ "bincode", "bs58", @@ -1171,7 +1193,7 @@ dependencies = [ "digest 0.10.6", "getrandom 0.2.8", "hmac 0.12.1", - "k256", + "k256 0.13.1", "lazy_static", "serde", "sha2 0.10.6", @@ -1180,16 +1202,16 @@ dependencies = [ [[package]] name = "coins-bip39" -version = "0.7.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" +checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", "getrandom 0.2.8", - "hex", "hmac 0.12.1", - "pbkdf2 0.11.0", + "once_cell", + "pbkdf2 0.12.1", "rand 0.8.5", "sha2 0.10.6", "thiserror", @@ -1197,16 +1219,15 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94090a6663f224feae66ab01e41a2555a8296ee07b5f20dab8888bdefc9f617" +checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" dependencies = [ - "base58check", - "base64 0.12.3", + "base64 0.21.0", "bech32", - "blake2 0.10.6", + "bs58", "digest 0.10.6", - "generic-array 0.14.6", + "generic-array 0.14.7", "hex", "ripemd", "serde", @@ -1216,6 +1237,60 @@ dependencies = [ "thiserror", ] +[[package]] +name = "color-eyre" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" +dependencies = [ + "backtrace", + "color-spantrace 0.1.6", + "eyre", + "indenter", + "once_cell", + "owo-colors 1.3.0", + "tracing-error 0.1.2", +] + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace 0.2.0", + "eyre", + "indenter", + "once_cell", + "owo-colors 3.5.0", + "tracing-error 0.2.0", +] + +[[package]] +name = "color-spantrace" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1" +dependencies = [ + "once_cell", + "owo-colors 1.3.0", + "tracing-core", + "tracing-error 0.1.2", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors 3.5.0", + "tracing-core", + "tracing-error 0.2.0", +] + [[package]] name = "colored" version = "2.0.0" @@ -1238,7 +1313,7 @@ version = "0.2.0" source = "git+https://github.com/webb-tools/lighthouse.git?branch=develop#913b28fb46ad651d3b6ca1efbfc85b68c3d314e3" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1253,13 +1328,13 @@ dependencies = [ "pathdiff", "serde", "serde_json", - "toml", + "toml 0.5.11", ] [[package]] name = "consensus_types" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "bitvec 1.0.1", "derive_more", @@ -1276,45 +1351,12 @@ dependencies = [ "tree_hash 0.4.1 (git+https://github.com/webb-tools/pallet-eth2-light-client)", ] -[[package]] -name = "console" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "regex", - "terminal_size", - "unicode-width", - "winapi 0.3.9", -] - -[[package]] -name = "console" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.42.0", -] - [[package]] name = "const-oid" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" @@ -1342,37 +1384,50 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "corosensei" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "scopeguard", + "windows-sys 0.33.0", +] [[package]] name = "cosmwasm-crypto" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8263ce52392898aa17c2a0984b7c542df416e434f6e0cb1c1a11771054d159" +checksum = "f22add0f9b2a5416df98c1d0248a8d8eedb882c38fbf0c5052b64eebe865df6d" dependencies = [ "digest 0.10.6", "ed25519-zebra", - "k256", + "k256 0.11.6", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1895f6d7a191bb044e3c555190d1da555c2571a3af41f849f60c855580e392f" +checksum = "c2e64f710a18ef90d0a632cf27842e98ffc2d005a38a6f76c12fd0bc03bc1a2d" dependencies = [ - "syn", + "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc01051aab3bb88d5efe0b90f24a6df1ca96a873b12fc21b862b17539c84ee9" +checksum = "76fee88ff5bf7bef55bd37ac0619974701b99bf6bd4b16cf56ee8810718abd71" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", @@ -1399,49 +1454,144 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] [[package]] -name = "cranelift-entity" -version = "0.92.1" +name = "cranelift-bforest" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e39cfc857e7e539aa623e03bb6bec11f54aef3dfdef41adcfa7b594af3b54" +checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" dependencies = [ - "serde", + "cranelift-entity 0.82.3", ] [[package]] -name = "crc32fast" -version = "1.3.2" +name = "cranelift-codegen" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" dependencies = [ - "cfg-if 1.0.0", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity 0.82.3", + "gimli 0.26.2", + "log", + "regalloc", + "smallvec", + "target-lexicon", ] [[package]] -name = "crossbeam-channel" -version = "0.5.7" +name = "cranelift-codegen-meta" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", + "cranelift-codegen-shared", ] [[package]] -name = "crossbeam-deque" -version = "0.8.3" +name = "cranelift-codegen-shared" +version = "0.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", +checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" + +[[package]] +name = "cranelift-entity" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" + +[[package]] +name = "cranelift-entity" +version = "0.92.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e39cfc857e7e539aa623e03bb6bec11f54aef3dfdef41adcfa7b594af3b54" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.82.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap 2.34.0", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", "crossbeam-utils", ] @@ -1479,7 +1629,19 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +dependencies = [ + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1491,7 +1653,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -1501,7 +1663,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1511,17 +1673,38 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] +[[package]] +name = "csv" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +dependencies = [ + "csv-core", + "itoa 1.0.6", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -1552,9 +1735,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1564,9 +1747,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -1574,24 +1757,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.13", ] [[package]] name = "cxxbridge-flags" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -1606,12 +1789,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core 0.14.3", - "darling_macro 0.14.3", + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] @@ -1625,21 +1808,21 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] @@ -1650,18 +1833,18 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core 0.14.3", + "darling_core 0.14.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1674,6 +1857,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c5cb402c5c958281c7c0702edea7b780d03b86b606497ca3a10fcd3fc393ac" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1682,7 +1875,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1695,19 +1888,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn", -] - -[[package]] -name = "dialoguer" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9dd058f8b65922819fabb4a41e7d1964e56344042c26efbccd465202c23fa0c" -dependencies = [ - "console 0.14.1", - "lazy_static", - "tempfile", - "zeroize", + "syn 1.0.109", ] [[package]] @@ -1731,7 +1912,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1740,7 +1921,8 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1818,7 +2000,7 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1833,10 +2015,23 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64d2e9c561b39b2de83c0387baffecfbc62ad116c4fc8f4e5b040b8e0737d7c" +dependencies = [ + "der 0.7.2", + "digest 0.10.6", + "elliptic-curve 0.13.4", + "rfc6979 0.4.0", + "signature 2.1.0", ] [[package]] @@ -1845,7 +2040,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -1887,34 +2082,47 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.6", - "ff", - "generic-array 0.14.6", - "group", - "pkcs8", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", "subtle", "zeroize", ] [[package]] -name = "ena" -version = "0.14.1" +name = "elliptic-curve" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ - "log", + "base16ct 0.2.0", + "crypto-bigint 0.5.1", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.1", + "subtle", + "zeroize", ] [[package]] -name = "encode_unicode" -version = "0.3.6" +name = "ena" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +dependencies = [ + "log", +] [[package]] name = "encoding_rs" @@ -1927,15 +2135,14 @@ dependencies = [ [[package]] name = "enr" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" +checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" dependencies = [ "base64 0.13.1", - "bs58", "bytes 1.4.0", "hex", - "k256", + "k256 0.13.1", "log", "rand 0.8.5", "rlp", @@ -1944,6 +2151,47 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -1965,13 +2213,13 @@ checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" dependencies = [ "errno-dragonfly", "libc", - "winapi 0.3.9", + "windows-sys 0.45.0", ] [[package]] @@ -1990,7 +2238,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes 0.8.2", + "aes", "ctr", "digest 0.10.6", "hex", @@ -2009,7 +2257,7 @@ dependencies = [ [[package]] name = "eth-types" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "derive_more", "eth2_serde_utils 0.1.1 (git+https://github.com/webb-tools/pallet-eth2-light-client)", @@ -2029,7 +2277,7 @@ dependencies = [ [[package]] name = "eth2-pallet-init" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "async-trait", "clap 3.2.23", @@ -2047,13 +2295,13 @@ dependencies = [ "sp-keyring", "sp-runtime", "subxt", - "tokio 1.26.0", - "toml", + "tokio 1.28.1", + "toml 0.5.11", "tree_hash 0.4.1 (git+https://github.com/webb-tools/pallet-eth2-light-client)", - "webb", + "webb 0.5.21", "webb-proposals", - "webb-relayer-types 0.1.0 (git+https://github.com/webb-tools/relayer.git)", - "webb-relayer-utils 0.1.0 (git+https://github.com/webb-tools/relayer.git)", + "webb-relayer-types 0.1.0", + "webb-relayer-utils 0.1.0", ] [[package]] @@ -2070,7 +2318,7 @@ dependencies = [ [[package]] name = "eth2_hashing" version = "0.3.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "lazy_static", "ring", @@ -2107,7 +2355,7 @@ dependencies = [ [[package]] name = "eth2_serde_utils" version = "0.1.1" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "ethereum-types 0.14.1", "hex", @@ -2128,7 +2376,7 @@ dependencies = [ [[package]] name = "eth2_ssz" version = "0.4.1" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "ethereum-types 0.14.1", "itertools", @@ -2143,7 +2391,7 @@ dependencies = [ "darling 0.13.4", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2164,7 +2412,7 @@ dependencies = [ [[package]] name = "eth2_to_substrate_relay" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "async-std", "async-trait", @@ -2192,20 +2440,20 @@ dependencies = [ "sp-core", "sp-keyring", "thread", - "tokio 1.26.0", - "toml", + "tokio 1.28.1", + "toml 0.5.11", "tree_hash 0.4.1 (git+https://github.com/webb-tools/pallet-eth2-light-client)", "types", "warp", - "webb", + "webb 0.5.21", "webb-proposals", - "webb-relayer-utils 0.1.0 (git+https://github.com/webb-tools/relayer.git)", + "webb-relayer-utils 0.1.0", ] [[package]] name = "eth_rpc_client" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "async-std", "atomic_refcell", @@ -2230,8 +2478,8 @@ dependencies = [ "serde", "serde_json", "thread", - "tokio 1.26.0", - "toml", + "tokio 1.28.1", + "toml 0.5.11", "tree_hash 0.4.1 (git+https://github.com/webb-tools/lighthouse.git?branch=develop)", "types", "warp", @@ -2314,42 +2562,41 @@ dependencies = [ [[package]] name = "ethers" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f26f9d8d80da18ca72aca51804c65eb2153093af3bec74fd5ce32aa0c1f665" +checksum = "697aba1bec98cb86e7bebd69f9bb365218871464137af9e93e7a72bd6dc421d0" dependencies = [ - "ethers-addressbook 1.0.2", - "ethers-contract 1.0.2", - "ethers-core 1.0.2", - "ethers-etherscan 1.0.2", - "ethers-middleware 1.0.2", - "ethers-providers 1.0.2", - "ethers-signers 1.0.2", + "ethers-addressbook 2.0.2", + "ethers-contract 2.0.2", + "ethers-core 2.0.2", + "ethers-etherscan 2.0.2", + "ethers-middleware 2.0.2", + "ethers-providers 2.0.2", + "ethers-signers 2.0.2", "ethers-solc", ] [[package]] name = "ethers" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839a392641e746a1ff365ef7c901238410b5c6285d240cf2409ffaaa7df9a78a" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ - "ethers-addressbook 2.0.0", - "ethers-contract 2.0.0", - "ethers-core 2.0.0", - "ethers-etherscan 2.0.0", - "ethers-middleware 2.0.0", - "ethers-providers 2.0.0", - "ethers-signers 2.0.0", + "ethers-addressbook 2.0.4", + "ethers-contract 2.0.4", + "ethers-core 2.0.4", + "ethers-etherscan 2.0.4", + "ethers-middleware 2.0.4", + "ethers-providers 2.0.4", + "ethers-signers 2.0.4", ] [[package]] name = "ethers-addressbook" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4be54dd2260945d784e06ccdeb5ad573e8f1541838cee13a1ab885485eaa0b" +checksum = "c0b603812e5e4d63521c691cbc1f34743879e96a1ee96c6594639d7fa0cf6fbc" dependencies = [ - "ethers-core 1.0.2", + "ethers-core 2.0.2", "once_cell", "serde", "serde_json", @@ -2357,11 +2604,10 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1e010165c08a2a3fa43c0bb8bc9d596f079a021aaa2cc4e8d921df09709c95" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ - "ethers-core 2.0.0", + "ethers-core 2.0.4", "once_cell", "serde", "serde_json", @@ -2369,14 +2615,14 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c3c3e119a89f0a9a1e539e7faecea815f74ddcf7c90d0b00d1f524db2fdc9c" +checksum = "b4e8ed7c2b2a22e07b65ae0eb426c948a7448f1be15c66e4813e02c423751fc9" dependencies = [ - "ethers-contract-abigen 1.0.2", - "ethers-contract-derive 1.0.2", - "ethers-core 1.0.2", - "ethers-providers 1.0.2", + "ethers-contract-abigen 2.0.2", + "ethers-contract-derive 2.0.2", + "ethers-core 2.0.2", + "ethers-providers 2.0.2", "futures-util", "hex", "once_cell", @@ -2388,14 +2634,13 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be33fd47a06cc8f97caf614cf7cf91af9dd6dcd767511578895fa884b430c4b8" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ - "ethers-contract-abigen 2.0.0", - "ethers-contract-derive 2.0.0", - "ethers-core 2.0.0", - "ethers-providers 2.0.0", + "ethers-contract-abigen 2.0.4", + "ethers-contract-derive 2.0.4", + "ethers-core 2.0.4", + "ethers-providers 2.0.4", "futures-util", "hex", "once_cell", @@ -2407,114 +2652,110 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4e5ad46aede34901f71afdb7bb555710ed9613d88d644245c657dc371aa228" +checksum = "bf0984f4ec4e267fd27b7c9fa2f73e72c5c98491a73f777290654154d104f723" dependencies = [ "Inflector", - "cfg-if 1.0.0", "dunce", - "ethers-core 1.0.2", + "ethers-core 2.0.2", + "ethers-etherscan 2.0.2", "eyre", "getrandom 0.2.8", "hex", + "prettyplease 0.1.25", "proc-macro2", "quote", "regex", "reqwest", "serde", "serde_json", - "syn", - "toml", + "syn 1.0.109", + "tokio 1.28.1", + "toml 0.7.3", "url", "walkdir", ] [[package]] name = "ethers-contract-abigen" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d9f9ecb4a18c1693de954404b66e0c9df31dac055b411645e38c4efebf3dbc" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ "Inflector", - "cfg-if 1.0.0", "dunce", - "ethers-core 2.0.0", - "ethers-etherscan 2.0.0", + "ethers-core 2.0.4", "eyre", "getrandom 0.2.8", "hex", - "prettyplease", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", - "reqwest", "serde", "serde_json", - "syn", - "tokio 1.26.0", - "toml", - "url", + "syn 2.0.13", + "toml 0.7.3", "walkdir", ] [[package]] name = "ethers-contract-derive" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f192e8e4cf2b038318aae01e94e7644e0659a76219e94bcd3203df744341d61f" +checksum = "914e9211077a1b590af1ee6b8dfbd54515c808119546c95da69479908dc3d4de" dependencies = [ - "ethers-contract-abigen 1.0.2", - "ethers-core 1.0.2", + "ethers-contract-abigen 2.0.2", + "ethers-core 2.0.2", "hex", "proc-macro2", "quote", - "serde_json", - "syn", + "syn 1.0.109", ] [[package]] name = "ethers-contract-derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001b33443a67e273120923df18bab907a0744ad4b5fef681a8b0691f2ee0f3de" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ - "ethers-contract-abigen 2.0.0", - "ethers-core 2.0.0", - "eyre", + "Inflector", + "ethers-contract-abigen 2.0.4", + "ethers-core 2.0.4", "hex", "proc-macro2", "quote", "serde_json", - "syn", + "syn 2.0.13", ] [[package]] name = "ethers-core" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15" +checksum = "40bf114f1017ace0f622f1652f59c2c5e1abfe7d88891cca0c43da979b351de0" dependencies = [ "arrayvec 0.7.2", "bytes 1.4.0", "cargo_metadata", "chrono", "convert_case 0.6.0", - "elliptic-curve", + "elliptic-curve 0.13.4", "ethabi", - "generic-array 0.14.6", + "generic-array 0.14.7", + "getrandom 0.2.8", "hex", - "k256", + "k256 0.13.1", + "num_enum 0.5.11", "once_cell", "open-fastrlp", "proc-macro2", "rand 0.8.5", "rlp", - "rlp-derive", "serde", "serde_json", "strum", - "syn", + "syn 1.0.109", + "tempfile", "thiserror", "tiny-keccak", "unicode-xid", @@ -2522,32 +2763,28 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5925cba515ac18eb5c798ddf6069cc33ae00916cb08ae64194364a1b35c100b" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ "arrayvec 0.7.2", "bytes 1.4.0", "cargo_metadata", "chrono", - "convert_case 0.6.0", - "elliptic-curve", + "elliptic-curve 0.13.4", "ethabi", - "generic-array 0.14.6", + "generic-array 0.14.7", "getrandom 0.2.8", "hex", - "k256", - "num_enum", + "k256 0.13.1", + "num_enum 0.6.1", "once_cell", "open-fastrlp", - "proc-macro2", "rand 0.8.5", "rlp", - "rlp-derive", "serde", "serde_json", "strum", - "syn", + "syn 2.0.13", "tempfile", "thiserror", "tiny-keccak", @@ -2556,16 +2793,16 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9713f525348e5dde025d09b0a4217429f8074e8ff22c886263cc191e87d8216" +checksum = "8920b59cf81e357df2c8102d6a9dc81c2d68f7409543ff3b6868851ecf007807" dependencies = [ - "ethers-core 1.0.2", + "ethers-core 2.0.2", + "ethers-solc", "getrandom 0.2.8", "reqwest", - "semver 1.0.16", + "semver 1.0.17", "serde", - "serde-aux", "serde_json", "thiserror", "tracing", @@ -2573,16 +2810,14 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d769437fafd0b47ea8b95e774e343c5195c77423f0f54b48d11c0d9ed2148ad" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ - "ethers-core 2.0.0", + "ethers-core 2.0.4", "getrandom 0.2.8", "reqwest", - "semver 1.0.16", + "semver 1.0.17", "serde", - "serde-aux", "serde_json", "thiserror", "tracing", @@ -2590,17 +2825,18 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e71df7391b0a9a51208ffb5c7f2d068900e99d6b3128d3a4849d138f194778b7" +checksum = "c54b30f67c1883ed68bd38aedbdd321831382c12e1b95089c8261c79bb85e4da" dependencies = [ "async-trait", - "auto_impl 0.5.0", - "ethers-contract 1.0.2", - "ethers-core 1.0.2", - "ethers-etherscan 1.0.2", - "ethers-providers 1.0.2", - "ethers-signers 1.0.2", + "auto_impl", + "ethers-contract 2.0.2", + "ethers-core 2.0.2", + "ethers-etherscan 2.0.2", + "ethers-providers 2.0.2", + "ethers-signers 2.0.2", + "futures-channel", "futures-locks", "futures-util", "instant", @@ -2608,7 +2844,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", "tracing-futures", "url", @@ -2616,17 +2852,17 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dd311b76eab9d15209e4fd16bb419e25543709cbdf33079e8923dfa597517c" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ "async-trait", - "auto_impl 1.0.1", - "ethers-contract 2.0.0", - "ethers-core 2.0.0", - "ethers-etherscan 2.0.0", - "ethers-providers 2.0.0", - "ethers-signers 2.0.0", + "auto_impl", + "ethers-contract 2.0.4", + "ethers-core 2.0.4", + "ethers-etherscan 2.0.4", + "ethers-providers 2.0.4", + "ethers-signers 2.0.4", + "futures-channel", "futures-locks", "futures-util", "instant", @@ -2634,7 +2870,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", "tracing-futures", "url", @@ -2642,14 +2878,16 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a9e0597aa6b2fdc810ff58bc95e4eeaa2c219b3e615ed025106ecb027407d8" +checksum = "c2fa0857eaad0c1678f982a2f4cfbe33ebd51d273cc93de0182b7c693f2a84a1" dependencies = [ "async-trait", - "auto_impl 1.0.1", - "base64 0.13.1", - "ethers-core 1.0.2", + "auto_impl", + "base64 0.21.0", + "bytes 1.4.0", + "enr", + "ethers-core 2.0.2", "futures-core", "futures-timer", "futures-util", @@ -2657,36 +2895,34 @@ dependencies = [ "hashers", "hex", "http", + "instant", "once_cell", - "parking_lot 0.11.2", "pin-project 1.0.12", "reqwest", "serde", "serde_json", "thiserror", - "tokio 1.26.0", - "tokio-tungstenite 0.17.2", + "tokio 1.28.1", "tracing", "tracing-futures", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "web-sys", "ws_stream_wasm", ] [[package]] name = "ethers-providers" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7174af93619e81844d3d49887106a3721e5caecdf306e0b824bfb4316db3be" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ "async-trait", - "auto_impl 1.0.1", + "auto_impl", "base64 0.21.0", + "bytes 1.4.0", "enr", - "ethers-core 2.0.0", + "ethers-core 2.0.4", "futures-core", "futures-timer", "futures-util", @@ -2694,54 +2930,53 @@ dependencies = [ "hashers", "hex", "http", + "instant", "once_cell", - "parking_lot 0.11.2", "pin-project 1.0.12", "reqwest", "serde", "serde_json", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", "tracing-futures", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "web-sys", "ws_stream_wasm", ] [[package]] name = "ethers-signers" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f41ced186867f64773db2e55ffdd92959e094072a1d09a5e5e831d443204f98" +checksum = "5caa7cad4f444931d0ed45818e609847781582399eff0be5c089e8666475c7fb" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", + "elliptic-curve 0.13.4", "eth-keystore", - "ethers-core 1.0.2", + "ethers-core 2.0.2", "hex", "rand 0.8.5", "sha2 0.10.6", "thiserror", + "tracing", ] [[package]] name = "ethers-signers" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d45ff294473124fd5bb96be56516ace179eef0eaec5b281f68c953ddea1a8bf" +version = "2.0.4" +source = "git+https://github.com/gakonst/ethers-rs#3031efb2380483dd6bc5a6599519e58e95a8467f" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", + "elliptic-curve 0.13.4", "eth-keystore", - "ethers-core 2.0.0", + "ethers-core 2.0.4", "hex", "rand 0.8.5", "sha2 0.10.6", @@ -2751,13 +2986,13 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbe9c0a6d296c57191e5f8a613a3b5e816812c28f4a28d6178a17c21db903d77" +checksum = "139542f51f4c405d0dd7e97c34232140a14e8744d1cf121777355567187259e4" dependencies = [ "cfg-if 1.0.0", "dunce", - "ethers-core 1.0.2", + "ethers-core 2.0.2", "getrandom 0.2.8", "glob", "hex", @@ -2768,14 +3003,13 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "solang-parser", - "svm-rs", "thiserror", "tiny-keccak", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", "walkdir", "yansi", @@ -2834,10 +3068,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "finality-update-verify" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "bitvec 1.0.1", "bls 0.2.0 (git+https://github.com/webb-tools/pallet-eth2-light-client)", @@ -2877,16 +3121,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -2935,9 +3169,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if 1.0.0", "parity-scale-codec", @@ -2979,9 +3213,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -2994,9 +3228,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -3004,15 +3238,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -3022,9 +3256,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-locks" @@ -3038,38 +3272,42 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -3103,12 +3341,13 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -3142,6 +3381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", + "indexmap", "stable_deref_trait", ] @@ -3157,13 +3397,36 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -3202,11 +3465,17 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.26.0", + "tokio 1.28.1", "tokio-util 0.7.7", "tracing", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hash-db" version = "0.15.2" @@ -3322,12 +3591,24 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "hmac" version = "0.8.1" @@ -3364,7 +3645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3465,9 +3746,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes 1.4.0", "futures-channel", @@ -3481,7 +3762,7 @@ dependencies = [ "itoa 1.0.6", "pin-project-lite 0.2.9", "socket2 0.4.9", - "tokio 1.26.0", + "tokio 1.28.1", "tower-service", "tracing", "want", @@ -3494,15 +3775,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", - "hyper 0.14.24", + "hyper 0.14.25", "log", - "rustls", + "rustls 0.20.8", "rustls-native-certs", - "tokio 1.26.0", - "tokio-rustls", + "tokio 1.28.1", + "tokio-rustls 0.23.4", "webpki-roots", ] +[[package]] +name = "hyper-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +dependencies = [ + "http", + "hyper 0.14.25", + "rustls 0.21.1", + "tokio 1.28.1", + "tokio-rustls 0.24.0", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -3510,24 +3804,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes 1.4.0", - "hyper 0.14.24", + "hyper 0.14.25", "native-tls", - "tokio 1.26.0", + "tokio 1.28.1", "tokio-native-tls", ] [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi 0.3.9", + "windows", ] [[package]] @@ -3600,7 +3894,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3611,34 +3905,22 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", "serde", ] -[[package]] -name = "indicatif" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" -dependencies = [ - "console 0.15.5", - "lazy_static", - "number_prefix", - "regex", -] - [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -3657,9 +3939,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -3681,12 +3960,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3700,9 +3980,21 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix 0.37.7", + "windows-sys 0.45.0", +] [[package]] name = "itertools" @@ -3725,15 +4017,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.61" @@ -3769,8 +4052,8 @@ dependencies = [ "rustls-native-certs", "soketto", "thiserror", - "tokio 1.26.0", - "tokio-rustls", + "tokio 1.28.1", + "tokio-rustls 0.23.4", "tokio-util 0.7.7", "tracing", "webpki-roots", @@ -3789,13 +4072,13 @@ dependencies = [ "futures-channel", "futures-timer", "futures-util", - "hyper 0.14.24", + "hyper 0.14.25", "jsonrpsee-types", "rustc-hash", "serde", "serde_json", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", ] @@ -3806,15 +4089,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" dependencies = [ "async-trait", - "hyper 0.14.24", - "hyper-rustls", + "hyper 0.14.25", + "hyper-rustls 0.23.2", "jsonrpsee-core", "jsonrpsee-types", "rustc-hash", "serde", "serde_json", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", ] @@ -3839,10 +4122,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", - "sha3", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.3", + "elliptic-curve 0.13.4", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] @@ -3866,21 +4162,21 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" +checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" dependencies = [ "ascii-canvas", - "atty", "bit-set", "diff", "ena", + "is-terminal", "itertools", "lalrpop-util", "petgraph", "pico-args", "regex", - "regex-syntax", + "regex-syntax 0.6.29", "string_cache", "term", "tiny-keccak", @@ -3889,9 +4185,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" +checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd" dependencies = [ "regex", ] @@ -3905,11 +4201,17 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libflate" @@ -3931,6 +4233,16 @@ dependencies = [ "rle-decode-fast", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + [[package]] name = "libm" version = "0.2.6" @@ -4017,6 +4329,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.9" @@ -4037,6 +4355,27 @@ dependencies = [ "serde", ] +[[package]] +name = "loupe" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" +dependencies = [ + "indexmap", + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "mach" version = "0.3.2" @@ -4093,11 +4432,20 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +dependencies = [ + "rustix 0.37.7", +] + +[[package]] +name = "memmap2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ - "rustix", + "libc", ] [[package]] @@ -4148,7 +4496,7 @@ dependencies = [ [[package]] name = "merkle_proof" version = "0.2.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "eth2_hashing 0.3.0 (git+https://github.com/webb-tools/pallet-eth2-light-client)", "ethereum-types 0.14.1", @@ -4171,7 +4519,7 @@ dependencies = [ [[package]] name = "milagro_bls" version = "1.5.0" -source = "git+https://github.com/Snowfork/milagro_bls#2c9e8b383981308a8b4cbbc19e0b897dcf14534b" +source = "git+https://github.com/Snowfork/milagro_bls#bc2b5b5e8d48b7e2e1bfaa56dc2d93e13cb32095" dependencies = [ "amcl", "hex", @@ -4182,9 +4530,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -4254,6 +4602,12 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + [[package]] name = "multipart" version = "0.17.1" @@ -4339,6 +4693,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -4348,6 +4716,17 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", ] [[package]] @@ -4370,6 +4749,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -4407,7 +4797,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", ] [[package]] @@ -4419,14 +4818,32 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "number_prefix" -version = "0.4.0" +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "object" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "crc32fast", + "hashbrown 0.11.2", + "indexmap", + "memchr", +] [[package]] name = "object" @@ -4455,6 +4872,12 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -4474,7 +4897,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ "arrayvec 0.7.2", - "auto_impl 1.0.1", + "auto_impl", "bytes 1.4.0", "ethereum-types 0.14.1", "open-fastrlp-derive", @@ -4489,14 +4912,14 @@ dependencies = [ "bytes 1.4.0", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -4509,13 +4932,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -4526,20 +4949,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.25.1+1.1.1t" +version = "111.25.2+1.1.1t" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef9a9cc6ea7d9d5e7c4a913dc4b48d0e359eddf01af1dfec96ba7064b4aba10" +checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -4549,9 +4971,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "overload" @@ -4559,6 +4981,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parity-scale-codec" version = "3.4.0" @@ -4583,7 +5017,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4622,7 +5056,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi 0.3.9", ] @@ -4635,22 +5069,11 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "paste" version = "1.0.12" @@ -4687,7 +5110,7 @@ checksum = "0f35583365be5d148e959284f42526841917b7bfa09e2d1a7ad5dde2cf0eaa39" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4710,11 +5133,18 @@ name = "pbkdf2" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "pbkdf2" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" dependencies = [ "digest 0.10.6", "hmac 0.12.1", - "password-hash", - "sha2 0.10.6", ] [[package]] @@ -4725,9 +5155,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.6" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" dependencies = [ "thiserror", "ucd-trie", @@ -4755,37 +5185,35 @@ dependencies = [ [[package]] name = "phf" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ "phf_macros", - "phf_shared", - "proc-macro-hack", + "phf_shared 0.11.1", ] [[package]] name = "phf_generator" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ - "phf_shared", + "phf_shared 0.11.1", "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" dependencies = [ "phf_generator", - "phf_shared", - "proc-macro-hack", + "phf_shared 0.11.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4797,6 +5225,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.4.2" @@ -4829,7 +5266,7 @@ checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4840,7 +5277,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4867,16 +5304,54 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", ] [[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.2", + "spki 0.7.1", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4891,12 +5366,22 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.1.24" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebcd279d20a4a0a2404a33056388e950504d891c855c7975b9a8fef75f3bf04" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.13", ] [[package]] @@ -4932,7 +5417,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -4954,7 +5439,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -4969,17 +5454,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -5044,6 +5523,26 @@ dependencies = [ "cc", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -5052,9 +5551,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -5182,6 +5681,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -5189,39 +5697,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9af2cf09ef80e610097515e80095b7f76660a92743c4185aff5406cd5ce3dd5" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c501201393982e275433bc55de7d6ae6f00e7699cd5572c5b57581cd69c881b" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", +] + +[[package]] +name = "regalloc" +version = "0.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +dependencies = [ + "log", + "rustc-hash", + "smallvec", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -5230,20 +5749,47 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi 0.3.9", +] + +[[package]] +name = "rend" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64 0.21.0", "bytes 1.4.0", @@ -5253,8 +5799,8 @@ dependencies = [ "h2 0.3.16", "http", "http-body 0.4.5", - "hyper 0.14.24", - "hyper-rustls", + "hyper 0.14.25", + "hyper-rustls 0.24.0", "hyper-tls", "ipnet", "js-sys", @@ -5264,14 +5810,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite 0.2.9", - "rustls", + "rustls 0.21.1", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded 0.7.1", - "tokio 1.26.0", + "tokio 1.28.1", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.0", "tower-service", "url", "wasm-bindgen", @@ -5287,11 +5833,21 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -5316,6 +5872,31 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "rkyv" +version = "0.7.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -5329,6 +5910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes 1.4.0", + "rlp-derive", "rustc-hex", ] @@ -5340,7 +5922,7 @@ checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5360,9 +5942,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -5391,20 +5973,34 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", +] + +[[package]] +name = "rustix" +version = "0.36.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0af200a3324fa5bcd922e84e9b55a298ea9f431a489f01961acdebc6e908f25" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", ] [[package]] name = "rustix" -version = "0.36.9" +version = "0.37.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.1", "windows-sys 0.45.0", ] @@ -5420,6 +6016,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + [[package]] name = "rustls-native-certs" version = "0.6.2" @@ -5441,6 +6049,16 @@ dependencies = [ "base64 0.21.0", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -5453,6 +6071,59 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "safe-lock" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077d73db7973cccf63eb4aff1e5a34dc2459baa867512088269ea5f2f4253c90" + +[[package]] +name = "safe-proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "814c536dcd27acf03296c618dab7ad62d28e70abd7ba41d3f34a2ce707a2c666" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "safe-quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e530f7831f3feafcd5f1aae406ac205dd998436b4007c8e80f03eca78a88f7" +dependencies = [ + "safe-proc-macro2", +] + +[[package]] +name = "safe-regex" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15289bf322e0673d52756a18194167f2378ec1a15fe884af6e2d2cb934822b0" +dependencies = [ + "safe-regex-macro", +] + +[[package]] +name = "safe-regex-compiler" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba76fae590a2aa665279deb1f57b5098cbace01a0c5e60e262fcf55f7c51542" +dependencies = [ + "safe-proc-macro2", + "safe-quote", +] + +[[package]] +name = "safe-regex-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c2e96b5c03f158d1b16ba79af515137795f4ad4e8de3f790518aae91f1d127" +dependencies = [ + "safe-proc-macro2", + "safe-regex-compiler", +] + [[package]] name = "safe_arith" version = "0.1.0" @@ -5461,7 +6132,7 @@ source = "git+https://github.com/webb-tools/lighthouse.git?branch=develop#913b28 [[package]] name = "safe_arith" version = "0.1.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" [[package]] name = "safemem" @@ -5475,7 +6146,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -5512,9 +6183,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" dependencies = [ "bitvec 1.0.1", "cfg-if 1.0.0", @@ -5526,14 +6197,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5583,7 +6254,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -5655,16 +6326,36 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", - "generic-array 0.14.6", - "pkcs8", + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.2", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -5730,9 +6421,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] @@ -5746,6 +6437,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -5754,41 +6451,50 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.154" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] -name = "serde-aux" -version = "4.1.2" +name = "serde-json-wasm" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", - "serde_json", ] [[package]] -name = "serde-json-wasm" -version = "0.5.0" +name = "serde_bytes" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ + "half", "serde", ] [[package]] name = "serde_derive" -version = "1.0.154" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -5799,14 +6505,14 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa 1.0.6", "ryu", @@ -5815,9 +6521,18 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.10" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" dependencies = [ "serde", ] @@ -5865,7 +6580,7 @@ dependencies = [ "darling 0.13.4", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5893,17 +6608,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", -] - [[package]] name = "sha1" version = "0.10.5" @@ -5989,6 +6693,22 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[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.10" @@ -6065,14 +6785,14 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1 0.9.8", + "sha-1", ] [[package]] name = "solang-parser" -version = "0.1.18" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8ac4bfef383f368bd9bb045107a501cd9cd0b64ad1983e1b7e839d6a44ecad" +checksum = "ff87dae6cdccacdbf3b19e99b271083556e808de0f59c74a01482f64fdbc61fc" dependencies = [ "itertools", "lalrpop", @@ -6117,7 +6837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c96dc3debbe5c22ebf18f99e6a53199efe748e6e584a1902adb88cbad66ae7c" dependencies = [ "array-bytes", - "base58 0.2.0", + "base58", "bitflags", "blake2 0.10.6", "bounded-collections", @@ -6177,7 +6897,7 @@ checksum = "66fb9dc63d54de7d7bed62a505b6e0bd66c122525ea1abb348f6564717c3df2d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -6310,7 +7030,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -6435,17 +7155,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", ] [[package]] -name = "ss58-registry" -version = "1.39.0" +name = "spki" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" dependencies = [ - "Inflector", - "num-format", + "base64ct", + "der 0.7.2", +] + +[[package]] +name = "ss58-registry" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +dependencies = [ + "Inflector", + "num-format", "proc-macro2", "quote", "serde", @@ -6474,7 +7204,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot 0.12.1", - "phf_shared", + "phf_shared 0.10.0", "precomputed-hash", ] @@ -6512,7 +7242,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -6534,7 +7264,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -6562,7 +7292,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54639dba6a113584083968b6a8f457dedae612abe1bd214762101ca29f12e332" dependencies = [ - "base58 0.2.0", + "base58", "blake2 0.10.6", "derivative", "frame-metadata", @@ -6595,7 +7325,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" dependencies = [ - "darling 0.14.3", + "darling 0.14.4", "frame-metadata", "heck 0.4.1", "hex", @@ -6606,8 +7336,8 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn", - "tokio 1.26.0", + "syn 1.0.109", + "tokio 1.28.1", ] [[package]] @@ -6616,10 +7346,10 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74c08de402a78c4c06c3ee3702c80e519efdcb65911348e018b6998d04404916" dependencies = [ - "darling 0.14.3", + "darling 0.14.4", "proc-macro-error", "subxt-codegen", - "syn", + "syn 1.0.109", ] [[package]] @@ -6645,38 +7375,7 @@ dependencies = [ "proc-macro2", "quote", "smallvec", - "syn", -] - -[[package]] -name = "svm-rs" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01afefe60c02f4a2271fb15d1965c37856712cebb338330b06649d12afec42df" -dependencies = [ - "anyhow", - "cfg-if 1.0.0", - "clap 3.2.23", - "console 0.14.1", - "dialoguer", - "fs2", - "hex", - "home", - "indicatif", - "itertools", - "once_cell", - "rand 0.8.5", - "reqwest", - "semver 1.0.16", - "serde", - "serde_json", - "sha2 0.9.9", - "tempfile", - "thiserror", - "tokio 1.26.0", - "tracing", - "url", - "zip", + "syn 1.0.109", ] [[package]] @@ -6700,23 +7399,22 @@ dependencies = [ ] [[package]] -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" +name = "syn" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", - "syn", - "unicode-xid", + "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tap" version = "1.0.1" @@ -6731,15 +7429,15 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix 0.37.7", + "windows-sys 0.45.0", ] [[package]] @@ -6762,23 +7460,13 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi 0.3.9", -] - [[package]] name = "test_random_derive" version = "0.2.0" source = "git+https://github.com/webb-tools/lighthouse.git?branch=develop#913b28fb46ad651d3b6ca1efbfc85b68c3d314e3" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -6798,22 +7486,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -6852,22 +7540,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - [[package]] name = "tiny-bip39" version = "1.0.0" @@ -6896,6 +7568,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -6930,14 +7612,13 @@ dependencies = [ [[package]] name = "tokio" -version = "1.26.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes 1.4.0", "libc", - "memchr", "mio 0.8.6", "num_cpus", "parking_lot 0.12.1", @@ -6945,18 +7626,18 @@ dependencies = [ "signal-hook-registry", "socket2 0.4.9", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -6966,7 +7647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.26.0", + "tokio 1.28.1", ] [[package]] @@ -6975,11 +7656,21 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", - "tokio 1.26.0", + "rustls 0.20.8", + "tokio 1.28.1", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls 0.21.1", + "tokio 1.28.1", +] + [[package]] name = "tokio-stream" version = "0.1.12" @@ -6988,7 +7679,7 @@ checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite 0.2.9", - "tokio 1.26.0", + "tokio 1.28.1", ] [[package]] @@ -7004,22 +7695,6 @@ dependencies = [ "tungstenite 0.11.1", ] -[[package]] -name = "tokio-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" -dependencies = [ - "futures-util", - "log", - "rustls", - "tokio 1.26.0", - "tokio-rustls", - "tungstenite 0.17.3", - "webpki", - "webpki-roots", -] - [[package]] name = "tokio-tungstenite" version = "0.18.0" @@ -7028,7 +7703,7 @@ checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", - "tokio 1.26.0", + "tokio 1.28.1", "tungstenite 0.18.0", ] @@ -7057,7 +7732,7 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite 0.2.9", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", ] @@ -7070,19 +7745,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -7097,7 +7789,7 @@ dependencies = [ "futures-util", "pin-project 1.0.12", "pin-project-lite 0.2.9", - "tokio 1.26.0", + "tokio 1.28.1", "tower-layer", "tower-service", "tracing", @@ -7117,7 +7809,6 @@ dependencies = [ "http-body 0.4.5", "http-range-header", "pin-project-lite 0.2.9", - "tower", "tower-layer", "tower-service", "tracing", @@ -7156,7 +7847,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7169,6 +7860,26 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24" +dependencies = [ + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.16", +] + [[package]] name = "tracing-futures" version = "0.2.5" @@ -7264,7 +7975,7 @@ checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" dependencies = [ "lazy_static", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7280,7 +7991,7 @@ dependencies = [ [[package]] name = "tree_hash" version = "0.4.1" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "eth2_hashing 0.3.0 (git+https://github.com/webb-tools/pallet-eth2-light-client)", "ethereum-types 0.14.1", @@ -7294,17 +8005,17 @@ source = "git+https://github.com/webb-tools/lighthouse.git?branch=develop#913b28 dependencies = [ "darling 0.13.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "tree_hash_derive" version = "0.4.0" -source = "git+https://github.com/webb-tools/pallet-eth2-light-client#85a6561d61e5cbab074c266dccd9e9ed687f1321" +source = "git+https://github.com/webb-tools/pallet-eth2-light-client#e08be1f4fb6a0dd1ac1068eca7b0e3d88e16d5da" dependencies = [ "darling 0.13.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7349,30 +8060,9 @@ dependencies = [ "input_buffer", "log", "rand 0.7.3", - "sha-1 0.9.8", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes 1.4.0", - "http", - "httparse", - "log", - "rand 0.8.5", - "rustls", - "sha-1 0.10.1", - "thiserror", + "sha-1", "url", "utf-8", - "webpki", ] [[package]] @@ -7423,18 +8113,18 @@ checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "typed-builder" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6179333b981641242a768f30f371c9baccbfcc03749627000c500ab88bf4528b" +checksum = "64cba322cb9b7bc6ca048de49e83918223f35e7a86311267013afff257004870" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -7517,9 +8207,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -7620,12 +8310,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi 0.3.9", "winapi-util", ] @@ -7706,7 +8395,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -7740,7 +8429,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7752,129 +8441,360 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] -name = "wasm-timer" -version = "0.2.5" +name = "wasm-encoder" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" dependencies = [ - "futures", + "leb128", +] + +[[package]] +name = "wasmer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" +dependencies = [ + "cfg-if 1.0.0", + "indexmap", "js-sys", - "parking_lot 0.11.2", - "pin-utils", + "loupe", + "more-asserts", + "target-lexicon", + "thiserror", "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-derive", + "wasmer-engine", + "wasmer-engine-dylib", + "wasmer-engine-universal", + "wasmer-types", + "wasmer-vm", + "wat", + "winapi 0.3.9", ] [[package]] -name = "wasmi" -version = "0.13.2" +name = "wasmer-artifact" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" dependencies = [ - "parity-wasm", - "wasmi-validation", - "wasmi_core", + "enumset", + "loupe", + "thiserror", + "wasmer-compiler", + "wasmer-types", ] [[package]] -name = "wasmi-validation" -version = "0.5.0" +name = "wasmer-compiler" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" dependencies = [ - "parity-wasm", + "enumset", + "loupe", + "rkyv", + "serde", + "serde_bytes", + "smallvec", + "target-lexicon", + "thiserror", + "wasmer-types", + "wasmparser 0.83.0", ] [[package]] -name = "wasmi_core" -version = "0.2.1" +name = "wasmer-compiler-cranelift" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" dependencies = [ - "downcast-rs", - "libm", - "memory_units", - "num-rational", - "num-traits", + "cranelift-codegen", + "cranelift-entity 0.82.3", + "cranelift-frontend", + "gimli 0.26.2", + "loupe", + "more-asserts", + "rayon", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", ] [[package]] -name = "wasmparser" -version = "0.96.0" +name = "wasmer-derive" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" +checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" dependencies = [ - "indexmap", - "url", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "wasmtime" -version = "5.0.1" +name = "wasmer-engine" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ffcc607adc9da024e87ca814592d4bc67f5c5b58e488f5608d5734a1ebc23e" +checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" dependencies = [ - "anyhow", - "bincode", - "cfg-if 1.0.0", - "indexmap", - "libc", - "log", - "object 0.29.0", - "once_cell", - "paste", - "psm", + "backtrace", + "enumset", + "lazy_static", + "loupe", + "memmap2", + "more-asserts", + "rustc-demangle", "serde", + "serde_bytes", "target-lexicon", - "wasmparser", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.42.0", + "thiserror", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", ] [[package]] -name = "wasmtime-asm-macros" -version = "5.0.1" +name = "wasmer-engine-dylib" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cb5dc4d79cd7b2453c395f64e9013d2ad90bd083be556d5565cb224ebe8d57" +checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" dependencies = [ "cfg-if 1.0.0", + "enum-iterator", + "enumset", + "leb128", + "libloading", + "loupe", + "object 0.28.4", + "rkyv", + "serde", + "tempfile", + "tracing", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-engine", + "wasmer-object", + "wasmer-types", + "wasmer-vm", + "which", ] [[package]] -name = "wasmtime-environ" -version = "5.0.1" +name = "wasmer-engine-universal" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9350c919553cddf14f78f9452119c8004d7ef6bfebb79a41a21819ed0c5604d8" +checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" dependencies = [ - "anyhow", - "cranelift-entity", - "gimli 0.26.2", - "indexmap", - "log", - "object 0.29.0", - "serde", - "target-lexicon", + "cfg-if 1.0.0", + "enumset", + "leb128", + "loupe", + "region", + "rkyv", + "wasmer-compiler", + "wasmer-engine", + "wasmer-engine-universal-artifact", + "wasmer-types", + "wasmer-vm", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-engine-universal-artifact" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" +dependencies = [ + "enum-iterator", + "enumset", + "loupe", + "rkyv", "thiserror", - "wasmparser", - "wasmtime-types", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", ] [[package]] -name = "wasmtime-jit" -version = "5.0.1" +name = "wasmer-object" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ba5779ea786386432b94c9fc9ad5597346c319e8239db0d98d5be5cc109a7e" +checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" dependencies = [ - "addr2line 0.17.0", - "anyhow", - "bincode", - "cfg-if 1.0.0", - "cpp_demangle", - "gimli 0.26.2", - "log", + "object 0.28.4", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-types" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" +dependencies = [ + "backtrace", + "enum-iterator", + "indexmap", + "loupe", + "more-asserts", + "rkyv", + "serde", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" +dependencies = [ + "backtrace", + "cc", + "cfg-if 1.0.0", + "corosensei", + "enum-iterator", + "indexmap", + "lazy_static", + "libc", + "loupe", + "mach", + "memoffset 0.6.5", + "more-asserts", + "region", + "rkyv", + "scopeguard", + "serde", + "thiserror", + "wasmer-artifact", + "wasmer-types", + "winapi 0.3.9", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", +] + +[[package]] +name = "wasmparser" +version = "0.83.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" + +[[package]] +name = "wasmparser" +version = "0.96.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ffcc607adc9da024e87ca814592d4bc67f5c5b58e488f5608d5734a1ebc23e" +dependencies = [ + "anyhow", + "bincode", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser 0.96.0", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb5dc4d79cd7b2453c395f64e9013d2ad90bd083be556d5565cb224ebe8d57" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "wasmtime-environ" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9350c919553cddf14f78f9452119c8004d7ef6bfebb79a41a21819ed0c5604d8" +dependencies = [ + "anyhow", + "cranelift-entity 0.92.1", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.96.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ba5779ea786386432b94c9fc9ad5597346c319e8239db0d98d5be5cc109a7e" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli 0.26.2", + "log", "object 0.29.0", "rustc-demangle", "serde", @@ -7922,7 +8842,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix", + "rustix 0.36.12", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -7935,10 +8855,31 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86e1e4f66a2b9a114f9def450ab9971828c968db6ea6fccd613724b771fa4913" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.92.1", "serde", "thiserror", - "wasmparser", + "wasmparser 0.96.0", +] + +[[package]] +name = "wast" +version = "56.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b54185c051d7bbe23757d50fe575880a2426a2f06d2e9f6a10fd9a4a42920c0" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56681922808216ab86d96bb750f70d500b5a7800e41564290fd46bb773581299" +dependencies = [ + "wast", ] [[package]] @@ -7953,11 +8894,30 @@ dependencies = [ [[package]] name = "webb" -version = "0.5.17" -source = "git+https://github.com/webb-tools/webb-rs.git#38932538c529c0b9815a992c8299ba4acb87b3f4" +version = "0.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426649fd51964143b49326891ba1d5a10f166645f7b5a16794a0045e8361ad27" dependencies = [ "async-trait", - "ethers 2.0.0", + "ethers 2.0.2", + "hex", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "serde", + "serde_json", + "subxt", + "tempfile", + "thiserror", +] + +[[package]] +name = "webb" +version = "0.5.24" +source = "git+https://github.com/webb-tools/webb-rs#19a8d4c4bd5b24e68e2838ed43b8f26f13e91e28" +dependencies = [ + "async-trait", + "ethers 2.0.4", "hex", "parity-scale-codec", "rand 0.8.5", @@ -7971,7 +8931,7 @@ dependencies = [ [[package]] name = "webb-block-poller" -version = "0.1.0" +version = "0.5.0" dependencies = [ "anyhow", "async-trait", @@ -7982,58 +8942,117 @@ dependencies = [ "paw", "serde_json", "tempfile", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "webb 0.5.24", "webb-relayer", "webb-relayer-config", "webb-relayer-context", "webb-relayer-store", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] -name = "webb-event-watcher-traits" +name = "webb-bridge-registry-backends" +version = "0.5.0" +dependencies = [ + "async-trait", + "ethereum-types 0.14.1", + "futures", + "hex", + "hex-literal", + "native-tls", + "sp-core", + "tokio 1.28.1", + "typed-builder 0.14.0", + "webb 0.5.24", + "webb-proposals", + "webb-relayer-config", + "webb-relayer-utils 0.5.0", +] + +[[package]] +name = "webb-chains-info" +version = "0.5.0" +dependencies = [ + "anyhow", + "prettyplease 0.2.4", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.13", + "toml 0.7.3", +] + +[[package]] +name = "webb-circom-proving" version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-circom", + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-groth16 0.3.0 (git+https://github.com/arkworks-rs/groth16?rev=765817f)", + "ark-relations", + "ark-serialize", + "ark-std", + "arkworks-native-gadgets", + "cfg-if 1.0.0", + "color-eyre 0.6.2", + "num-bigint", + "num-traits", + "once_cell", + "serde", + "serde_json", + "thiserror", + "wasmer", +] + +[[package]] +name = "webb-event-watcher-traits" +version = "0.5.0" dependencies = [ "async-trait", "backoff", "futures", "native-tls", "sled", - "tokio 1.26.0", + "sp-core", + "tokio 1.28.1", "tracing", "tracing-test", - "webb", + "webb 0.5.24", "webb-proposals", "webb-relayer-config", "webb-relayer-context", "webb-relayer-store", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-ew-dkg" -version = "0.1.0" +version = "0.5.0" dependencies = [ "async-trait", "ethereum-types 0.14.1", "hex", "native-tls", "sled", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "webb 0.5.24", "webb-event-watcher-traits", "webb-proposals", "webb-relayer-config", "webb-relayer-store", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-ew-evm" -version = "0.1.0" +version = "0.5.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -8048,20 +9067,22 @@ dependencies = [ "native-tls", "serde_json", "sled", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "typed-builder 0.14.0", + "webb 0.5.24", + "webb-bridge-registry-backends", "webb-event-watcher-traits", "webb-proposal-signing-backends", "webb-proposals", "webb-relayer-config", "webb-relayer-store", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-ew-substrate" -version = "0.1.0" +version = "0.5.0" dependencies = [ "async-trait", "hex", @@ -8069,16 +9090,18 @@ dependencies = [ "native-tls", "sled", "sp-core", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "typed-builder 0.14.0", + "webb 0.5.24", + "webb-bridge-registry-backends", "webb-event-watcher-traits", "webb-proposal-signing-backends", "webb-proposals", "webb-relayer-config", "webb-relayer-store", - "webb-relayer-types 0.1.0", - "webb-relayer-utils 0.1.0", + "webb-relayer-types 0.5.0", + "webb-relayer-utils 0.5.0", ] [[package]] @@ -8102,9 +9125,9 @@ dependencies = [ "serde_json", "tempfile", "thiserror", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "webb 0.5.24", "webb-proposal-signing-backends", "webb-proposals", "webb-relayer", @@ -8115,29 +9138,73 @@ dependencies = [ "webb-relayer-store", "webb-relayer-tx-queue", "webb-relayer-tx-relay", - "webb-relayer-types 0.1.0", - "webb-relayer-utils 0.1.0", + "webb-relayer-types 0.5.0", + "webb-relayer-utils 0.5.0", ] [[package]] -name = "webb-proposal-signing-backends" +name = "webb-price-oracle-backends" +version = "0.5.0" +dependencies = [ + "async-trait", + "axum", + "chrono", + "futures", + "native-tls", + "reqwest", + "serde", + "tokio 1.28.1", + "typed-builder 0.14.0", + "webb 0.5.24", + "webb-chains-info", + "webb-relayer-store", + "webb-relayer-utils 0.5.0", +] + +[[package]] +name = "webb-proof-generation" version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-circom", + "ark-ff", + "ark-groth16 0.3.0 (git+https://github.com/arkworks-rs/groth16?rev=765817f)", + "ark-relations", + "ark-serialize", + "arkworks-setups", + "hex", + "itertools", + "num-bigint", + "serde", + "serde_json", + "wasmer", + "webb 0.5.24", + "webb-circom-proving", +] + +[[package]] +name = "webb-proposal-signing-backends" +version = "0.5.0" dependencies = [ "async-trait", "ethereum-types 0.14.1", "futures", "hex", + "impl-trait-for-tuples", "native-tls", + "parking_lot 0.12.1", + "rand 0.8.5", "sled", "sp-core", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "typed-builder 0.12.0", - "webb", + "tracing-subscriber 0.3.16", + "typed-builder 0.14.0", + "webb 0.5.24", "webb-proposals", "webb-relayer-store", - "webb-relayer-types 0.1.0", - "webb-relayer-utils 0.1.0", + "webb-relayer-types 0.5.0", + "webb-relayer-utils 0.5.0", ] [[package]] @@ -8158,10 +9225,11 @@ dependencies = [ [[package]] name = "webb-relayer" -version = "0.4.1" +version = "0.5.0" dependencies = [ "anyhow", "axum", + "build-data", "config", "dotenv", "ethereum-types 0.14.1", @@ -8169,12 +9237,15 @@ dependencies = [ "paw", "serde_json", "sled", + "sp-core", + "sp-runtime", "tempfile", - "tokio 1.26.0", + "tokio 1.28.1", "tower-http", "tracing", "url", - "webb", + "webb 0.5.24", + "webb-bridge-registry-backends", "webb-event-watcher-traits", "webb-ew-dkg", "webb-ew-evm", @@ -8186,16 +9257,17 @@ dependencies = [ "webb-relayer-handlers", "webb-relayer-store", "webb-relayer-tx-queue", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-config" -version = "0.1.0" +version = "0.5.0" dependencies = [ "anyhow", "config", "directories-next", + "dotenv", "eth2_to_substrate_relay", "ethereum-types 0.14.1", "glob", @@ -8208,42 +9280,46 @@ dependencies = [ "tracing", "tracing-subscriber 0.3.16", "url", - "webb", + "webb 0.5.24", "webb-proposals", "webb-relayer-store", - "webb-relayer-types 0.1.0", - "webb-relayer-utils 0.1.0", + "webb-relayer-types 0.5.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-context" -version = "0.1.0" +version = "0.5.0" dependencies = [ - "coingecko", - "ethers 1.0.2", + "http", "native-tls", + "regex", + "serde", + "serde_json", "sp-core", - "tokio 1.26.0", - "webb", + "tokio 1.28.1", + "tracing", + "webb 0.5.24", + "webb-price-oracle-backends", "webb-relayer-config", "webb-relayer-store", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-handler-utils" -version = "0.1.0" +version = "0.5.0" dependencies = [ "native-tls", "serde", - "tokio 1.26.0", - "webb", + "tokio 1.28.1", + "webb 0.5.24", "webb-relayer-tx-relay-utils", ] [[package]] name = "webb-relayer-handlers" -version = "0.1.0" +version = "0.5.0" dependencies = [ "axum", "axum-client-ip", @@ -8253,22 +9329,22 @@ dependencies = [ "serde", "serde_json", "sp-core", - "tokio 1.26.0", + "tokio 1.28.1", "tokio-stream", "tracing", - "webb", + "webb 0.5.24", "webb-proposals", "webb-relayer-config", "webb-relayer-context", "webb-relayer-handler-utils", "webb-relayer-store", "webb-relayer-tx-relay", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-store" -version = "0.1.0" +version = "0.5.0" dependencies = [ "hex", "native-tls", @@ -8278,14 +9354,14 @@ dependencies = [ "sled", "tempfile", "tracing", - "webb", + "webb 0.5.24", "webb-proposals", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-tx-queue" -version = "0.1.0" +version = "0.5.0" dependencies = [ "backoff", "ethereum-types 0.14.1", @@ -8295,18 +9371,21 @@ dependencies = [ "sled", "sp-core", "sp-runtime", - "tokio 1.26.0", + "tokio 1.28.1", "tracing", - "webb", + "tracing-subscriber 0.3.16", + "url", + "webb 0.5.24", + "webb-relayer-config", "webb-relayer-context", "webb-relayer-store", - "webb-relayer-types 0.1.0", - "webb-relayer-utils 0.1.0", + "webb-relayer-types 0.5.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-tx-relay" -version = "0.1.0" +version = "0.5.0" dependencies = [ "chrono", "ethereum-types 0.14.1", @@ -8314,19 +9393,22 @@ dependencies = [ "native-tls", "once_cell", "serde", - "tokio 1.26.0", + "sp-core", + "tokio 1.28.1", "tracing", - "webb", + "webb 0.5.24", + "webb-chains-info", + "webb-price-oracle-backends", "webb-proposals", "webb-relayer-config", "webb-relayer-context", "webb-relayer-handler-utils", - "webb-relayer-utils 0.1.0", + "webb-relayer-utils 0.5.0", ] [[package]] name = "webb-relayer-tx-relay-utils" -version = "0.1.0" +version = "0.5.0" dependencies = [ "native-tls", "serde", @@ -8335,6 +9417,7 @@ dependencies = [ [[package]] name = "webb-relayer-types" version = "0.1.0" +source = "git+https://github.com/webb-tools/relayer.git#1e7bb7f51cf666eedd4df18a922694cdf8f88ed4" dependencies = [ "ethereum-types 0.14.1", "native-tls", @@ -8343,13 +9426,12 @@ dependencies = [ "tiny-bip39", "tracing", "url", - "webb", + "webb 0.5.21", ] [[package]] name = "webb-relayer-types" -version = "0.1.0" -source = "git+https://github.com/webb-tools/relayer.git#14ec42eff6627b4479312490595cf3702fcedcdc" +version = "0.5.0" dependencies = [ "ethereum-types 0.14.1", "native-tls", @@ -8358,12 +9440,13 @@ dependencies = [ "tiny-bip39", "tracing", "url", - "webb", + "webb 0.5.24", ] [[package]] name = "webb-relayer-utils" version = "0.1.0" +source = "git+https://github.com/webb-tools/relayer.git#1e7bb7f51cf666eedd4df18a922694cdf8f88ed4" dependencies = [ "ark-std", "axum", @@ -8372,9 +9455,8 @@ dependencies = [ "derive_more", "glob", "hex", - "hyper 0.14.24", + "hyper 0.14.25", "libsecp256k1", - "native-tls", "prometheus 0.13.3", "reqwest", "serde_json", @@ -8382,31 +9464,37 @@ dependencies = [ "sled", "thiserror", "url", - "webb", + "webb 0.5.21", "webb-proposals", ] [[package]] name = "webb-relayer-utils" -version = "0.1.0" -source = "git+https://github.com/webb-tools/relayer.git#14ec42eff6627b4479312490595cf3702fcedcdc" +version = "0.5.0" dependencies = [ + "ark-std", + "async-trait", "axum", "backoff", "config", "derive_more", + "futures", "glob", "hex", - "hyper 0.14.24", + "hyper 0.14.25", "libsecp256k1", + "native-tls", "prometheus 0.13.3", "reqwest", + "serde", + "serde_bytes", "serde_json", "serde_path_to_error", "sled", "thiserror", + "tokio 1.28.1", "url", - "webb", + "webb 0.5.24", "webb-proposals", ] @@ -8483,19 +9571,41 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "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]] @@ -8504,71 +9614,167 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[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.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] @@ -8604,7 +9810,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.0", - "send_wrapper", + "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -8643,71 +9849,20 @@ checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zip" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" -dependencies = [ - "aes 0.7.5", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac 0.12.1", - "pbkdf2 0.11.0", - "sha1", - "time 0.3.20", - "zstd", -] - -[[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.7+zstd.1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" -dependencies = [ - "cc", - "libc", - "pkg-config", + "syn 2.0.13", ] diff --git a/Cargo.toml b/Cargo.toml index 4497c76ce..d89da4256 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace.package] +version = "0.5.0" authors = ["Webb Developers "] license = "Apache-2.0" documentation = "https://docs.rs/webb-relayer" @@ -7,41 +8,41 @@ repository = "https://github.com/webb-tools/relayer" edition = "2021" [workspace] -members = [ - ".", - "crates/*", - "event-watchers/*", - "services/*", -] - -[patch.crates-io] -webb = { git = "https://github.com/webb-tools/webb-rs.git" } +members = [".", "crates/*", "event-watchers/*", "services/*"] [workspace.dependencies] +webb-price-oracle-backends = { path = "crates/price-oracle-backends" } +webb-bridge-registry-backends = { path = "crates/bridge-registry-backends" } webb-proposal-signing-backends = { path = "crates/proposal-signing-backends" } webb-relayer-tx-queue = { path = "crates/tx-queue" } webb-relayer-handlers = { path = "crates/relayer-handlers" } webb-relayer-store = { path = "crates/relayer-store" } webb-relayer-config = { path = "crates/relayer-config" } webb-relayer-context = { path = "crates/relayer-context" } -webb-relayer-utils = { path = "crates/relayer-utils"} -webb-event-watcher-traits = { path = "crates/event-watcher-traits"} +webb-relayer-utils = { path = "crates/relayer-utils" } +webb-chains-info = { path = "crates/chains-info" } +webb-event-watcher-traits = { path = "crates/event-watcher-traits" } webb-ew-dkg = { path = "event-watchers/dkg" } webb-ew-evm = { path = "event-watchers/evm" } webb-ew-substrate = { path = "event-watchers/substrate" } webb-relayer-handler-utils = { path = "crates/relayer-handler-utils" } webb-relayer-types = { path = "crates/relayer-types" } webb-relayer = { path = "services/webb-relayer" } +webb-proof-generation = { path = "crates/proof-generation" } +webb-circom-proving = { path = "crates/circom-proving" } +thiserror = "^1" anyhow = "^1" tracing = { version = "^0.1", features = ["log"] } +tracing-subscriber = { version = "0.3.16", features = ["parking_lot", "env-filter"] } url = { version = "^2.3", features = ["serde"] } sled = "^0.34" tokio = { version = "^1", features = ["full"] } config = { version = "0.13", default-features = false, features = ["toml", "json"] } serde_json = { version = "^1", default-features = false } paw = { version = "^1.0" } -webb = { version = "0.5.17", default-features = false } +# webb = { version = "0.5.21", default-features = false } +webb = { version = "0.5.24", git = "https://github.com/webb-tools/webb-rs", default-features = false } sp-core = { version = "16.0.0", default-features = false } sp-runtime = { version = "18.0.0", default-features = false } # Used by ethers (but we need it to be vendored with the lib). @@ -49,16 +50,21 @@ native-tls = { version = "^0.2", features = ["vendored"] } webb-proposals = { version = "0.5.4", default-features = false, features = ["scale"] } ethereum-types = "0.14.1" dotenv = "0.15.0" -axum = { version = "0.6.4", features = ["ws"] } +axum = { version = "0.6.12", features = ["ws"] } +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } tempfile = "^3.3" async-trait = "^0.1" futures = { version = "^0.3", default-features = false } +parking_lot = "^0.12" +rand = { version = "0.8", default-features = false, features = ["getrandom"] } +typed-builder = "0.14.0" backoff = { version = "0.4.0", features = ["tokio"] } hex = { version = "0.4", default-features = false } libsecp256k1 = "0.7.1" serde = { version = "^1", default-features = false, features = ["derive"] } glob = "^0.3" serde_path_to_error = "0.1.9" +serde_bytes = "0.11" # eth2 light client crates eth-rpc-client = { package = "eth_rpc_client", git = "https://github.com/webb-tools/pallet-eth2-light-client" } diff --git a/README.md b/README.md index 1d3f1670c..ad00db7a5 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@
- ![Webb Logo](./assets/webb_banner_light.png#gh-light-mode-only) +![Webb Logo](./assets/webb_banner_light.png#gh-light-mode-only) + +![Webb Logo](./assets/webb_banner_dark.png#gh-dark-mode-only) + - ![Webb Logo](./assets/webb_banner_dark.png#gh-dark-mode-only) -
-# Webb Relayer -[![GitHub release (latest by date)](https://img.shields.io/github/v/release/webb-tools/relayer?style=flat-square)](https://github.com/webb-tools/relayer/releases/latest) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/webb-tools/relayer/check.yml?branch=main&style=flat-square)](https://github.com/webb-tools/relayer/actions) [![License Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![Twitter](https://img.shields.io/twitter/follow/webbprotocol.svg?style=flat-square&label=Twitter&color=1DA1F2)](https://twitter.com/webbprotocol) [![Telegram](https://img.shields.io/badge/Telegram-gray?logo=telegram)](https://t.me/webbprotocol) [![Discord](https://img.shields.io/discord/833784453251596298.svg?style=flat-square&label=Discord&logo=discord)](https://discord.gg/cv8EfJu3Tn) +# Webb Relayer +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/webb-tools/relayer?style=flat-square)](https://github.com/webb-tools/relayer/releases/latest) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/webb-tools/relayer/check.yml?branch=main&style=flat-square)](https://github.com/webb-tools/relayer/actions) [![License Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![Twitter](https://img.shields.io/twitter/follow/webbprotocol.svg?style=flat-square&label=Twitter&color=1DA1F2)](https://twitter.com/webbprotocol) [![Telegram](https://img.shields.io/badge/Telegram-gray?logo=telegram)](https://t.me/webbprotocol) [![Discord](https://img.shields.io/discord/833784453251596298.svg?style=flat-square&label=Discord&logo=discord)](https://discord.gg/cv8EfJu3Tn)

📖 Table of Contents

@@ -69,10 +70,10 @@ rustup update Great! Now your Rust environment is ready! 🚀🚀 -Lastly, install +Lastly, install - - [DVC](https://dvc.org/) is used for fetching large ZK files and managing them alongside git - - [substrate.io](https://docs.substrate.io/main-docs/install/) may require additional dependencies +- [DVC](https://dvc.org/) is used for fetching large ZK files and managing them alongside git +- [substrate.io](https://docs.substrate.io/main-docs/install/) may require additional dependencies 🚀🚀 Your environment is complete! 🚀🚀 @@ -102,9 +103,9 @@ cargo run --bin webb-relayer --features cli -- -c ./config/development/evm-local > Hot Tip 🌶️: To increase the logger verbosity add additional `-vvvv` during start up command. You will now see `TRACE` logs. Happy debugging! -#### Local Substrate Mixer +#### Local Substrate Setup -To use the relayer for our Substrate mixer, you will first need to start a local substrate node that integrates with our pallets [webb-standalone-node](https://github.com/webb-tools/protocol-substrate/). Once the Substrate node is started locally you can proceed to start the relayer. +To use the relayer for our Substrate based chains, you will first need to start a local substrate node that integrates with our pallets [webb-standalone-node](https://github.com/webb-tools/protocol-substrate/). Once the Substrate node is started locally you can proceed to start the relayer. ``` cargo run --bin webb-relayer --features cli -- -c ./config/development/local-substrate -vvv @@ -145,11 +146,39 @@ webb-relayer -vv -c ./config - [`SubstrateConfig`](https://webb-tools.github.io/relayer/webb_relayer/config/struct.SubstrateConfig.html) - [`EvmChainConfig`](https://webb-tools.github.io/relayer/webb_relayer/config/struct.EvmChainConfig.html) +#### Relayer Common Configuration + +| Field | Description | Optionality | +| --------------- | ------------------------------------------------------------------------------------------------ | ----------- | +| `port` | Relayer port number | Required | +| `features` | Enable required features by setting them to `true` . All featured are enabled by default | Optional | +| `evm-etherscan` | Etherscan api configuration for chains, required if `private-tx` feature is enabled for relayer. | Optional | + +- `Features` Configuration + +``` +[features] +governance-relay = true +data-query = true +private-tx-relay = true +``` + +- `Evm-etherscan` Configuration + +``` +[evm-etherscan.goerli] +chain-id = 5 +api-key = "$ETHERSCAN_GOERLI_API_KEY" +[evm-etherscan.polygon] +chain-id = 137 +api-key = "$POLYGONSCAN_MAINNET_API_KEY" +``` + #### Chain Configuration | Field | Description | Optionality | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -| `http-endpoint` | Http(s) Endpoint for quick Req/Res | Required | +| `http-endpoint` | Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. | Required | | `ws-endpoint` | Websocket Endpoint for long living connections | Required | | `name` | The Chain/Node name | Required | | `explorer` | Block explorer, used for generating clickable links for transactions that happens on this chain. | Optional | @@ -162,50 +191,45 @@ webb-relayer -vv -c ./config #### Contract Configuration -| Field | Description | Optionality | -| -------------------------- | ------------------------------------------------------------------------------------------ | ----------- | -| `contract` | Chain contract. Must be either:
- VAnchor
- SignatureBridge
| Required | -| `address` | The address of this contract on this chain. | Required | -| `deployed-at` | The block number where this contract got deployed at. | Required | -| `events-watcher` | Control the events watcher for this contract. | Optional | -| `withdraw-config` | Config the fees and gas limits of your private transaction relayer. | Optional | -| `proposal-signing-backend` | a value of `ProposalSigingBackend` (for example `{ type = "DKGNode", node = "dkg-node" }`) | Optional | +| Field | Description | Optionality | +| -------------------------- | ---------------------------------------------------------------------------------------- | ----------- | +| `contract` | Chain contract. Must be either:
- VAnchor
- SignatureBridge
| Required | +| `address` | The address of this contract on this chain. | Required | +| `deployed-at` | The block number where this contract got deployed at. | Required | +| `events-watcher` | Control the events watcher for this contract. | Optional | +| `withdraw-config` | Config the fees and gas limits of your private transaction relayer. | Optional | +| `proposal-signing-backend` | a value of `ProposalSigingBackend` (for example `{ type = "DKGNode", chain-id = 1080 }`) | Optional | #### Event Watcher Configuration -| Field | Description | Optionality | -| -------------------------- | ------------------------------------------------------------------------------------------ | ----------- | -| `enabled` | Boolean value. Default set to `true` | Optional | -| `polling-interval` | Interval between polling next block in millisecond. Default value is `3000ms` | Optional | -| `print-progress-interval` | Interval between printing sync progress in millisecond. Default value is `7000ms` | Optional | -| `sync-blocks-from` | Block number from which relayer will start syncing. Default will be `latest` block number | Optional | - +| Field | Description | Optionality | +| ------------------------- | ----------------------------------------------------------------------------------------- | ----------- | +| `enabled` | Boolean value. Default set to `true` | Optional | +| `polling-interval` | Interval between polling next block in millisecond. Default value is `3000ms` | Optional | +| `print-progress-interval` | Interval between printing sync progress in millisecond. Default value is `7000ms` | Optional | +| `sync-blocks-from` | Block number from which relayer will start syncing. Default will be `latest` block number | Optional | ### Docker 🐳 -To use Docker to run the relayer, you will need to specify a config file and provide an `.env` file as described above. Then proceed to save it into the `config` directory. +To deploy the relayer with Docker, copy the `docker` folder to your server. Add an `.env` file as described above and save it into the `config` directory. You also need to adjust the `server_name` (domain) specified in `user_conf.d/relayer.conf`. When you are ready, start the relayer with `docker compose up -d`. You can see the logs with `docker compose logs -f`. It will automatically request a TLS certificate using Let's Encrypt and start operating. -To run docker image: +> Note: this uses the latest and pre-released version deployed from `develop` branch, change `:edge` to the [latest stable release version](https://github.com/webb-tools/relayer/pkgs/container/relayer). On the other hand if you always want to use the latest development build, set up a cronjob to execute `docker compose pull && docker compose up -d` regularly in the docker folder. -```sh -docker run --rm -v ":/config" --env-file .env -p 9955:9955 ghcr.io/webb-tools/relayer:edge -``` +The Docker setup also includes a preconfigured Grafana installation for monitoring. It is available on `localhost:3000` with login `admin` / `admin`. It includes configuration for Slack alerts, to use it enter a Slack Incoming Webhook URL in `provisioning/alerting/alerting.yaml` where it says `slack-placeholder`. -> Note: this uses the latest and pre-released version deployed from `main` branch, change `edge` to the latest stable release version. +#### Metrics Information -This will mount a configuration files at the `/config` directory inside the container so it would allow it to read the configuration you added. +The Metric information is being handled by prometheus and the Relayer supports the following metrics: -#### Metrics Information - The Metric information is being handled by prometheus and the Relayer supports the following metrics: - 1. The number of times the `BridgeWatcher` enter backoff - 2. The number of times the `handle_proposal` executes - 3. The number of times the Transaction Queue enters backoff - 4. The number of times a Proposal attempted to be queued - 5. Total `Fees` Earned by the relayer - 6. Total `transaction` made - 7. Total `gas` spent - 8. Number of `proposals` proposed - 9. Amount of `data` stored +1. The number of times the `BridgeWatcher` enter backoff +2. The number of times the `handle_proposal` executes +3. The number of times the Transaction Queue enters backoff +4. The number of times a Proposal attempted to be queued +5. Total `Fees` Earned by the relayer +6. Total `transaction` made +7. Total `gas` spent +8. Number of `proposals` proposed +9. Amount of `data` stored

API 📡

@@ -261,6 +285,16 @@ The relayer has 3 endpoints available to query from. They are outlined below for "experimental": { "smart-anchor-updates": false, "smart-anchor-updates-retries": 0 + }, + "features": { "dataQuery": true, "governanceRelay": true, "privateTxRelay": true }, + "assets": { + "TNT": { "price": 0.1, "name": "Tangle Network Token", "decimals": 18 }, + "tTNT": { "price": 0.1, "name": "Test Tangle Network Token", "decimals": 18 } + }, + "build" : { + "version": "0.5.0", + "commit": "c8875ba78298d34272e40c2e302fcfe33f191147", + "timestamp": "2023-05-19T15:57:40Z" } } ``` @@ -277,6 +311,7 @@ The relayer has 3 endpoints available to query from. They are outlined below for - `contract_address` Contract address of `vanchor`, applicable in case of evm based chains. ##### For evm + ``` /api/v1/leaves/{target_system}/{chain_id}/{contract_address} #example @@ -284,7 +319,9 @@ The relayer has 3 endpoints available to query from. They are outlined below for ``` ##### For substrate + > Note: Since substrate doesn't have contract address we use `tree_id` + ``` /api/v1/leaves/{target_system}/{chain_id}/{tree_id}/{pallet_id} #example @@ -297,47 +334,35 @@ The relayer has 3 endpoints available to query from. They are outlined below for ```json { "leaves": [ - [ - 42, 255, 162, 80, 180, 134, 202, 153, - 49, 119, 64, 112, 255, 230, 46, 130, - 230, 68, 98, 78, 34, 206, 175, 181, - 18, 112, 14, 188, 166, 72, 115, 130 - ], - [ - 2, 103, 50, 103, 246, 93, 14, 110, - 51, 252, 154, 157, 102, 157, 16, 237, - 117, 55, 107, 175, 233, 112, 144, 18, - 178, 30, 175, 223, 81, 29, 227, 71 - ], - [ - 40, 30, 11, 223, 86, 95, 109, 18, - 31, 93, 224, 12, 208, 111, 222, 217, - 80, 195, 194, 100, 220, 183, 40, 142, - 232, 45, 32, 187, 98, 174, 142, 231 - ], - [ - 31, 141, 59, 100, 22, 69, 177, 39, - 226, 21, 59, 203, 47, 116, 66, 133, - 11, 254, 53, 179, 205, 251, 172, 32, - 8, 228, 96, 148, 230, 134, 80, 122 - ], + "0x015110a4b1a8bf29f7b6b2cb3fe5f52c2eeccd9ff7e8a0fb7d4ff2ae61516562", + "0x2fa56e6179d1bf0afc6f3ee2a52dc68cc2076d380a55165578c1c558e1f6f1dc", + "0x031317e0fe026ce99cf9b3cf8fefed7ddc21c5f4181e49fd6e8370aea5006da0", + "0x07507826af3c90c457222ad0305d90bf8bcfb1d343c2a9c17d280ff648b43582", + "0x0ff8f7f0fc798b9b34464ba51a10bdde16d17506f3251f9658335504f07c9c5f", + "0x0b92b3c5013eb2374527a167af6464f1ab8b11da1dd36e5a6a2cf76130fee9e3", + "0x2bccea444d1078a2b5778f3c8f28013219abfe5c236d1276b87276ec5eec4354", + "0x0be7c8578e746b1b7d913c79affb80c715b96a1304edb68d1d7e6dc33f30260f", + "0x117dae7ac7b62ed97525cc8541823c2caae25ffaf6168361ac19ca484851744f", + "0x0c187c0b413f2c2e8ebaeffbe9351fda6eb46dfa396b0c73298215950439fa75" ], "lastQueriedBlock": 37 } - ``` +``` **Retrieve encrypted leaves cache** ##### For evm ``` + /api/v1/encrypted_outputs/evm/{chain_id}/{contract_address} #example /api/v1/encrypted_outputs/evm/4/0x9d36b94f245857ec7280415140800dde7642addb -``` + +````
Expected Response - + ```json { "encryptedOutputs": [ @@ -368,24 +393,27 @@ The relayer has 3 endpoints available to query from. They are outlined below for ], "lastQueriedBlock": 37 } - - ``` + +```` +
**Retrieve Metrics information** + ``` /api/v1/metrics ``` +
Expected Response - ```json - { +```json +{ "metrics": "# HELP bridge_watcher_back_off_metric specifies how many times the bridge watcher backed off\n# TYPE bridge_watcher_back_off_metric counter\nbridge_watcher_back_off_metric 0\n# HELP gas_spent_metric The total number of gas spent\n# TYPE gas_spent_metric counter\ngas_spent_metric 0\n# HELP handle_proposal_execution_metric How many times did the function handle_proposal get executed\n# TYPE handle_proposal_execution_metric counter\nhandle_proposal_execution_metric 0\n# HELP proposal_queue_attempt_metric How many times a proposal is attempted to be queued\n# TYPE proposal_queue_attempt_metric counter\nproposal_queue_attempt_metric 0\n# HELP total_active_relayer_metric The total number of active relayers\n# TYPE total_active_relayer_metric counter\ntotal_active_relayer_metric 0\n# HELP total_fee_earned_metric The total number of fees earned\n# TYPE total_fee_earned_metric counter\ntotal_fee_earned_metric 0\n# HELP total_number_of_data_stored_metric The Total number of data stored\n# TYPE total_number_of_data_stored_metric counter\ntotal_number_of_data_stored_metric 1572864\n# HELP total_number_of_proposals_metric The total number of proposals proposed\n# TYPE total_number_of_proposals_metric counter\ntotal_number_of_proposals_metric 0\n# HELP total_transaction_made_metric The total number of transaction made\n# TYPE total_transaction_made_metric counter\ntotal_transaction_made_metric 0\n# HELP transaction_queue_back_off_metric How many times the transaction queue backed off\n# TYPE transaction_queue_back_off_metric counter\ntransaction_queue_back_off_metric 0\n" } - ``` -
+``` + **Retrieve fee information** @@ -402,20 +430,22 @@ The relayer has 3 endpoints available to query from. They are outlined below for
Expected Response - ```json - { - "estimatedFee": "0x476b26e0f", - "gasPrice": "0x11", - "refundExchangeRate": "0x28f", - "maxRefund": "0xf3e59", - "timestamp": "2023-01-19T06:29:49.556114073Z" - } - ``` +```json +{ + "estimatedFee": "0x476b26e0f", + "gasPrice": "0x11", + "refundExchangeRate": "0x28f", + "maxRefund": "0xf3e59", + "timestamp": "2023-01-19T06:29:49.556114073Z" +} +``` +
**Retrieve Metrics information for specific resource** ##### For evm + ``` /api/v1/metrics/{target_system}/{chain_id}/{contract_address} #example @@ -423,11 +453,13 @@ The relayer has 3 endpoints available to query from. They are outlined below for ``` ##### For substrate + ``` /api/v1/metrics/{target_system}/{chain_id}/{tree_id}/{pallet_id} #example /api/v1/metrics/substrate/4/9/44 ``` +
Expected Response @@ -452,18 +484,20 @@ cargo test ### To run E2E tests -First you will need [`protocol-substrate`](https://github.com/webb-tools/protocol-substrate) node, compiled locally (in release mode) and both the `protocol-substrate` and `relayer` project must be next to each other. The relayer must be compiled using `--features integration-tests,cli`. +First you will need [`protocol-substrate`](https://github.com/webb-tools/protocol-substrate) and [`tangle`](https://github.com/webb-tools/tangle) nodes, compiled locally (in release mode) and both the `protocol-substrate` and `relayer` project must be next to each other. The relayer must be compiled using `--features integration-tests,cli`. Here is the basic setup you will need: -1. Clone the Relayer repo `git clone https://github.com/webb-tools/relayer.git` +1. Clone the Relayer repo `git clone https://github.com/webb-tools/relayer.git` 2. Clone Protocol Substrate node `https://github.com/webb-tools/protocol-substrate.git` 3. Then fetch the submodules for the node `cd protocol-substrate && git submodule update --init` 4. While you are there, build the standalone node `cargo build --release -p webb-standalone-node` -5. And then go back to the relayer `cd ../relayer` -6. Run `cd tests && dvc pull` -7. Run `yarn install` (in `tests` dir) -8. `yarn test` +5. Clone Tangle repo `https://github.com/webb-tools/tangle.git` +6. Build tangle `cd tangle && cargo build --release --features integration-tests -p tangle-standalone` +7. And then go back to the relayer `cd ../relayer` +8. Run `cd tests && dvc pull` +9. Run `yarn install` (in `tests` dir) +10. `yarn test` ### Tips for E2E tests @@ -473,7 +507,7 @@ Here is the basic setup you will need: 4. failing tests will keep retry before giving up, up to 5 times. To disable that use `yarn test --retries=0`. 5. You can combine all the tips above together, for more options see [here](https://mochajs.org/#command-line-usage) -For the Substrate Mixer test, you can connect to your local chain manually by: +For the Substrate test, you can connect to your local chain manually by: 1. Specifying the Alice node ports such as: diff --git a/ci/clippy.sh b/ci/clippy.sh new file mode 100755 index 000000000..b7e3b734d --- /dev/null +++ b/ci/clippy.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +# Checks which apply to all code including tests +cargo clippy --workspace --all-targets --all-features -- -D warnings -D deprecated -D clippy::perf \ + -D clippy::complexity -D clippy::style -D clippy::correctness \ + -D clippy::suspicious -D clippy::dbg_macro -D clippy::if_then_some_else_none \ + -D clippy::items-after-statements -D clippy::implicit_clone \ + -D clippy::cast_lossless -D clippy::manual_string_new \ + -D clippy::redundant_closure_for_method_calls \ + -D clippy::unused_self -D clippy::get_first + +# Checks which apply to main code (not tests) +cargo clippy --workspace --all-features -- -D clippy::unwrap_used \ + -D clippy::indexing_slicing diff --git a/config/README.md b/config/README.md new file mode 100644 index 000000000..cb4510b74 --- /dev/null +++ b/config/README.md @@ -0,0 +1,1282 @@ +## Relayer Configration files + +This directory contains the example configuration files for the relayer. The relayer is configured +using a set of TOML file(s) which can be thought of as a set of blueprints for the relayer. In the +following section we will describe the different configuration entries and how to use them. + +### Index + +- [Global Configuration](#global-configuration) + + - [port](#port) + - [features](#features) + - [governance-relay](#governance-relay) + - [data-query](#data-query) + - [private-tx-relay](#private-tx-relay) + - [evm-etherscan](#evm-etherscan) + - [chain-id](#chain-id) + - [api-key](#api-key) + - [assets](#assets) + - [name](#name) + - [decimals](#decimals) + - [price](#price) + +- [EVM Chain Configuration](#evm-chain-configuration) + - [name](#name-1) + - [chain-id](#chain-id) + - [http-endpoint](#http-endpoint) + - [ws-endpoint](#ws-endpoint) + - [private-key](#private-key) + - [block-confirmations](#block-confirmations) + - [enabled](#enabled) + - [explorer](#explorer) + - [beneficiary](#beneficiary) + - [tx-queue](#tx-queue) + - [max-sleep-interval](#max-sleep-interval) + - [contracts](#contracts) + - [contract](#contract) + - [address](#address) + - [deployed-at](#deployed-at) + - [events-watcher](#events-watcher) + - [enabled](#enabled-1) + - [enable-data-query](#enable-data-query) + - [polling-interval](#polling-interval) + - [max-blocks-per-step](#max-blocks-per-step) + - [sync-blocks-from](#sync-blocks-from) + - [print-progress-interval](#print-progress-interval) + - [proposal-signing-backend](#proposal-signing-backend) + - [type](#type) + - [chain-id](#chain-id-1) + - [private-key](#private-key-1) + - [linked-anchors](#linked-anchors) + - [type](#type-1) + - [resource-id](#resource-id) + - [chain-id](#chain-id-1) + - [address](#address-1) + - [pallet](#pallet) + - [tree-id](#tree-id) +- [Substrate Node Configuration](#substrate-node-configuration) + - [name](#name-2) + - [chain-id](#chain-id-2) + - [http-endpoint](#http-endpoint-1) + - [ws-endpoint](#ws-endpoint-1) + - [enabled](#enabled-2) + - [explorer](#explorer-1) + - [suri](#suri) + - [beneficiary](#beneficiary-1) + - [runtime](#runtime) + - [tx-queue](#tx-queue-1) + - [max-sleep-interval](#max-sleep-interval-1) + - [pallets](#pallets) + - [pallet](#pallet-1) + - [events-watcher](#events-watcher-1) + - [enabled](#enabled-3) + - [polling-interval](#polling-interval-1) + - [max-blocks-per-step](#max-blocks-per-step-1) + - [sync-blocks-from](#sync-blocks-from-1) + - [print-progress-interval](#print-progress-interval-1) + - [enable-data-query](#enable-data-query-1) + - [proposal-signing-backend](#proposal-signing-backend) + - [type](#type) + - [chain-id](#chain-id-1) + - [private-key](#private-key-1) + - [linked-anchors](#linked-anchors) + - [type](#type-1) + - [resource-id](#resource-id) + - [chain-id](#chain-id-2) + - [address](#address-1) + - [pallet](#pallet-1) + - [tree-id](#tree-id) + +### Global Configuration + +The global configuration file is used to configure the relayer. It is usually located at a file +called `main.toml` in the `config` directory. + +#### port + +The port on which the relayer will listen for incoming connections. + +- Type: `number` +- Required: `false` +- Default: `9955` +- env: `WEBB_PORT` + +Example: + +```toml +port = 9955 +``` + +#### features + +The features section is used to enable or disable the relayer features. + +- Type: `table` +- Required: `false` +- Default: `{ governance-relay = true, data-query = true, private-tx-relay = true }` +- env: `WEBB_FEATURES_GOVERNANCE_RELAY`, `WEBB_FEATURES_DATA_QUERY`, + `WEBB_FEATURES_PRIVATE_TX_RELAY` + +Example: + +```toml +[features] +governance-relay = true +data-query = true +private-tx-relay = true +``` + +##### governance-relay + +Enable or disable the governance-relay feature. Enabling this feature will allow the relayer to +relay proposals and vote on them between the chains. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_FEATURES_GOVERNANCE_RELAY` + +Example: + +```toml +[features] +governance-relay = true +``` + +##### data-query + +Enable or disable the data-query feature. Enabling this feature will allow the relayer to work as a +data query oracle. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_FEATURES_DATA_QUERY` + +Example: + +```toml +[features] +data-query = true +``` + +##### private-tx-relay + +Enable or disable the private-tx-relay feature. When enabled, the relayer will be able to relay +private transactions. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_FEATURES_PRIVATE_TX_RELAY` + +Example: + +```toml +[features] +private-tx-relay = true +``` + +#### evm-etherscan + +Etherscan api configuration for chains. This config is required if +[private-tx-relay](#private-tx-relay) is enabled. example: + +```toml +[evm-etherscan.goerli] +chain-id = 5 +api-key = "$ETHERSCAN_GOERLI_API_KEY" +[evm-etherscan.polygon] +chain-id = 137 +api-key = "$POLYGONSCAN_MAINNET_API_KEY" +``` + +##### chain-id + +Etherscan chain id, this are used to fetch gas prices from the explorer. example: + +```toml +chain-id = 5 +``` + +##### api-key + +Etherscan api key, this are used to fetch gas prices from the explorer. example: + +```toml +api-key = "$ETHERSCAN_GOERLI_API_KEY" +``` + +#### Assets + +The assets section is used to configure the unlisted assets that the relayer will be able to work +with. + +Unlisted assets are assets that are not listed in any exchange, and the relayer cannot get the price +of the asset from any exchange. So, the price of the asset must be configured manually. These are +mainly used for testing purposes and for assets that are not listed in any exchange. + +- Type: `table` +- Required: `false` +- Default: `{}` +- env: `WEBB_ASSETS__NAME`, `WEBB_ASSETS__PRICE`, + `WEBB_ASSETS__DECIMALS` + +Example: + +```toml +[assets] +tTNT = { name = "Test Tangle Network Token", price = 0.10, decimals = 18 } +``` + +##### name + +The name of the asset. + +- Type: `string` +- Required: `true` +- env: `WEBB_ASSETS__NAME` + +Example: + +```toml +name = "Test Tangle Network Token" +``` + +##### price + +The price of the asset in USD. + +- Type: `number` +- Required: `true` +- env: `WEBB_ASSETS__PRICE` + +Example: + +```toml +price = 0.10 +``` + +##### decimals + +The number of decimals of the asset. + +- Type: `number` +- Required: `true` +- env: `WEBB_ASSETS__DECIMALS` + +Example: + +```toml +decimals = 18 +``` + +### EVM Chain Configuration + +The EVM chain configuration file is used to configure the relayer to work with a specific EVM chain. +It is usually located at a file called `evm/.toml` in the `config` directory. + +The value of this configuration is a table, and the name of the table is the name of the chain, for +example: + +```toml +[evm.ethereum] +chain-id = 1 +name = "ethereum" +# ... +``` + +So, in general it is `[evm.]`, where `` is the name of the chain. The +following sections describe the different configuration entries and how to use them. + +#### name + +The name of the chain. This name will be used to identify the chain in the relayer. + +- Type: `string` +- Required: `true` +- env: `WEBB_EVM__NAME` + +Example: + +```toml +name = "ethereum" +``` + +#### chain-id + +The chain id of the chain. This id will be used to identify the chain in the relayer. + +- Type: `number` +- Required: `true` +- env: `WEBB_EVM__CHAIN_ID` + +Example: + +```toml +chain-id = 1 +``` + +#### http-endpoint + +The HTTP(s) RPC endpoint for this chain, used for watching events, and sending transactions. Input can be single http-endpoint or array of multiple http-endpoints. + +- Type: `string | string[]` +- Required: `true` +- env: `WEBB_EVM__HTTP_ENDPOINT` + +Example: +- Single Endpoint +```toml +http-endpoint = "https://mainnet.infura.io/v3/" +``` + +- Multiple Endpoints +```toml +http-endpoint = ["https://mainnet.infura.io/v3/","https://rpc.testnet.network"] +``` + + +#### ws-endpoint + +The WebSocket RPC endpoint for this chain, used for watching events, and sending transactions. + +- Type: `string` +- Required: `true` +- env: `WEBB_EVM__WS_ENDPOINT` + +Example: + +```toml +ws-endpoint = "wss://mainnet.infura.io/ws/v3/" +``` + +#### private-key + +The private key configuration specifies the private key of the account on the EVM chain used for +signing transactions. The format of the private key depends on its value: + +If the private key starts with `0x`, it is considered a raw (64 bytes) hex-encoded private key. +Example: `0x8917174396171783496173419137618235192359106130478137647163400318`. + +If the private key starts with `$`, it is considered an environment variable containing a +hex-encoded private key. Example: `$MAINNET_PRIVATE_KEY`. + +If the private key starts with '> ', it is considered a command that the relayer will execute to +obtain the hex-encoded private key. Example: > `./getKey.sh mainnet-privatekey`. + +If the private key doesn't contain special characters and has 12 or 24 words, it is considered a +mnemonic string. The words should be separated by spaces, and the string should not be enclosed in +quotes or any other characters. + +- Type: `string` +- Required: + - `true` if `features.governance-relay` is `true` + - `true` if `features.private-tx-relay` is `true` + - `false` otherwise +- env: `WEBB_EVM__PRIVATE_KEY` + +Example: + +```toml +private-key = "0x8917174396171783496173419137618235192359106130478137647163400318" +``` + +> **Warning** The private key should be kept secret, and should not be hard-coded in the +> configuration file. Instead, it should be loaded from an environment variable, or a file. + +#### block-confirmations + +The number of block confirmations to wait before processing an event. + +- Type: `number` +- Required: `false` +- Default: `0` +- env: `WEBB_EVM__BLOCK_CONFIRMATIONS` + +Example: + +```toml +block-confirmations = 5 +``` + +#### enabled + +Enable or disable this chain. If this is set to `false`, then the relayer will not consider this +chain while loading the configuration files. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_EVM__ENABLED` + +Example: + +```toml +enabled = true +``` + +#### explorer + +The block explorer URL for this chain. This is used to generate links to the transactions, useful +for debugging. + +- Type: `string` +- Required: `false` +- Default: `null` +- env: `WEBB_EVM__EXPLORER` + +Example: + +```toml +explorer = "https://etherscan.io" +``` + +#### beneficiary + +The address of the beneficiary account on this chain. This is used to receive the fees from relaying +transactions. It is optional, and if not provided, the relayer will use the account address of the +provided [private-key](#private-key) for this chain. + +- Type: `string` +- Required: `false` +- Default: `null` +- env: `WEBB_EVM__BENEFICIARY` + +Example: + +```toml +beneficiary = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" +``` + +#### Tx Queue + +The tx queue is used to store the transactions that are waiting to be sent to the chain. The relayer +uses a database to store the transactions, and the configuration for the database is stored in the +`tx-queue` section of the configuration file. + +##### max-sleep-interval + +The maximum time to sleep between sending transactions. This controls the rate at which the relayer +sends transactions to the chain. + +- Type: `number` +- Required: `false` +- Default: `10000ms` +- env: `WEBB_EVM__TX_QUEUE_MAX_SLEEP_INTERVAL` + +Example: + +```toml +tx-queue = { max-sleep-interval = 5000 } +``` + +#### Contracts + +The contracts section is used to configure the contracts that the relayer will use to interact with +the chain. The contracts are identified by their address and type, and the configuration for each +contract is stored in a list, each has a different type and address. + +Here is an example of the configuration for the contracts: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +address = "0x8eB24319393716668D768dCEC29356ae9CfFe285" +deployed-at = 3123412 +# ... + +[[evm.ethereum.contracts]] +contract = "SignatureBridge" +address = "0xd8dA6BF26964aF9D7eEd9e03E03517D3faA9d045" +deployed-at = 3123413 +# ... +``` + +##### contract + +The type of the contract. This is used to identify the contract, and the relayer will use this to +determine which contract to use for a specific operation. Each contract type has its own +configuration, and different internal service that will handle the contract operations. + +- Type: `enum` +- Possible values: + - `VAnchor` + - `SignatureBridge` +- Required: `true` +- env: `WEBB_EVM__CONTRACTS__CONTRACT` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +``` + +##### address + +The address of the contract on the configured chain. + +- Type: `string` +- Required: `true` +- env: `WEBB_EVM__CONTRACTS__ADDRESS` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +address = "0x8eB24319393716668D768dCEC29356ae9CfFe285" +``` + +##### deployed-at + +The block number at which the contract was deployed. This is used to determine the starting block +number for scanning events. + +- Type: `number` +- Required: `true` +- env: `WEBB_EVM__CONTRACTS__DEPLOYED_AT` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +deployed-at = 3123412 +``` + +##### Events Watcher + +The events watcher is used to watch for events emitted by the contracts. The relayer uses this +configuration values to determine how the relayer will poll the events from that contract. + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { enabled = true, poll-interval = 12000 } +``` + +###### enabled + +Enable or disable the events watcher for this contract. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_EVM__CONTRACTS__EVENTS_WATCHER_ENABLED` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { enabled = true } +``` + +###### enable-data-query + +Enable or disable the data query for this contract. When enabled, it allows the relayer to query the +contract's events, such as the leaves. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_EVM__CONTRACTS__EVENTS_WATCHER_ENABLE_DATA_QUERY` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { enable-data-query = true } +``` + +###### poll-interval + +The interval at which the relayer will poll for events from the contract. + +- Type: `number` +- Required: `false` +- Default: `7000ms` +- env: `WEBB_EVM__CONTRACTS__EVENTS_WATCHER_POLL_INTERVAL` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { poll-interval = 12000 } +``` + +##### max-blocks-per-step + +The maximum number of blocks to scan for events in a single step. This controls the rate at which +the relayer scans for events from the contract, and also affects the speed at which the relayer +synchronizes with the chain. + +- Type: `number` +- Required: `false` +- Default: `100` +- env: `WEBB_EVM__CONTRACTS__MAX_BLOCKS_PER_STEP` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { max-blocks-per-step = 500 } +``` + +##### sync-blocks-from + +The block number to start scanning for events from. This value can override the +[deployed-block](#deployed-at) configuration value but is only used during the initial +synchronization of the chain. If not specified, the relayer uses the [deployed-block](#deployed-at) +configuration value as the default starting block number. + +- Type: `number` +- Required: `false` +- Default: `null` (_will use the value of the [deployed-at](#deployed-at) configuration value_) +- env: `WEBB_EVM__CONTRACTS__SYNC_BLOCKS_FROM` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { sync-blocks-from = 3123412 } +``` + +##### print-progress-interval + +The interval at which the relayer will print the progress of the syncing process. This is used to +show the user the progress of the syncing process which could be useful for debugging purposes. + +- Type: `number` +- Required: `false` +- Default: `30000ms` +- env: `WEBB_EVM__CONTRACTS__PRINT_PROGRESS_INTERVAL` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +events-watcher = { print-progress-interval = 60000 } +``` + +##### Proposal Signing Backend + +A Proposal Signing backend is used for signing proposals that the relayer will submit to be signed +and later executed on the target chain. Currently, there are two types of proposal signing backends, +the Mocked one, and the DKG based one. + +###### type + +The type of the proposal signing backend to use. + +- Type: `string` +- Required: `true` +- Possible values: + - `Mocked` + - `Dkg` +- env: `WEBB_EVM__CONTRACTS__PROPOSAL_SIGNING_BACKEND_TYPE` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +proposal-signing-backend = { type = "Mocked", private-key = "0x..." } +``` + +Let's take a look at each one of them. + +###### Mocked Proposal Signing Backend + +The mocked proposal signing backend is used for testing purposes, and it is mostly used for our +local and integration tests. This backend will sign the proposals with a mocked keypair; hence +should not be used in production environment. + +- Available configuration values: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +proposal-signing-backend = { type = "Mocked", private-key = "0x..." } +``` + +###### private-key + +The private key to use for signing the proposals. Only used by the mocked proposal signing backend. + +- Type: `string` +- Required: + - `true` if the [type](#type) is `Mocked` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__PROPOSAL_SIGNING_BACKEND_PRIVATE_KEY` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +proposal-signing-backend = { type = "Mocked", private-key = "0x..." } +``` + +###### Dkg Proposal Signing Backend + +The DKG proposal signing backend is used for signing the proposals using the DKG configured node. + +- Available configuration values: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +proposal-signing-backend = { type = "Dkg", chain-id = 1080 } +``` + +###### chain-id + +The node's chain-id to use for signing the proposals. Only used by the DKG proposal signing backend. + +- Type: `string` +- Required: + - `true` if the [type](#type) is `Dkg` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__PROPOSAL_SIGNING_BACKEND_CHAIN_ID` + +Example: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +proposal-signing-backend = { type = "Dkg", chain-id = 1080 } +``` + +##### Linked Anchors + +The Linked Anchors configuration is used to define the linked anchors for the VAnchor contract. This +configuration is only available when the [type](#type) is set to `VAnchor`. + +The configuration value is a list of Linked Anchors, defined in a human-readable format. However, +the relayer will convert them to a raw format before using them. + +- Required: `false` +- Default: `null` (defaults to an empty list) +- Available configuration values: + +```toml +[[evm.ethereum.contracts]] +contract = "VAnchor" +linked-anchors = [ + { type = "Evm", chain-id = 1, address = "0x..." }, + { type = "Substrate", chain-id = 1080, pallet = 42, tree-id = 4 }, + { type = "Raw", resource-id = "0x..." }, +] +``` + +###### type + +The type of the linked anchor definition. + +- Type: `string` +- Required: `true` +- Possible values: + - `Evm` + - `Substrate` + - `Raw` +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__TYPE` + +###### chain-id + +The chain-id of the linked anchor definition. Only used by the `Evm` and `Substrate` types. + +- Type: `number` +- Required: + - `true` if the [type](#type) is `Evm` or `Substrate` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__CHAIN_ID` + +Example: + +```toml +[[evm.ethereum.contracts]] +type = "VAnchor" +linked-anchors = [ + { type = "Evm", chain-id = 1, address = "0x..." }, + { type = "Substrate", chain-id = 1080, pallet = 42, tree-id = 4 }, +] +``` + +###### address + +The address of the linked anchor definition. Only used by the `Evm` type. + +- Type: `string` +- Required: + - `true` if the [type](#type) is `Evm` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__ADDRESS` + +Example: + +```toml +[[evm.ethereum.contracts]] +type = "VAnchor" +linked-anchors = [ + { type = "Evm", chain-id = 1, address = "0x..." }, +] +``` + +###### pallet + +The pallet of the linked anchor definition. Only used by the `Substrate` type. + +- Type: `number` +- Required: + - `true` if the [type](#type) is `Substrate` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__PALLET` + +Example: + +```toml +[[evm.ethereum.contracts]] +type = "VAnchor" +linked-anchors = [ + { type = "Substrate", chain-id = 1080, pallet = 42, tree-id = 4 }, +] +``` + +###### tree-id + +The tree-id of the linked anchor definition. Only used by the `Substrate` type. + +- Type: `number` +- Required: + - `true` if the [type](#type) is `Substrate` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__TREE_ID` + +Example: + +```toml +[[evm.ethereum.contracts]] +type = "VAnchor" +linked-anchors = [ + { type = "Substrate", chain-id = 1080, pallet = 42, tree-id = 4 }, +] +``` + +###### resource-id + +The resource-id of the linked anchor definition. Only used by the `Raw` type. + +- Type: `string` +- Required: + - `true` if the [type](#type) is `Raw` + - `false` otherwise +- env: `WEBB_EVM__CONTRACTS__LINKED_ANCHORS__RESOURCE_ID` + +Example: + +```toml +[[evm.ethereum.contracts]] +type = "VAnchor" +linked-anchors = [ + { type = "Raw", resource-id = "0x..." }, +] +``` + +### Substrate Node Configuration + +The Substrate Node configuration file is used to specify the configuration settings required for the +relayer to work with a specific Substrate Node. The file is typically located in the `config` +directory and named `substrate/.toml`. + +The configuration value is a table, and its name corresponds to the name of the node. For example: + +```toml +[substrate.tangle] +chain-id = 1080 +name = "tangle" +# ... +``` + +In general, the configuration table for a node is identified as `[substrate.]`, where +`` is the name of the network. The following sections outline the different configuration +entries and how to use them. + +#### name + +The name of the Substrate node. + +- Type: `string` +- Required: `true` +- env: `WEBB_SUBSTRATE__NAME` + +Example: + +```toml +[substrate.tangle] +name = "tangle" +``` + +#### chain-id + +The chain-id of the Substrate node. + +- Type: `number` +- Required: `true` +- env: `WEBB_SUBSTRATE__CHAIN_ID` + +Example: + +```toml +[substrate.tangle] +chain-id = 1080 +``` + +#### http-endpoint + +The RPC endpoint of the Substrate node. + +- Type: `string` +- Required: `true` +- env: `WEBB_SUBSTRATE__HTTP_ENDPOINT` + +Example: + +```toml +[substrate.tangle] +http-endpoint = "http://localhost:9933" +``` + +#### ws-endpoint + +The RPC WebSocket endpoint of the Substrate node. + +- Type: `string` +- Required: `true` +- env: `WEBB_SUBSTRATE__WS_ENDPOINT` + +Example: + +```toml +[substrate.tangle] +ws-endpoint = "ws://localhost:9944" +``` + +#### runtime + +The runtime of the running substrate node. These are predefined in the relayer, with each having +different types of Options. + +- Type: `string` +- Required: `true` +- env: `WEBB_SUBSTRATE__RUNTIME` +- Possible values: + - `DKG` (that is, `dkg-substrate`) + - `WebbProtocol` (also known as `protocol-substrate`) + +Example: + +```toml +[substrate.tangle] +runtime = "WebbProtocol" +``` + +#### enabled + +Whether the Substrate node is enabled or not. If it is not enabled, the relayer will not try to add +it to the list of available nodes. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_SUBSTRATE__ENABLED` + +Example: + +```toml +[substrate.tangle] +enabled = true +``` + +#### explorer + +The explorer of the Substrate Network. This is used to display clickable links to the explorer in +the logs. + +- Type: `string` +- Required: `false` +- Default: `null` +- env: `WEBB_SUBSTRATE__EXPLORER` + +Example: + +```toml +[substrate.tangle] +explorer = "https://tangle-explorer.webb.tools" +``` + +#### suri + +SURI stands for "secret URI". It is a mnemonic phrase that can be used to generate a private key. +This is used to sign extrinsics. We will refer to this as the "s" for now. The value is a string +(`s`) that we will try to interpret the string in order to generate a key pair. In the case that the +pair can be expressed as a direct derivation from a seed (some cases, such as Sr25519 derivations +with path components cannot). + +This takes a helper function to do the key generation from a phrase, password and junction iterator. + +- If `s` begins with a `$` character it is interpreted as an environment variable. +- If `s` is a possibly `0x` prefixed 64-digit hex string, then it will be interpreted directly as a + `MiniSecretKey` (aka "seed" in `subkey`). +- If `s` is a valid BIP-39 key phrase of 12, 15, 18, 21 or 24 words, then the key will be derived + from it. In this case: + - the phrase may be followed by one or more items delimited by `/` characters. + - the path may be followed by `///`, in which case everything after the `///` is treated as a + password. +- If `s` begins with a `/` character it is prefixed with the Substrate public `DEV_PHRASE` and + interpreted as above. + +In this case they are interpreted as HDKD junctions; purely numeric items are interpreted as +integers, non-numeric items as strings. Junctions prefixed with `/` are interpreted as soft +junctions, and with `//` as hard junctions. + +There is no correspondence mapping between SURI strings and the keys they represent. Two different +non-identical strings can actually lead to the same secret being derived. Notably, integer junction +indices may be legally prefixed with arbitrary number of zeros. Similarly an empty password (ending +the SURI with `///`) is perfectly valid and will generally be equivalent to no password at all. + +The value of this string could also start with `$` to indicate that it is an environment variable, +in which case the value of the environment variable will be used. + +> **Warning**: This is a sensitive value, and should be kept secret. It is recommended to use an +> environment variable to store the value of this string. + +- Type: `string` +- Required: `true` +- env: `WEBB_SUBSTRATE__SURI` + +Example: + +```toml +[substrate.tangle] +suri = "$TANGLE_SURI" +``` + +#### beneficiary + +The beneficiary is the address that will receive the fees from the transactions. This is optional, +and will default to the address derived from the `suri` if not provided. + +- Type: `string` +- Required: `false` +- Default: `null` +- env: `WEBB_SUBSTRATE__BENEFICIARY` + +Example: + +```toml +[substrate.tangle] +beneficiary = "5FZ2Wfjy5rZ7g5j7Y9Zwv5Z4Z3Z9Z9Z9Z9Z9Z9Z9Z9Z9Z9Z9Z9" +``` + +#### Tx Queue + +The transaction queue is a queue of transactions that are waiting to be sent to the Substrate node. + +##### max-sleep-interval + +The maximum sleep interval between sending transactions to the Substrate node. + +- Type: `number` +- Required: `false` +- Default: `10000ms` +- env: `WEBB_SUBSTRATE__TRANSACTION_QUEUE_MAX_SLEEP_INTERVAL` + +Example: + +```toml +[substrate.tangle] +tx-queue = { max-sleep-interval = 10000 } +``` + +#### Pallets + +The pallets are the different pallets that are used by the relayer. Each will define its own +configuration which will eventually be used by the relayer to start a different service for each +pallet. + +There are currently 5 different pallets that are supported by the relayer: + +- `DKG` +- `DkgProposals` +- `DkgProposalHandler` +- `SignatureBridge` +- `VAnchorBn254` + +- Type: `table` +- Required: `false` +- Default: `null` (empty table) + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" + +[[substrate.tangle.pallets]] +pallet = "DkgProposals" +# ... +``` + +##### pallet + +The type of the pallet. This is used to determine which pallet to use. + +- Type: `string` +- Required: `true` +- Possible values: + - `DKG` + - `DkgProposals` + - `DkgProposalHandler` + - `SignatureBridge` + - `VAnchorBn254` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +``` + +##### events-watcher + +The events watcher is used to watch for events emitted by the pallet. + +- Type: `table` +- Required: `false` +- Default: `null` (empty table) + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true } +``` + +###### enabled + +Whether the events watcher is enabled or not. If it is not enabled, the relayer will not try to +watch events emitted by the pallet. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_ENABLED` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true } +``` + +##### enable-data-query + +Whether the data query is enabled or not. If it is not enabled, the relayer will not try to query +the data from the pallet. + +- Type: `bool` +- Required: `false` +- Default: `true` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_ENABLE_DATA_QUERY` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true, enable-data-query = true } +``` + +##### polling-interval + +The polling interval is the interval at which the relayer will poll the Substrate node for new +blocks. + +- Type: `number` +- Required: `false` +- Default: `6000ms` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_POLLING_INTERVAL` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true, polling-interval = 6000 } +``` + +##### max-blocks-per-step + +The maximum number of blocks to process per step. + +- Type: `number` +- Required: `false` +- Default: `100` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_MAX_BLOCKS_PER_STEP` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true, max-blocks-per-step = 100 } +``` + +##### sync-blocks-from + +The block number from which to start syncing events from. This is useful if you want to start the +relayer from a specific block instead of block zero. + +- Type: `number` +- Required: `false` +- Default: `0` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_SYNC_BLOCKS_FROM` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true, sync-blocks-from = 42069 } +``` + +##### print-progress-interval + +The interval at which the relayer will print the progress of the syncing process. Useful for +debugging. + +- Type: `number` +- Required: `false` +- Default: `12000ms` +- env: `WEBB_SUBSTRATE__PALLET__EVENTS_WATCHER_PRINT_PROGRESS_INTERVAL` + +Example: + +```toml +[[substrate.tangle.pallets]] +pallet = "DKG" +events-watcher = { enabled = true, print-progress-interval = 12000 } +``` diff --git a/config/block-header-relay/.env.example b/config/block-header-relay/.env.example new file mode 100644 index 000000000..c88fb37f3 --- /dev/null +++ b/config/block-header-relay/.env.example @@ -0,0 +1,11 @@ +GOERLI_HTTP_URL=https://example.com +GOERLI_WS_URL=wss://example.com +GOERLI_LIGHT_CLIENT_RPC_URL=https://example.com + +SEPOLIA_HTTP_URL=https://example.com +SEPOLIA_WS_URL=wss://example.com +SEPOLIA_LIGHT_CLIENT_RPC_URL=https://example.com + +ETHEREUM_MAINNET_HTTP_URL=https://example.com +ETHEREUM_MAINNET_WS_URL=ws://example.com +ETHEREUM_MAINNET_LIGHT_CLIENT_RPC_URL=https://example.com diff --git a/config/block-header-relay/eth2-networks/ethereum-mainnet.toml b/config/block-header-relay/eth2-networks/ethereum-mainnet.toml index 38a37c079..c6a9abd0a 100644 --- a/config/block-header-relay/eth2-networks/ethereum-mainnet.toml +++ b/config/block-header-relay/eth2-networks/ethereum-mainnet.toml @@ -1,14 +1,16 @@ [evm.mainnet] -enabled=false +enabled = false # The name that the chain is indexed on, for linkable anchors name = "ethereum-mainnet" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$ETHEREUM_MAINNET_HTTP_URL" # Websocket Endpoint for long living connections ws-endpoint = "$ETHEREUM_MAINNET_WS_URL" # chain specific id from evm opcode chain-id = 1 +block-confirmations = 10 + [evm.mainnet.block-poller] # The starting block to listen at. start-block = 15697112 @@ -19,4 +21,4 @@ max-blocks-per-step = 1 # The print progress interval. print-progress-interval = 60_000 # Light client RPC url -light-client-rpc-url = "$ETHEREUM_MAINNET_LIGHT_CLIENT_RPC_URL" \ No newline at end of file +light-client-rpc-url = "$ETHEREUM_MAINNET_LIGHT_CLIENT_RPC_URL" diff --git a/config/block-header-relay/eth2-networks/goerli.toml b/config/block-header-relay/eth2-networks/goerli.toml index ef7101764..67916266e 100644 --- a/config/block-header-relay/eth2-networks/goerli.toml +++ b/config/block-header-relay/eth2-networks/goerli.toml @@ -1,5 +1,5 @@ [evm.goerli] -enabled=true +enabled = true # The name that the chain is indexed on, for linkable anchors name = "goerli-testnet" # Http(s) Endpoint for quick Req/Res @@ -8,6 +8,7 @@ http-endpoint = "$GOERLI_HTTP_URL" ws-endpoint = "$GOERLI_WS_URL" # chain specific id from evm opcode chain-id = 5002 +block-confirmations = 10 [evm.goerli.block-poller] # The starting block to listen at. @@ -19,4 +20,4 @@ max-blocks-per-step = 1 # The print progress interval. print-progress-interval = 60_000 # Light client RPC url -light-client-rpc-url = "$GOERLI_LIGHT_CLIENT_RPC_URL" \ No newline at end of file +light-client-rpc-url = "$GOERLI_LIGHT_CLIENT_RPC_URL" diff --git a/config/exclusive-strategies/testnet-evm-bridge/main.toml b/config/block-header-relay/eth2-networks/main.toml similarity index 81% rename from config/exclusive-strategies/testnet-evm-bridge/main.toml rename to config/block-header-relay/eth2-networks/main.toml index 35864c7a8..c1383c114 100644 --- a/config/exclusive-strategies/testnet-evm-bridge/main.toml +++ b/config/block-header-relay/eth2-networks/main.toml @@ -5,4 +5,4 @@ port = 9955 [features] governance-relay = false data-query = true -private-tx-relay = true \ No newline at end of file +private-tx-relay = false diff --git a/config/block-header-relay/eth2-networks/sepolia.toml b/config/block-header-relay/eth2-networks/sepolia.toml index c166597d7..0f684ae48 100644 --- a/config/block-header-relay/eth2-networks/sepolia.toml +++ b/config/block-header-relay/eth2-networks/sepolia.toml @@ -1,13 +1,14 @@ [evm.sepolia] -enabled=true +enabled = true # The name that the chain is indexed on, for linkable anchors name = "sepolia-testnet" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$SEPOLIA_HTTP_URL" # Websocket Endpoint for long living connections ws-endpoint = "$SEPOLIA_WS_URL" # chain specific id from evm opcode chain-id = 11155111 +block-confirmations = 10 [evm.sepolia.block-poller] # The starting block to listen at. @@ -19,4 +20,4 @@ max-blocks-per-step = 1 # The print progress interval. print-progress-interval = 60_000 # Light client RPC url -light-client-rpc-url = "$GOERLI_LIGHT_CLIENT_RPC_URL" \ No newline at end of file +light-client-rpc-url = "$GOERLI_LIGHT_CLIENT_RPC_URL" diff --git a/config/development/.env.example b/config/development/.env.example new file mode 100644 index 000000000..53c89675a --- /dev/null +++ b/config/development/.env.example @@ -0,0 +1,8 @@ + +PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 +HERMES_PRIVATE_KEY=${PRIVATE_KEY} +ATHENA_PRIVATE_KEY=${PRIVATE_KEY} +DEMETER_PRIVATE_KEY=${PRIVATE_KEY} + +GOVERNOR_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 +ETHERSCAN_API_KEY=YI7KXQGB98XXXXZJ5595E6Q7ZH2PGU32BG7 \ No newline at end of file diff --git a/config/development/README.md b/config/development/README.md new file mode 100644 index 000000000..5876e9702 --- /dev/null +++ b/config/development/README.md @@ -0,0 +1,64 @@ +## Development Configuration + +This directory contains configuration for the development environment. + +## EVM Blanknet + +The EVM Blanknet is a private Ethereum network that is used for development and testing. It is +configured in the `evm-blanknet` directory. This configuration uses 3 chains [Athena, Hermes, and +Demeter] each chain contains the deplpyed contracts and the deployed Anchors will be linked to +each other. This will use the Mocked Signing Backend for signing proposals. + +## EVM Local <> Tangle + +The EVM Local <> Tangle is a private Ethereum network that is used for development and testing. It +is configured in the `evm-local-tangle` directory. This configuration starts a 3 chains [Athena, +Hermes, and Demeter] each chain will contain the deployed contracts and the deployed Anchors will be +linked to each other. in addition to that, there is a configured Tangle network that will be used +for signing proposals. + +## Substrate Local + +The Substrate Local is a private Substrate network that is used for development and testing. It is +configured in the `substrate-local` directory. This configuration will try to connect to a local +running Substrate node that uses the Webb Protocol (protocol-substrate) Runtime; for easier testing, +you could also use the tangle node for that. + +### Running the Development Environment + +To run the development environment, you need to have the following installed: + +- [Node.js](https://nodejs.org/en/download/) +- [Rust](https://www.rustup.rs/) + +Then, you can run the following commands: + +```bash +cp config/development//.env.example .env +``` + +You will also need to follow these steps: + +- [Running Local Bridge](https://github.com/webb-tools/webb-dapp/tree/develop/apps/bridge-dapp#run-local-webb-relayer-and-local-network-alongside-hubble-bridge) +- [Local Standalone Tangle Network\*](https://github.com/webb-tools/tangle/tree/main/scripts#run-a-standalone-tangle-network) + +* Note: The Tangle Network is only Required for the EVM Local <> Tangle configuration and is not + required for the EVM Blanknet configuration. + +After that, you can run the relayer: + +```bash +cargo run --bin webb-relayer -- -vvv --tmp --config config/development/ +``` + +## Production Configuration + +This Repository does not contain any production ready configuration, but they are all examples and +for demonstration purposes only. + +However, you can take a look at the following configurations: + +1. [exclusive-startegies](../exclusive-strategies/) - These are a set of configurations that are + used for the exclusive strategies of different roles that you can use the relayer for. +2. [example](../example/) - This is a very simple and minimal configuration for starting a Private + Transaction Relayer over the Gorli Testnet. diff --git a/config/development/evm-blanknet/athena.toml b/config/development/evm-blanknet/athena.toml index 0c715ab7b..abe9d6679 100644 --- a/config/development/evm-blanknet/athena.toml +++ b/config/development/evm-blanknet/athena.toml @@ -2,10 +2,10 @@ [evm.athena] # The name that the chain is indexed on, for linkable anchors name = "athena" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ATHENA_HTTP_URL" +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. +http-endpoint = "http://localhost:5002" # Websocket Endpoint for long living connections -ws-endpoint = "$ATHENA_WS_URL" +ws-endpoint = "ws://localhost:5002" # chain specific id from evm opcode chain-id = 5002 # The Private Key of this account on this network @@ -51,7 +51,7 @@ events-watcher = { enabled = true, polling-interval = 1000, print-progress-inter # These fields are used to determine the generation of AnchorUpdate proposals linked-anchors = [ { type = "Evm", chain = "hermes", chain-id = 5001, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, + { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, ] # Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. @@ -59,7 +59,6 @@ linked-anchors = [ # the signed proposals to the configured SignatureBridge of the relevant chains. proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -# proposal-signing-backend = { type = "DKGNode", node = "5" } [[evm.athena.contracts]] contract = "SignatureBridge" diff --git a/config/development/evm-blanknet/demeter.toml b/config/development/evm-blanknet/demeter.toml index 2fbade435..1209e397f 100644 --- a/config/development/evm-blanknet/demeter.toml +++ b/config/development/evm-blanknet/demeter.toml @@ -2,10 +2,10 @@ [evm.demeter] # The name that the chain is indexed on, for linkable anchors name = "demeter" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$DEMETER_HTTP_URL" +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. +http-endpoint = "http://localhost:5003" # Websocket Endpoint for long living connections -ws-endpoint = "$DEMETER_WS_URL" +ws-endpoint = "ws://localhost:5003" # chain specific id from evm opcode chain-id = 5003 # The Private Key of this account on this network @@ -57,7 +57,6 @@ linked-anchors = [ # the signed proposals to the configured SignatureBridge of the relevant chains. proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -# proposal-signing-backend = { type = "DKGNode", node = "5" } [[evm.demeter.contracts]] contract = "SignatureBridge" diff --git a/config/development/evm-blanknet/hermes.toml b/config/development/evm-blanknet/hermes.toml index 6e84e23be..d23843ad9 100644 --- a/config/development/evm-blanknet/hermes.toml +++ b/config/development/evm-blanknet/hermes.toml @@ -2,10 +2,10 @@ [evm.hermes] # The name that the chain is indexed on, for linkable anchors name = "hermes" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$HERMES_HTTP_URL" +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. +http-endpoint = "http://localhost:5001" # Websocket Endpoint for long living connections -ws-endpoint = "$HERMES_WS_URL" +ws-endpoint = "ws://localhost:5001" # chain specific id from evm opcode chain-id = 5001 # The Private Key of this account on this network @@ -50,14 +50,13 @@ events-watcher = { enabled = true, polling-interval = 1000, print-progress-inter # These fields are used to determine the generation of AnchorUpdate proposals linked-anchors = [ { type = "Evm", chain = "athena", chain-id = 5002, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, + { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, ] # Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. # When the type is "Mocked", the supplied private-key will sign any proposed updates and submit # the signed proposals to the configured SignatureBridge of the relevant chains. proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -#proposal-signing-backend = { type = "DKGNode", node = "5" } [[evm.hermes.contracts]] contract = "SignatureBridge" diff --git a/config/development/evm-blanknet/main.toml b/config/development/evm-blanknet/main.toml index 1e2e1792b..4fc8de9aa 100644 --- a/config/development/evm-blanknet/main.toml +++ b/config/development/evm-blanknet/main.toml @@ -3,3 +3,7 @@ port = 9955 [experimental] smart-anchor-updates = false smart-anchor-updates-retries = 3 + +[evm-etherscan.mainnet] +chain-id = 5001 +api-key = "$ETHERSCAN_API_KEY" \ No newline at end of file diff --git a/config/development/evm-local-tangle/.env.example b/config/development/evm-local-tangle/.env.example deleted file mode 100644 index 54f86e742..000000000 --- a/config/development/evm-local-tangle/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -HERMES_HTTP_URL=http://localhost:5001 -HERMES_WS_URL=ws://localhost:5001 -ATHENA_HTTP_URL=http://localhost:5002 -ATHENA_WS_URL=ws://localhost:5002 -DEMETER_HTTP_URL=http://localhost:5003 -DEMETER_WS_URL=ws://localhost:5003 - -HERMES_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -ATHENA_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -DEMETER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 - -GOVERNOR_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 - -WEBB_SUBSTRATE_LOCALDKG_ENABLED=true -LOCAL_DKG_HTTP_URL=http://localhost:9933 -LOCAL_DKG_WS_URL=ws://localhost:9944 -LOCAL_DKG_SURI=//Alice diff --git a/config/development/evm-local-tangle/athena.json b/config/development/evm-local-tangle/athena.json new file mode 100644 index 000000000..6df21d604 --- /dev/null +++ b/config/development/evm-local-tangle/athena.json @@ -0,0 +1,55 @@ +{ + "evm": { + "athena": { + "name": "athena", + "http-endpoint": "http://localhost:5002", + "ws-endpoint": "ws://localhost:5002", + "chain-id": 5002, + "private-key": "$ATHENA_PRIVATE_KEY", + "tx-queue": { + "max-sleep-interval": 1500 + }, + "enabled": true, + "contracts": [ + { + "contract": "VAnchor", + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 1000, + "print-progress-interval": 60000 + }, + "linked-anchors": [ + { + "type": "Evm", + "chain": "hermes", + "chain-id": 5001, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + }, + { + "type": "Evm", + "chain": "demeter", + "chain-id": 5003, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + } + ], + "proposal-signing-backend": { + "type": "DKGNode", + "chain-id": 1080 + } + }, + { + "contract": "SignatureBridge", + "address": "0xf2e246bb76df876cef8b38ae84130f4f55de395b", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 1000, + "print-progress-interval": 60000 + } + } + ] + } + } +} diff --git a/config/development/evm-local-tangle/athena.toml b/config/development/evm-local-tangle/athena.toml deleted file mode 100644 index 1c3c487b8..000000000 --- a/config/development/evm-local-tangle/athena.toml +++ /dev/null @@ -1,75 +0,0 @@ -# Block which represents properties for a network -[evm.athena] -# The name that the chain is indexed on, for linkable anchors -name = "athena" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ATHENA_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$ATHENA_WS_URL" -# chain specific id from evm opcode -chain-id = 5002 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$ATHENA_PRIVATE_KEY" - -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.athena.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } -# Configuration related to withdraw (for private transaction relaying) -# - withdraw-gasLimit: Value which specifies the maximum amount of gas which will be used when -# submitting a withdraw transaction -# - withdraw-fee-percentage: Value which specifies the fees that this relayer will collect upon -# submitting a withdraw transaction -withdraw-config = { withdraw-fee-percentage = 0, withdraw-gaslimit = "0x350000" } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm",chain = "hermes", chain-id = 5001, address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, -] - -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -# proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "1080" } - -[[evm.athena.contracts]] -contract = "SignatureBridge" -address = "0x51a240271AB8AB9f9a21C82d9a85396b704E164d" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } diff --git a/config/development/evm-local-tangle/demeter.json b/config/development/evm-local-tangle/demeter.json new file mode 100644 index 000000000..793df16ef --- /dev/null +++ b/config/development/evm-local-tangle/demeter.json @@ -0,0 +1,55 @@ +{ + "evm": { + "demeter": { + "name": "demeter", + "http-endpoint": "http://localhost:5003", + "ws-endpoint": "ws://localhost:5003", + "chain-id": 5003, + "private-key": "$DEMETER_PRIVATE_KEY", + "tx-queue": { + "max-sleep-interval": 1500 + }, + "enabled": true, + "contracts": [ + { + "contract": "VAnchor", + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 10000, + "print-progress-interval": 0 + }, + "linked-anchors": [ + { + "type": "Evm", + "chain": "hermes", + "chain-id": 5001, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + }, + { + "type": "Evm", + "chain": "athena", + "chain-id": 5002, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + } + ], + "proposal-signing-backend": { + "type": "DKGNode", + "chain-id": 1080 + } + }, + { + "contract": "SignatureBridge", + "address": "0xf2e246bb76df876cef8b38ae84130f4f55de395b", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 10000, + "print-progress-interval": 0 + } + } + ] + } + } +} diff --git a/config/development/evm-local-tangle/demeter.toml b/config/development/evm-local-tangle/demeter.toml deleted file mode 100644 index a3cde8871..000000000 --- a/config/development/evm-local-tangle/demeter.toml +++ /dev/null @@ -1,68 +0,0 @@ -# Block which represents properties for a network -[evm.demeter] -# The name that the chain is indexed on, for linkable anchors -name = "demeter" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$DEMETER_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$DEMETER_WS_URL" -# chain specific id from evm opcode -chain-id = 5003 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$DEMETER_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.demeter.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } -withdraw-config = { withdraw-fee-percentage = 0, withdraw-gaslimit = "0x350000" } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "hermes", chain-id = 5001, address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { type = "Evm", chain = "athena", chain-id = 5002, address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3"}, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -# proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "1080" } - -[[evm.demeter.contracts]] -contract = "SignatureBridge" -address = "0x2946259E0334f33A064106302415aD3391BeD384" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } diff --git a/config/development/evm-local-tangle/hermes.json b/config/development/evm-local-tangle/hermes.json new file mode 100644 index 000000000..7afff8797 --- /dev/null +++ b/config/development/evm-local-tangle/hermes.json @@ -0,0 +1,55 @@ +{ + "evm": { + "hermes": { + "name": "hermes", + "http-endpoint": "http://localhost:5001", + "ws-endpoint": "ws://localhost:5001", + "chain-id": 5001, + "private-key": "$HERMES_PRIVATE_KEY", + "tx-queue": { + "max-sleep-interval": 1500 + }, + "enabled": true, + "contracts": [ + { + "contract": "VAnchor", + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 1000, + "print-progress-interval": 60000 + }, + "linked-anchors": [ + { + "type": "Evm", + "chain": "athena", + "chain-id": 5002, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + }, + { + "type": "Evm", + "chain": "demeter", + "chain-id": 5003, + "address": "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" + } + ], + "proposal-signing-backend": { + "type": "DKGNode", + "chain-id": 1080 + } + }, + { + "contract": "SignatureBridge", + "address": "0xf2e246bb76df876cef8b38ae84130f4f55de395b", + "deployed-at": 1, + "events-watcher": { + "enabled": true, + "polling-interval": 1000, + "print-progress-interval": 60000 + } + } + ] + } + } +} diff --git a/config/development/evm-local-tangle/hermes.toml b/config/development/evm-local-tangle/hermes.toml deleted file mode 100644 index cc69a8cf2..000000000 --- a/config/development/evm-local-tangle/hermes.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.hermes] -# The name that the chain is indexed on, for linkable anchors -name = "hermes" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$HERMES_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$HERMES_WS_URL" -# chain specific id from evm opcode -chain-id = 5001 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$HERMES_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.hermes.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xc705034ded85e817b9E56C977E61A2098362898B" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } -# Configuration related to withdraw (for private transaction relaying) -# - withdraw-gasLimit: Value which specifies the maximum amount of gas which will be used when -# submitting a withdraw transaction -# - withdraw-fee-percentage: Value which specifies the fees that this relayer will collect upon -# submitting a withdraw transaction -withdraw-config = { withdraw-fee-percentage = 0, withdraw-gaslimit = "0x350000" } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "athena", chain-id = 5002, address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -#proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "1080" } - -[[evm.hermes.contracts]] -contract = "SignatureBridge" -address = "0xDe09E74d4888Bc4e65F589e8c13Bce9F71DdF4c7" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 30000 } diff --git a/config/development/evm-local-tangle/main.toml b/config/development/evm-local-tangle/main.toml index 01bdd9f03..c6b8b6a65 100644 --- a/config/development/evm-local-tangle/main.toml +++ b/config/development/evm-local-tangle/main.toml @@ -3,8 +3,12 @@ port = 9955 [experimental] smart-anchor-updates = false smart-anchor-updates-retries = 3 +# etherscan api key +[evm-etherscan.mainnet] +chain-id = 5001 +api-key = "$ETHERSCAN_API_KEY" -[features] -governance-relay = true -data-query = true -private-tx-relay = true \ No newline at end of file +# The following is the default configuration for Unlisted Assets +[assets] +tTNT = { name = "Test Tangle Token", decimals = 18, price = 0.10 } +TNT = { name = "Tangle Token", decimals = 18, price = 0.10 } diff --git a/config/development/evm-local-tangle/tangle.toml b/config/development/evm-local-tangle/tangle.toml index 06b40860d..a3f71e392 100644 --- a/config/development/evm-local-tangle/tangle.toml +++ b/config/development/evm-local-tangle/tangle.toml @@ -10,6 +10,7 @@ suri = "//Bob" [[substrate.tangle.pallets]] pallet = "DKG" events-watcher = { enabled = true, polling-interval = 3000, print-progress-interval = 30000 } + [[substrate.tangle.pallets]] pallet = "DKGProposalHandler" events-watcher = { enabled = true, polling-interval = 3000, print-progress-interval = 30000 } diff --git a/config/development/evm-localdkg/.env.example b/config/development/evm-localdkg/.env.example deleted file mode 100644 index 54f86e742..000000000 --- a/config/development/evm-localdkg/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -HERMES_HTTP_URL=http://localhost:5001 -HERMES_WS_URL=ws://localhost:5001 -ATHENA_HTTP_URL=http://localhost:5002 -ATHENA_WS_URL=ws://localhost:5002 -DEMETER_HTTP_URL=http://localhost:5003 -DEMETER_WS_URL=ws://localhost:5003 - -HERMES_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -ATHENA_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -DEMETER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 - -GOVERNOR_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 - -WEBB_SUBSTRATE_LOCALDKG_ENABLED=true -LOCAL_DKG_HTTP_URL=http://localhost:9933 -LOCAL_DKG_WS_URL=ws://localhost:9944 -LOCAL_DKG_SURI=//Alice diff --git a/config/development/evm-localdkg/athena.toml b/config/development/evm-localdkg/athena.toml deleted file mode 100644 index 9ee4ca4cf..000000000 --- a/config/development/evm-localdkg/athena.toml +++ /dev/null @@ -1,68 +0,0 @@ -# Block which represents properties for a network -[evm.athena] -# The name that the chain is indexed on, for linkable anchors -name = "athena" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ATHENA_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$ATHENA_WS_URL" -# chain specific id from evm opcode -chain-id = 5002 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$ATHENA_PRIVATE_KEY" - -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.athena.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "hermes", chain-id = "5001", address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { chain = "demeter", chain-id = "5003", address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, -] - -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -# proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.athena.contracts]] -contract = "SignatureBridge" -address = "0x51a240271AB8AB9f9a21C82d9a85396b704E164d" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } diff --git a/config/development/evm-localdkg/demeter.toml b/config/development/evm-localdkg/demeter.toml deleted file mode 100644 index 9f48dfc8e..000000000 --- a/config/development/evm-localdkg/demeter.toml +++ /dev/null @@ -1,66 +0,0 @@ -# Block which represents properties for a network -[evm.demeter] -# The name that the chain is indexed on, for linkable anchors -name = "demeter" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$DEMETER_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$DEMETER_WS_URL" -# chain specific id from evm opcode -chain-id = 5003 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$DEMETER_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.demeter.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 0 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "hermes", chain-id = "5001", address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { chain = "athena", chain-id = "5002", address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3"}, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -# proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.demeter.contracts]] -contract = "SignatureBridge" -address = "0x2946259E0334f33A064106302415aD3391BeD384" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 0 } diff --git a/config/development/evm-localdkg/dkg.toml b/config/development/evm-localdkg/dkg.toml deleted file mode 100644 index 55fdc05cf..000000000 --- a/config/development/evm-localdkg/dkg.toml +++ /dev/null @@ -1,15 +0,0 @@ -[substrate.localdkg] -name = "localdkg" -http-endpoint = "$LOCAL_DKG_HTTP_URL" -ws-endpoint = "$LOCAL_DKG_WS_URL" -chain-id = 5 -runtime = "DKG" -suri = "$LOCAL_DKG_SURI" - -[[substrate.localdkg.pallets]] -pallet = "DKGProposalHandler" -events-watcher = { enabled = true, polling-interval = 6000, print-progress-interval = 60000 } - -[[substrate.localdkg.pallets]] -pallet = "DKG" -events-watcher = { enabled = true, polling-interval = 6000, print-progress-interval = 60000 } diff --git a/config/development/evm-localdkg/hermes.toml b/config/development/evm-localdkg/hermes.toml deleted file mode 100644 index 68c54d811..000000000 --- a/config/development/evm-localdkg/hermes.toml +++ /dev/null @@ -1,66 +0,0 @@ -# Block which represents properties for a network -[evm.hermes] -# The name that the chain is indexed on, for linkable anchors -name = "hermes" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$HERMES_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$HERMES_WS_URL" -# chain specific id from evm opcode -chain-id = 5001 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$HERMES_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.hermes.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xc705034ded85e817b9E56C977E61A2098362898B" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "athena", chain-id = "5002", address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" }, - { chain = "demeter", chain-id = "5003", address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e"}, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -#proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.hermes.contracts]] -contract = "SignatureBridge" -address = "0xDe09E74d4888Bc4e65F589e8c13Bce9F71DdF4c7" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } diff --git a/config/development/evm-localdkg/main.toml b/config/development/evm-localdkg/main.toml deleted file mode 100644 index 1e2e1792b..000000000 --- a/config/development/evm-localdkg/main.toml +++ /dev/null @@ -1,5 +0,0 @@ -port = 9955 - -[experimental] -smart-anchor-updates = false -smart-anchor-updates-retries = 3 diff --git a/config/development/evm-localnet/.env.example b/config/development/evm-localnet/.env.example deleted file mode 100644 index 53763beb9..000000000 --- a/config/development/evm-localnet/.env.example +++ /dev/null @@ -1,12 +0,0 @@ -HERMES_HTTP_URL=http://localhost:5001 -HERMES_WS_URL=ws://localhost:5001 -ATHENA_HTTP_URL=http://localhost:5002 -ATHENA_WS_URL=ws://localhost:5002 -DEMETER_HTTP_URL=http://localhost:5003 -DEMETER_WS_URL=ws://localhost:5003 - -HERMES_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -ATHENA_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 -DEMETER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 - -GOVERNOR_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 diff --git a/config/development/evm-localnet/athena.toml b/config/development/evm-localnet/athena.toml deleted file mode 100644 index 18a1c97e7..000000000 --- a/config/development/evm-localnet/athena.toml +++ /dev/null @@ -1,69 +0,0 @@ -# Block which represents properties for a network -[evm.athena] -# The name that the chain is indexed on, for linkable anchors -name = "athena" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ATHENA_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$ATHENA_WS_URL" -# chain specific id from evm opcode -chain-id = 5002 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$ATHENA_PRIVATE_KEY" - -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.athena.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "hermes", chain-id = 5001, address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, -] - -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -# proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.athena.contracts]] -contract = "SignatureBridge" -address = "0x51a240271AB8AB9f9a21C82d9a85396b704E164d" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } diff --git a/config/development/evm-localnet/demeter.toml b/config/development/evm-localnet/demeter.toml deleted file mode 100644 index 65af0024c..000000000 --- a/config/development/evm-localnet/demeter.toml +++ /dev/null @@ -1,67 +0,0 @@ -# Block which represents properties for a network -[evm.demeter] -# The name that the chain is indexed on, for linkable anchors -name = "demeter" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$DEMETER_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$DEMETER_WS_URL" -# chain specific id from evm opcode -chain-id = 5003 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$DEMETER_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.demeter.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 0 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "hermes", chain-id = 5001, address = "0xc705034ded85e817b9E56C977E61A2098362898B" }, - { type = "Evm", chain = "athena", chain-id = 5002, address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" }, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -# proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.demeter.contracts]] -contract = "SignatureBridge" -address = "0x2946259E0334f33A064106302415aD3391BeD384" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 0 } diff --git a/config/development/evm-localnet/hermes.toml b/config/development/evm-localnet/hermes.toml deleted file mode 100644 index 308bb24e3..000000000 --- a/config/development/evm-localnet/hermes.toml +++ /dev/null @@ -1,67 +0,0 @@ -# Block which represents properties for a network -[evm.hermes] -# The name that the chain is indexed on, for linkable anchors -name = "hermes" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$HERMES_HTTP_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$HERMES_WS_URL" -# chain specific id from evm opcode -chain-id = 5001 -block-confirmations = 0 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$HERMES_PRIVATE_KEY" -tx-queue = { max-sleep-interval = 1500 } -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.hermes.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xc705034ded85e817b9E56C977E61A2098362898B" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 1 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "athena", chain-id = 5002, address = "0x91eB86019FD8D7c5a9E31143D422850A13F670A3" }, - { type = "Evm", chain = "demeter", chain-id = 5003, address = "0x6595b34ED0a270B10a586FC1EA22030A95386f1e" }, -] -# Proposal signing backend can have a value of "Mocked" or the name of the DKG configuration. -# When the type is "Mocked", the supplied private-key will sign any proposed updates and submit -# the signed proposals to the configured SignatureBridge of the relevant chains. - -proposal-signing-backend = { type = "Mocked", private-key = "$GOVERNOR_PRIVATE_KEY" } -#proposal-signing-backend = { type = "DKGNode", node = "5" } - -[[evm.hermes.contracts]] -contract = "SignatureBridge" -address = "0xDe09E74d4888Bc4e65F589e8c13Bce9F71DdF4c7" -deployed-at = 1 -events-watcher = { enabled = true, polling-interval = 1000, print-progress-interval = 60000 } diff --git a/config/development/evm-localnet/main.toml b/config/development/evm-localnet/main.toml deleted file mode 100644 index 1e2e1792b..000000000 --- a/config/development/evm-localnet/main.toml +++ /dev/null @@ -1,5 +0,0 @@ -port = 9955 - -[experimental] -smart-anchor-updates = false -smart-anchor-updates-retries = 3 diff --git a/config/development/local-substrate/substrate.toml b/config/development/substrate-local/substrate.toml similarity index 64% rename from config/development/local-substrate/substrate.toml rename to config/development/substrate-local/substrate.toml index e8cdd40ee..a84564ff5 100644 --- a/config/development/local-substrate/substrate.toml +++ b/config/development/substrate-local/substrate.toml @@ -1,12 +1,14 @@ port = 9955 -[substrate.localnode] -name="localnode" -enabled=true +[substrate.local] +name = "local" +chain-id = 1080 +enabled = true http-endpoint = "http://localhost:9933" ws-endpoint = "ws://localhost:9944" runtime = "WebbProtocol" suri = "//Alice//stash" -[[substrate.localnode.pallets]] + +[[substrate.local.pallets]] pallet = "VAnchorBn254" -events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 0 } +events-watcher = { enabled = true, polling-interval = 10000, print-progress-interval = 6000 } diff --git a/config/example/.env.example b/config/example/.env.example new file mode 100644 index 000000000..57c99677d --- /dev/null +++ b/config/example/.env.example @@ -0,0 +1,2 @@ +# Private Key that will be used cross different chains +PRIVATE_KEY="0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/config/example/README.md b/config/example/README.md new file mode 100644 index 000000000..0c639755a --- /dev/null +++ b/config/example/README.md @@ -0,0 +1,3 @@ +# Configration Examples + +This directory contains a very simple and minimal configuration for starting a Private Transaction Relayer over the Gorli Testnet. This configuration is used for testing and for demonstration purposes. diff --git a/config/example/config.toml b/config/example/config.toml new file mode 100644 index 000000000..a2c38659d --- /dev/null +++ b/config/example/config.toml @@ -0,0 +1,79 @@ +# The port that the Relayer API service will listen on +# +# default: 9955 +# env: WEBB_PORT +port = 9955 + +# Controls what features are enabled in the relayer system +[features] +# Enables the Governance Relayer Feature +# default: false +# env: WEBB_FEATURES_GOVERNANCE_RELAY +# See: https://docs.webb.tools/docs/relayer/overview/#data-proposing-and-signature-relaying +governance-relay = false +# Enables the Data Query Feature (Oracle) +# default: false +# env: WEBB_FEATURES_DATA_QUERY +# See: https://docs.webb.tools/docs/relayer/overview/#data-querying +data-query = true +# Enables the Private Transaction Relayer Feature +# default: false +# env: WEBB_FEATURES_PRIVATE_TX_RELAY +# See: https://docs.webb.tools/docs/relayer/overview/#private-transaction-relaying +private-tx-relay = true + +## The Next Section is where we define the networks and the contracts that the relayer will be interacting with. + +# The following block defines an EVM network (in this case, Goerli) that the relayer will connect to. +[evm.goerli] +name = "goerli" +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. +# env: WEBB_EVM_GOERLI_HTTP_ENDPOINT +http-endpoint = "https://rpc.ankr.com/eth_goerli" +# Websocket Endpoint for long living connections +# env: WEBB_EVM_GOERLI_WS_ENDPOINT +ws-endpoint = "wss://rpc.ankr.com/eth_goerli" +# chain specific id from evm opcode +# env: WEBB_EVM_GOERLI_CHAIN_ID +chain-id = 5 +# Value to indicate that the relayer should enable services for this chain +# default: true +# env: WEBB_EVM_GOERLI_ENABLED +enabled = true +# The number of block confirmations that the relayer should wait before processing a block +# default: 0 +# env: WEBB_EVM_GOERLI_BLOCK_CONFIRMATIONS +block-confirmations = 2 +# The Private Key of this account on this network +# the format is more dynamic here: +# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded +# private key. +# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 +# +# 2. if it starts with '$' then it would be considered as an Enviroment variable +# of a hex-encoded private key. +# Example: $GOERLI_PRIVATE_KEY +# +# 3. if it doesn't contains special characters and has 12 or 24 words in it +# then we should process it as a mnemonic string: 'word two three four ...' +# +# env: WEBB_EVM_GOERLI_PRIVATE_KEY +# Note: This value is optional if you are using the relayer as a data-query service only. +private-key = "$PRIVATE_KEY" + +# Block which represents the configuration for a supported contract on the network +[[evm.goerli.contracts]] +# The contract can be one of these values +# - VAnchor (Variable Anchor) +# - SignatureBridge +contract = "VAnchor" +# The address of the contract +address = "0x38e7aa90c77f86747fab355eecaa0c2e4c3a463d" +# The deployed block number of the contract. When a relayer does not have information for +# this contract in its store, it will start to sync and query for relevant historical data +# starting at this block number +deployed-at = 8703495 +# Configuration for the events watcher of this contract. The events-watcher can be switched on/off +# and the polling interval specifies the period of time (in ms) that the events-watcher thread +# will wait before issuing another query for new events. +events-watcher = { enabled = true, polling-interval = 15000 } diff --git a/config/exclusive-strategies/.env.example b/config/exclusive-strategies/.env.example new file mode 100644 index 000000000..d819995aa --- /dev/null +++ b/config/exclusive-strategies/.env.example @@ -0,0 +1,19 @@ +EXAMPLE_HTTPS_URL=https://example.com +EXAMPLE_WSS_URL=wss://example.com +EXAMPLE_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001 + +GOERLI_HTTPS_URL=${EXAMPLE_HTTPS_URL} +GOERLI_WSS_URL=${EXAMPLE_WSS_URL} +GOERLI_PRIVATE_KEY=${EXAMPLE_PRIVATE_KEY} + +SEPOLIA_HTTPS_URL=${EXAMPLE_HTTPS_URL} +SEPOLIA_WSS_URL=${EXAMPLE_WSS_URL} +SEPOLIA_PRIVATE_KEY=${EXAMPLE_PRIVATE_KEY} + +OPTIMISM_TESTNET_HTTPS_URL=${EXAMPLE_HTTPS_URL} +OPTIMISM_TESTNET_WSS_URL=${EXAMPLE_WSS_URL} +OPTIMISM_TESTNET_PRIVATE_KEY=${EXAMPLE_PRIVATE_KEY} + + +ETHERSCAN_API_KEY=YI7KXQGB98XXXXZJ5595E6Q7ZH2PGU32BG7 +MOCKED_BACKEND_KEY=${EXAMPLE_PRIVATE_KEY} diff --git a/config/exclusive-strategies/data-querying/.env.example b/config/exclusive-strategies/data-querying/.env.example deleted file mode 100644 index feb41f6b6..000000000 --- a/config/exclusive-strategies/data-querying/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= diff --git a/config/exclusive-strategies/data-querying/goerli.toml b/config/exclusive-strategies/data-querying/goerli.toml index bfd4500e8..ab1f3a468 100644 --- a/config/exclusive-strategies/data-querying/goerli.toml +++ b/config/exclusive-strategies/data-querying/goerli.toml @@ -1,6 +1,6 @@ [evm.goerli] name = "goerli" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$GOERLI_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$GOERLI_WSS_URL" diff --git a/config/exclusive-strategies/data-querying/sepolia.toml b/config/exclusive-strategies/data-querying/sepolia.toml index 9168864d9..16cf15fb8 100644 --- a/config/exclusive-strategies/data-querying/sepolia.toml +++ b/config/exclusive-strategies/data-querying/sepolia.toml @@ -1,6 +1,6 @@ [evm.sepolia] name = "sepolia" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$SEPOLIA_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$SEPOLIA_WSS_URL" diff --git a/config/exclusive-strategies/eggnet-mixer/substrate.toml b/config/exclusive-strategies/eggnet-mixer/substrate.toml deleted file mode 100644 index c3f824e74..000000000 --- a/config/exclusive-strategies/eggnet-mixer/substrate.toml +++ /dev/null @@ -1,10 +0,0 @@ -port = 9955 - -[substrate.webbeggnet] -enabled=true -chain-id=1080 -http-endpoint = "https://standalone.webb.tools" -ws-endpoint = "wss://standalone.webb.tools" -runtime = "WebbProtocol" -suri = "//Alice" -pallets = [] \ No newline at end of file diff --git a/config/exclusive-strategies/private-tx-relaying/.env.example b/config/exclusive-strategies/private-tx-relaying/.env.example deleted file mode 100644 index bfe032d99..000000000 --- a/config/exclusive-strategies/private-tx-relaying/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= -SEPOLIA_PRIVATE_KEY= \ No newline at end of file diff --git a/config/exclusive-strategies/private-tx-relaying/goerli.toml b/config/exclusive-strategies/private-tx-relaying/goerli.toml index 3df2e0fa2..0d6a49429 100644 --- a/config/exclusive-strategies/private-tx-relaying/goerli.toml +++ b/config/exclusive-strategies/private-tx-relaying/goerli.toml @@ -1,7 +1,7 @@ # Block which represents properties for a network [evm.goerli] name = "goerli" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$GOERLI_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$GOERLI_WSS_URL" diff --git a/config/exclusive-strategies/private-tx-relaying/main.toml b/config/exclusive-strategies/private-tx-relaying/main.toml index 2282f25ce..803f2bb3b 100644 --- a/config/exclusive-strategies/private-tx-relaying/main.toml +++ b/config/exclusive-strategies/private-tx-relaying/main.toml @@ -5,4 +5,11 @@ port = 9955 [features] governance-relay = false data-query = false -private-tx-relay = true \ No newline at end of file +private-tx-relay = true + +[evm-etherscan.goerli] +chain-id = 5 +api-key = "$ETHERSCAN_API_KEY" +[evm-etherscan.sepolia] +chain-id = 11155111 +api-key = "$ETHERSCAN_API_KEY" \ No newline at end of file diff --git a/config/exclusive-strategies/private-tx-relaying/sepolia.toml b/config/exclusive-strategies/private-tx-relaying/sepolia.toml index 83c2d2dde..093b91245 100644 --- a/config/exclusive-strategies/private-tx-relaying/sepolia.toml +++ b/config/exclusive-strategies/private-tx-relaying/sepolia.toml @@ -1,7 +1,7 @@ # Block which represents properties for a network [evm.sepolia] name = "sepolia" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$SEPOLIA_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$SEPOLIA_WSS_URL" diff --git a/config/exclusive-strategies/signature-relaying/.env.example b/config/exclusive-strategies/signature-relaying/.env.example deleted file mode 100644 index 3b97c029f..000000000 --- a/config/exclusive-strategies/signature-relaying/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= -SEPOLIA_PRIVATE_KEY= - -OPTIMISM_TESTNET_HTTPS_URL= -OPTIMISM_TESTNET_WSS_URL= -OPTIMISM_TESTNET_PRIVATE_KEY= - -MOCKED_BACKEND_KEY= \ No newline at end of file diff --git a/config/exclusive-strategies/signature-relaying/goerli.toml b/config/exclusive-strategies/signature-relaying/goerli.toml index 18f151d04..bc5d29cf1 100644 --- a/config/exclusive-strategies/signature-relaying/goerli.toml +++ b/config/exclusive-strategies/signature-relaying/goerli.toml @@ -1,7 +1,7 @@ # Block which represents properties for a network [evm.goerli] name = "goerli" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$GOERLI_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$GOERLI_WSS_URL" diff --git a/config/exclusive-strategies/signature-relaying/optimism_test.toml b/config/exclusive-strategies/signature-relaying/optimism_test.toml index 851ad7929..d572be2d3 100644 --- a/config/exclusive-strategies/signature-relaying/optimism_test.toml +++ b/config/exclusive-strategies/signature-relaying/optimism_test.toml @@ -1,7 +1,7 @@ # Block which represents properties for a network [evm.optimismtestnet] name = "optimismtestnet" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$OPTIMISM_TESTNET_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$OPTIMISM_TESTNET_WSS_URL" diff --git a/config/exclusive-strategies/signature-relaying/sepolia.toml b/config/exclusive-strategies/signature-relaying/sepolia.toml index 2f4e2e92d..d72f316b7 100644 --- a/config/exclusive-strategies/signature-relaying/sepolia.toml +++ b/config/exclusive-strategies/signature-relaying/sepolia.toml @@ -1,7 +1,7 @@ # Block which represents properties for a network [evm.sepolia] name = "sepolia" -# Http(s) Endpoint for quick Req/Res +# Http(s) Endpoint for quick Req/Res. Input can be single http-endpoint or array of multiple http-endpoints. http-endpoint = "$SEPOLIA_HTTPS_URL" # Websocket Endpoint for long living connections ws-endpoint = "$SEPOLIA_WSS_URL" diff --git a/config/exclusive-strategies/testnet-evm-bridge/.env.example b/config/exclusive-strategies/testnet-evm-bridge/.env.example deleted file mode 100644 index 4225339ee..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/.env.example +++ /dev/null @@ -1,27 +0,0 @@ -ARBITRUM_TESTNET_HTTPS_URL= -ARBITRUM_TESTNET_WSS_URL= -ARBITRUM_TESTNET_PRIVATE_KEY= - -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -MOONBASE_HTTPS_URL= -MOONBASE_WSS_URL= -MOONBASE_PRIVATE_KEY= - -OPTIMISM_TESTNET_HTTPS_URL= -OPTIMISM_TESTNET_WSS_URL= -OPTIMISM_TESTNET_PRIVATE_KEY= - -POLYGON_TESTNET_HTTPS_URL= -POLYGON_TESTNET_WSS_URL= -POLYGON_TESTNET_PRIVATE_KEY= - -RINKEBY_HTTPS_URL= -RINKEBY_WSS_URL= -RINKEBY_PRIVATE_KEY= - -ROPSTEN_HTTPS_URL= -ROPSTEN_WSS_URL= -ROPSTEN_PRIVATE_KEY= diff --git a/config/exclusive-strategies/testnet-evm-bridge/arbitrum_test.toml b/config/exclusive-strategies/testnet-evm-bridge/arbitrum_test.toml deleted file mode 100644 index c506df090..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/arbitrum_test.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.arbitrumtestnet] -name = "arbitrumtestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ARBITRUM_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$ARBITRUM_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://rinkeby-explorer.arbitrum.io/#/" -# chain specific id from evm opcode -chain-id = 421613 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $ARBITRUM_TESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh arbitrumtestnet-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$ARBITRUM_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.arbitrumtestnet.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 171869 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" }, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] diff --git a/config/exclusive-strategies/testnet-evm-bridge/goerli.toml b/config/exclusive-strategies/testnet-evm-bridge/goerli.toml deleted file mode 100644 index 0805daad0..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/goerli.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.goerli] -name = "goerli" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$GOERLI_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$GOERLI_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://goerli.etherscan.io" -# chain specific id from evm opcode -chain-id = 5 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $GOERLI_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh goerli-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$GOERLI_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.goerli.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 7471990 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf"}, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] \ No newline at end of file diff --git a/config/exclusive-strategies/testnet-evm-bridge/moonbase.toml b/config/exclusive-strategies/testnet-evm-bridge/moonbase.toml deleted file mode 100644 index d2691539e..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/moonbase.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.moonbase] -name = "moonbase" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MOONBASE_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MOONBASE_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://moonbase.moonscan.io/" -# chain specific id from evm opcode -chain-id = 1287 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $MOONBASE_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh moonbase-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$MOONBASE_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.moonbase.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 2723239 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07"} -] diff --git a/config/exclusive-strategies/testnet-evm-bridge/optimism_test.toml b/config/exclusive-strategies/testnet-evm-bridge/optimism_test.toml deleted file mode 100644 index 9a8216ae0..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/optimism_test.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.optimismtestnet] -name = "optimismtestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$OPTIMISM_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$OPTIMISM_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://blockscout.com/optimism/goerli/" -# chain specific id from evm opcode -chain-id = 420 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $OPTIMISM_TESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh optimismtestnet-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$OPTIMISM_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.optimismtestnet.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 640396 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] diff --git a/config/exclusive-strategies/testnet-evm-bridge/polygon_test.toml b/config/exclusive-strategies/testnet-evm-bridge/polygon_test.toml deleted file mode 100644 index 1696df554..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/polygon_test.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.polygontestnet] -name = "polygontestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$POLYGON_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$POLYGON_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://mumbai.polygonscan.com/" -# chain specific id from evm opcode -chain-id = 80001 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $POLYGONTESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh polygontestnet-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$POLYGON_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.polygontestnet.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 27776591 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" }, - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] diff --git a/config/exclusive-strategies/testnet-evm-bridge/rinkeby.toml b/config/exclusive-strategies/testnet-evm-bridge/rinkeby.toml deleted file mode 100644 index 997478f1d..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/rinkeby.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.rinkeby] -name = "rinkeby" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$RINKEBY_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$RINKEBY_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://rinkeby.etherscan.io" -# chain specific id from evm opcode -chain-id = 4 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $RINKEBY_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh rinkeby-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$RINKEBY_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.rinkeby.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 11266630 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "ropsten", chain-id = "3", address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" }, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] diff --git a/config/exclusive-strategies/testnet-evm-bridge/ropsten.toml b/config/exclusive-strategies/testnet-evm-bridge/ropsten.toml deleted file mode 100644 index 001356983..000000000 --- a/config/exclusive-strategies/testnet-evm-bridge/ropsten.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Block which represents properties for a network -[evm.ropsten] -name = "ropsten" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$ROPSTEN_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$ROPSTEN_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://ropsten.etherscan.io" -# chain specific id from evm opcode -chain-id = 3 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $ROPSTEN_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh ropsten-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$ROPSTEN_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.ropsten.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x35295fbb71273b84f66e70b8e341d408150dcaf9" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 12864534 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { chain = "arbitrumtestnet", chain-id = "421613", address = "0x91a9a1e76fa609f6ba8fcd718a60b030678765ad" }, - { chain = "rinkeby", chain-id = "4", address = "0x7ae23a95881bf8ab86174e89bd79199f398d19bf" }, - { chain = "goerli", chain-id = "5", address = "0x4e22da303c403daaf4653d3d9d63ef009bae89a6" }, - { chain = "polygontestnet", chain-id = "80001", address = "0xe6b075ecc4ccbc6e66569b1a2984cc47e88ee246" }, - { chain = "optimismtestnet", chain-id = "420", address = "0x12f2c4a1469b035e4459539e38ae68bc4dd5ba07" }, - { chain = "moonbase", chain-id = "1287", address = "0xc6b43568f0c39e3a68b597a3bb54a7b9e4308bf3"} -] diff --git a/config/full-support/dkg-backend/.env.example b/config/full-support/dkg-backend/.env.example deleted file mode 100644 index ea4e9262a..000000000 --- a/config/full-support/dkg-backend/.env.example +++ /dev/null @@ -1,23 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -MOONBASE_HTTPS_URL= -MOONBASE_WSS_URL= -MOONBASE_PRIVATE_KEY= - -OPTIMISM_TESTNET_HTTPS_URL= -OPTIMISM_TESTNET_WSS_URL= -OPTIMISM_TESTNET_PRIVATE_KEY= - -MUMBAI_TESTNET_HTTPS_URL= -MUMBAI_TESTNET_WSS_URL= -MUMBAI_TESTNET_PRIVATE_KEY= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= -SEPOLIA_PRIVATE_KEY= - -MOCKED_BACKEND_KEY= - -TANGLE_WEBB_RELAYER_SURI= \ No newline at end of file diff --git a/config/full-support/dkg-backend/goerli.toml b/config/full-support/dkg-backend/goerli.toml deleted file mode 100644 index 527bd8e2d..000000000 --- a/config/full-support/dkg-backend/goerli.toml +++ /dev/null @@ -1,44 +0,0 @@ -# Block which represents properties for a network -[evm.goerli] -name = "goerli" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$GOERLI_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$GOERLI_WSS_URL" - -explorer = "https://goerli.etherscan.io" -# chain specific id from evm opcode -chain-id = 5 - -block-confirmations = 2 - -private-key = "$GOERLI_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.goerli.contracts]] -contract = "VAnchor" -# The address of the contract -address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" - -deployed-at = 8188267 - -events-watcher = { enabled = true, polling-interval = 15000 } - -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -# proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "tangle" } - -[[evm.goerli.contracts]] -contract = "SignatureBridge" -address = "0x773cf4c29a6f2239b9d6ef821d17ab1d705390b9" - -deployed-at = 8188232 - -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/dkg-backend/main.toml b/config/full-support/dkg-backend/main.toml deleted file mode 100644 index ed9dd37fb..000000000 --- a/config/full-support/dkg-backend/main.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Webb Relayer Network Port -# default: 9955 -port = 9955 - -[features] -governance-relay = true -data-query = true -private-tx-relay = true \ No newline at end of file diff --git a/config/full-support/dkg-backend/moonbase.toml b/config/full-support/dkg-backend/moonbase.toml deleted file mode 100644 index e0eb84717..000000000 --- a/config/full-support/dkg-backend/moonbase.toml +++ /dev/null @@ -1,45 +0,0 @@ -# Block which represents properties for a network -[evm.moonbase] -name = "moonbase" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MOONBASE_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MOONBASE_WSS_URL" - -explorer = "https://moonbase.moonscan.io/" -# chain specific id from evm opcode -chain-id = 1287 - -block-confirmations = 2 - -private-key = "$MOONBASE_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.moonbase.contracts]] - -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" - -deployed-at = 3418157 - -events-watcher = { enabled = true, polling-interval = 15000 } - -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f"} -] -# proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "tangle" } - -[[evm.moonbase.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" - -deployed-at = 3418117 - -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/dkg-backend/mumbai.toml b/config/full-support/dkg-backend/mumbai.toml deleted file mode 100644 index 0a3ca738d..000000000 --- a/config/full-support/dkg-backend/mumbai.toml +++ /dev/null @@ -1,45 +0,0 @@ -# Block which represents properties for a network -[evm.mumbai] -name = "mumbai" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MUMBAI_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MUMBAI_TESTNET_WSS_URL" - -explorer = "https://mumbai.polygonscan.com/" -# chain specific id from evm opcode -chain-id = 80001 - -block-confirmations = 2 - -private-key = "$MUMBAI_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.mumbai.contracts]] - -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" - -deployed-at = 30098018 - -events-watcher = { enabled = true, polling-interval = 15000 } - -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -# proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "tangle" } - -[[evm.mumbai.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" - -deployed-at = 30097933 - -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/dkg-backend/optimism_test.toml b/config/full-support/dkg-backend/optimism_test.toml deleted file mode 100644 index a99836b49..000000000 --- a/config/full-support/dkg-backend/optimism_test.toml +++ /dev/null @@ -1,49 +0,0 @@ -# Block which represents properties for a network -[evm.optimismtestnet] -name = "optimismtestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$OPTIMISM_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$OPTIMISM_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://blockscout.com/optimism/goerli/" -# chain specific id from evm opcode -chain-id = 420 - -block-confirmations = 2 - -private-key = "$OPTIMISM_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.optimismtestnet.contracts]] - -contract = "VAnchor" -# The address of the contract -address = "0x9d36b94f245857ec7280415140800dde7642addb" - -deployed-at = 3706371 - -events-watcher = { enabled = true, polling-interval = 15000 } - -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" } -] -# proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "tangle" } - -[[evm.optimismtestnet.contracts]] -contract = "SignatureBridge" -address = "0x7e627d29de9a9a6aa6e58163d5fdfd6fddfaa268" - -deployed-at = 3706273 - -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/dkg-backend/sepolia.toml b/config/full-support/dkg-backend/sepolia.toml deleted file mode 100644 index b7a05f6c7..000000000 --- a/config/full-support/dkg-backend/sepolia.toml +++ /dev/null @@ -1,45 +0,0 @@ -# Block which represents properties for a network -[evm.sepolia] -name = "sepolia" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$SEPOLIA_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$SEPOLIA_WSS_URL" - -explorer = "https://sepolia.etherscan.io" -# chain specific id from evm opcode -chain-id = 11155111 - -block-confirmations = 2 - -private-key = "$SEPOLIA_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.sepolia.contracts]] - -contract = "VAnchor" -# The address of the contract -address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" - -deployed-at = 2545802 - -events-watcher = { enabled = true, polling-interval = 15000 } - -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -# proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } -proposal-signing-backend = { type = "DKGNode", node = "tangle" } - -[[evm.sepolia.contracts]] -contract = "SignatureBridge" -address = "0xc99aea79d36227ff5760372f56d37f683954bc2a" - -deployed-at = 2478538 - -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/dkg-backend/substrate.toml b/config/full-support/dkg-backend/substrate.toml deleted file mode 100644 index 8cebdc951..000000000 --- a/config/full-support/dkg-backend/substrate.toml +++ /dev/null @@ -1,14 +0,0 @@ -[substrate.tangle] -name = "tangle" -enabled = true -http-endpoint = "http://tangle_parachain:9933" -ws-endpoint = "ws://tangle_parachain:9944" -runtime = "DKG" -suri = "$TANGLE_WEBB_RELAYER_SURI" -chain-id = "1080" -[[substrate.tangle.pallets]] -pallet = "DKG" -events-watcher = { enabled = false, polling-interval = 12000, print-progress-interval = 60000 } -[[substrate.tangle.pallets]] -pallet = "DKGProposalHandler" -events-watcher = { enabled = false, polling-interval = 12000, print-progress-interval = 60000 } \ No newline at end of file diff --git a/config/full-support/mocked-backend/.env.example b/config/full-support/mocked-backend/.env.example deleted file mode 100644 index 899115598..000000000 --- a/config/full-support/mocked-backend/.env.example +++ /dev/null @@ -1,21 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -MOONBASE_HTTPS_URL= -MOONBASE_WSS_URL= -MOONBASE_PRIVATE_KEY= - -OPTIMISM_TESTNET_HTTPS_URL= -OPTIMISM_TESTNET_WSS_URL= -OPTIMISM_TESTNET_PRIVATE_KEY= - -MUMBAI_TESTNET_HTTPS_URL= -MUMBAI_TESTNET_WSS_URL= -MUMBAI_TESTNET_PRIVATE_KEY= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= -SEPOLIA_PRIVATE_KEY= - -MOCKED_BACKEND_KEY= \ No newline at end of file diff --git a/config/full-support/mocked-backend/goerli.toml b/config/full-support/mocked-backend/goerli.toml deleted file mode 100644 index 031570ba5..000000000 --- a/config/full-support/mocked-backend/goerli.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.goerli] -name = "goerli" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$GOERLI_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$GOERLI_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://goerli.etherscan.io" -# chain specific id from evm opcode -chain-id = 5 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $GOERLI_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh goerli-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$GOERLI_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.goerli.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 8188267 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.goerli.contracts]] -contract = "SignatureBridge" -address = "0x773cf4c29a6f2239b9d6ef821d17ab1d705390b9" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 8188232 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/mocked-backend/main.toml b/config/full-support/mocked-backend/main.toml deleted file mode 100644 index ed9dd37fb..000000000 --- a/config/full-support/mocked-backend/main.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Webb Relayer Network Port -# default: 9955 -port = 9955 - -[features] -governance-relay = true -data-query = true -private-tx-relay = true \ No newline at end of file diff --git a/config/full-support/mocked-backend/moonbase.toml b/config/full-support/mocked-backend/moonbase.toml deleted file mode 100644 index f58bc07e5..000000000 --- a/config/full-support/mocked-backend/moonbase.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.moonbase] -name = "moonbase" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MOONBASE_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MOONBASE_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://moonbase.moonscan.io/" -# chain specific id from evm opcode -chain-id = 1287 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $MOONBASE_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh moonbase-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$MOONBASE_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.moonbase.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3418157 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.moonbase.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3418117 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/mocked-backend/mumbai.toml b/config/full-support/mocked-backend/mumbai.toml deleted file mode 100644 index 31737dec7..000000000 --- a/config/full-support/mocked-backend/mumbai.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.mumbai] -name = "mumbai" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MUMBAI_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MUMBAI_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://mumbai.polygonscan.com/" -# chain specific id from evm opcode -chain-id = 80001 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $POLYGONTESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh mumbai-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$MUMBAI_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.mumbai.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 30098018 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.mumbai.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 30097933 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/mocked-backend/optimism_test.toml b/config/full-support/mocked-backend/optimism_test.toml deleted file mode 100644 index e40edb947..000000000 --- a/config/full-support/mocked-backend/optimism_test.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.optimismtestnet] -name = "optimismtestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$OPTIMISM_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$OPTIMISM_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://blockscout.com/optimism/goerli/" -# chain specific id from evm opcode -chain-id = 420 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $OPTIMISM_TESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh optimismtestnet-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$OPTIMISM_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.optimismtestnet.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x9d36b94f245857ec7280415140800dde7642addb" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3706371 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.optimismtestnet.contracts]] -contract = "SignatureBridge" -address = "0x7e627d29de9a9a6aa6e58163d5fdfd6fddfaa268" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3706273 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/full-support/mocked-backend/sepolia.toml b/config/full-support/mocked-backend/sepolia.toml deleted file mode 100644 index 478f58109..000000000 --- a/config/full-support/mocked-backend/sepolia.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.sepolia] -name = "sepolia" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$SEPOLIA_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$SEPOLIA_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://sepolia.etherscan.io" -# chain specific id from evm opcode -chain-id = 11155111 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $SEPOLIA_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh ropsten-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$SEPOLIA_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.sepolia.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 2545802 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.sepolia.contracts]] -contract = "SignatureBridge" -address = "0xc99aea79d36227ff5760372f56d37f683954bc2a" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 2478538 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/.env.example b/config/mocked-bridge-deployments/testnet-evm-bridge/.env.example deleted file mode 100644 index 899115598..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/.env.example +++ /dev/null @@ -1,21 +0,0 @@ -GOERLI_HTTPS_URL= -GOERLI_WSS_URL= -GOERLI_PRIVATE_KEY= - -MOONBASE_HTTPS_URL= -MOONBASE_WSS_URL= -MOONBASE_PRIVATE_KEY= - -OPTIMISM_TESTNET_HTTPS_URL= -OPTIMISM_TESTNET_WSS_URL= -OPTIMISM_TESTNET_PRIVATE_KEY= - -MUMBAI_TESTNET_HTTPS_URL= -MUMBAI_TESTNET_WSS_URL= -MUMBAI_TESTNET_PRIVATE_KEY= - -SEPOLIA_HTTPS_URL= -SEPOLIA_WSS_URL= -SEPOLIA_PRIVATE_KEY= - -MOCKED_BACKEND_KEY= \ No newline at end of file diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/goerli.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/goerli.toml deleted file mode 100644 index 031570ba5..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/goerli.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.goerli] -name = "goerli" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$GOERLI_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$GOERLI_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://goerli.etherscan.io" -# chain specific id from evm opcode -chain-id = 5 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $GOERLI_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh goerli-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$GOERLI_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.goerli.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 8188267 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.goerli.contracts]] -contract = "SignatureBridge" -address = "0x773cf4c29a6f2239b9d6ef821d17ab1d705390b9" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 8188232 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/main.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/main.toml deleted file mode 100644 index ed9dd37fb..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/main.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Webb Relayer Network Port -# default: 9955 -port = 9955 - -[features] -governance-relay = true -data-query = true -private-tx-relay = true \ No newline at end of file diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/moonbase.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/moonbase.toml deleted file mode 100644 index f58bc07e5..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/moonbase.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.moonbase] -name = "moonbase" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MOONBASE_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MOONBASE_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://moonbase.moonscan.io/" -# chain specific id from evm opcode -chain-id = 1287 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $MOONBASE_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh moonbase-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$MOONBASE_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.moonbase.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3418157 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.moonbase.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3418117 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/mumbai.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/mumbai.toml deleted file mode 100644 index 31737dec7..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/mumbai.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.mumbai] -name = "mumbai" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$MUMBAI_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$MUMBAI_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://mumbai.polygonscan.com/" -# chain specific id from evm opcode -chain-id = 80001 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $POLYGONTESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh mumbai-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$MUMBAI_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.mumbai.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 30098018 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.mumbai.contracts]] -contract = "SignatureBridge" -address = "0x206b293e2bc9e81d6af554a4302529d58eef7de7" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 30097933 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/optimism_test.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/optimism_test.toml deleted file mode 100644 index e40edb947..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/optimism_test.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.optimismtestnet] -name = "optimismtestnet" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$OPTIMISM_TESTNET_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$OPTIMISM_TESTNET_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://blockscout.com/optimism/goerli/" -# chain specific id from evm opcode -chain-id = 420 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $OPTIMISM_TESTNET_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh optimismtestnet-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$OPTIMISM_TESTNET_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.optimismtestnet.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0x9d36b94f245857ec7280415140800dde7642addb" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3706371 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "sepolia", chain-id = 11155111, address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.optimismtestnet.contracts]] -contract = "SignatureBridge" -address = "0x7e627d29de9a9a6aa6e58163d5fdfd6fddfaa268" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 3706273 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/config/mocked-bridge-deployments/testnet-evm-bridge/sepolia.toml b/config/mocked-bridge-deployments/testnet-evm-bridge/sepolia.toml deleted file mode 100644 index 478f58109..000000000 --- a/config/mocked-bridge-deployments/testnet-evm-bridge/sepolia.toml +++ /dev/null @@ -1,73 +0,0 @@ -# Block which represents properties for a network -[evm.sepolia] -name = "sepolia" -# Http(s) Endpoint for quick Req/Res -http-endpoint = "$SEPOLIA_HTTPS_URL" -# Websocket Endpoint for long living connections -ws-endpoint = "$SEPOLIA_WSS_URL" -# Block Explorer -# Optinal, and used for generating clickable links -# for transactions that happen on this chain. -explorer = "https://sepolia.etherscan.io" -# chain specific id from evm opcode -chain-id = 11155111 - -block-confirmations = 2 -# The Private Key of this account on this network -# the format is more dynamic here: -# 1. if it starts with '0x' then this would be raw (64 bytes) hex encoded -# private key. -# Example: 0x8917174396171783496173419137618235192359106130478137647163400318 -# -# 2. if it starts with '$' then it would be considered as an Enviroment variable -# of a hex-encoded private key. -# Example: $SEPOLIA_PRIVATE_KEY -# -# 3. if it starts with '> ' then it would be considered as a command that -# the relayer would execute and the output of this command would be the -# hex encoded private key. -# Example: > ./getKey.sh ropsten-privatekey -# -# 4. if it doesn't contains special characters and has 12 or 24 words in it -# then we should process it as a mnemonic string: 'word two three four ...' -private-key = "$SEPOLIA_PRIVATE_KEY" -# Value to indicate that the relayer should enable services for this chain -enabled = true - -# Block which represents the configuration for a supported contract on the network -[[evm.sepolia.contracts]] -# The contract can be one of these values -# - VAnchor (Variable Anchor) -# - SignatureBridge -contract = "VAnchor" -# The address of the contract -address = "0xb2d1d8d651c53a00e13ea0a363aab575a6886391" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 2545802 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 15000 } -# Entries for this anchor contract's connected edges. -# These fields are used to determine the generation of AnchorUpdate proposals -linked-anchors = [ - { type = "Evm", chain = "goerli", chain-id = 5, address = "0x3a4233bf223622f6571b8543498a62b9e2a3b31f" }, - { type = "Evm", chain = "optimismtestnet", chain-id = 420, address = "0x9d36b94f245857ec7280415140800dde7642addb" }, - { type = "Evm", chain = "mumbai", chain-id = 80001, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026" }, - { type = "Evm", chain = "moonbase", chain-id = 1287, address = "0xda27349ee55e7c91e1b521ece4c3dcc390383026"} -] -proposal-signing-backend = { type = "Mocked", private-key = "$MOCKED_BACKEND_KEY" } - -[[evm.sepolia.contracts]] -contract = "SignatureBridge" -address = "0xc99aea79d36227ff5760372f56d37f683954bc2a" -# The deployed block number of the contract. When a relayer does not have information for -# this contract in its store, it will start to sync and query for relevant historical data -# starting at this block number -deployed-at = 2478538 -# Configuration for the events watcher of this contract. The events-watcher can be switched on/off -# and the polling interval specifies the period of time (in ms) that the events-watcher thread -# will wait before issuing another query for new events. -events-watcher = { enabled = true, polling-interval = 45000, print-progress-interval = 0 } diff --git a/crates/bridge-registry-backends/Cargo.toml b/crates/bridge-registry-backends/Cargo.toml new file mode 100644 index 000000000..98ab09d08 --- /dev/null +++ b/crates/bridge-registry-backends/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "webb-bridge-registry-backends" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +documentation = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } + +[dependencies] +webb-relayer-utils = { workspace = true } +webb-relayer-config = { workspace = true } + +async-trait = { workspace = true } +futures = { workspace = true } +tokio = { workspace = true } +hex = { workspace = true } +webb = { workspace = true } +# Used by ethers (but we need it to be vendored with the lib). +native-tls = { workspace = true, optional = true } +webb-proposals = { workspace = true } +ethereum-types = { workspace = true } +sp-core = { workspace = true } + +typed-builder = { workspace = true } +hex-literal = "0.3.4" + +[features] +default = ["std"] +std = [] diff --git a/crates/bridge-registry-backends/src/dkg.rs b/crates/bridge-registry-backends/src/dkg.rs new file mode 100644 index 000000000..81f8b0ae1 --- /dev/null +++ b/crates/bridge-registry-backends/src/dkg.rs @@ -0,0 +1,63 @@ +use webb::substrate::subxt::{OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::runtime_types::pallet_bridge_registry::types::BridgeMetadata; +use webb_proposals::ResourceId; + +pub struct DkgBridgeRegistryBackend { + client: OnlineClient, +} + +impl DkgBridgeRegistryBackend { + pub fn new(client: OnlineClient) -> Self { + Self { client } + } +} + +#[async_trait::async_trait] +impl super::BridgeRegistryBackend for DkgBridgeRegistryBackend { + async fn next_bridge_index(&self) -> webb_relayer_utils::Result { + let storage = RuntimeApi::storage().bridge_registry(); + let next_bridge_index = storage.next_bridge_index(); + Ok(self + .client + .storage() + .at(None) + .await? + .fetch(&next_bridge_index) + .await? + .unwrap_or(1)) + } + + async fn resource_to_bridge_index( + &self, + resource_id: &ResourceId, + ) -> Option { + let resource_id2 = webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::ResourceId(resource_id.0); + let storage = RuntimeApi::storage().bridge_registry(); + let resource_to_bridge_index = + storage.resource_to_bridge_index(resource_id2); + self.client + .storage() + .at(None) + .await + .ok()? + .fetch(&resource_to_bridge_index) + .await + .ok()? + } + + async fn bridges( + &self, + index: u32, + ) -> webb_relayer_utils::Result> { + let storage = RuntimeApi::storage().bridge_registry(); + let bridges = storage.bridges(index); + Ok(self + .client + .storage() + .at(None) + .await? + .fetch(&bridges) + .await?) + } +} diff --git a/crates/bridge-registry-backends/src/lib.rs b/crates/bridge-registry-backends/src/lib.rs new file mode 100644 index 000000000..4f3620f62 --- /dev/null +++ b/crates/bridge-registry-backends/src/lib.rs @@ -0,0 +1,99 @@ +// Copyright 2022 Webb Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![warn(missing_docs)] +//! # Relayer Bridge Registry Backends 🕸️ +//! +//! ## Overview +//! This crate contains the bridge registry backends for the relayer. +//! Bridge registry backends are used to retrieve the bridges and attached resources that are +//! configured in relayer. +//! There are two types of bridge backends: +//! - `MockedBridgeRegistryBackend`: This is a mocked backend that is used for testing purposes. +//! - `DKGBridgeRegistryBackend`: This is the actual backend that is used in production. + +use ethereum_types::H256; +use webb::substrate::tangle_runtime::api::runtime_types::pallet_bridge_registry::types::BridgeMetadata; +use webb_proposals::ResourceId; +use webb_relayer_config::anchor::LinkedAnchorConfig; +use webb_relayer_config::anchor::RawResourceId; +use webb_relayer_utils::Error; + +#[doc(hidden)] +pub mod dkg; + +#[doc(hidden)] +pub mod mocked; + +/// Wrapper for calling DKG bridge registry pallet methods. +#[async_trait::async_trait] +pub trait BridgeRegistryBackend { + /// Returns the next available bridge index. In other words, it returns the index of the + /// last bridge + 1. + async fn next_bridge_index(&self) -> webb_relayer_utils::Result; + + /// Retrieves the bridge index which a given resource is attached to. + /// + /// The passed resource must belong to a bridge that is registered with DKG, otherwise an error + /// is returned. + async fn resource_to_bridge_index( + &self, + resource_id: &ResourceId, + ) -> Option; + + /// Returns bridge with the given index, if any. + async fn bridges( + &self, + index: u32, + ) -> webb_relayer_utils::Result>; + + /// Attempts to return other linked anchors from the same bridge as passed linked anchor. + /// + /// If `anchors` config value is passed, this is returned unconditionally (for use with + /// mocks/testing). + /// + /// If `anchors` is `None`, uses [resource_to_bridge_id] to retrieve the matching bridge, then + /// retrieves the bridge itself to get registered anchors. It returns all of them excluding the + /// one that was passed as `linked_anchor` parameter. + async fn config_or_dkg_bridges( + &self, + anchors: &Option>, + linked_anchor: &ResourceId, + ) -> webb_relayer_utils::Result> { + match anchors { + Some(a) => Ok(a.clone()), + None => { + let next_bridge_index = self + .resource_to_bridge_index(linked_anchor) + .await + .ok_or(Error::BridgeNotRegistered(*linked_anchor))?; + match self.bridges(next_bridge_index).await? { + None => Ok(vec![]), + Some(bridges) => Ok(bridges + .resource_ids + .0 + .into_iter() + .filter(|r| r.0 != linked_anchor.0) + .map(|r| { + let rr = RawResourceId { + resource_id: H256(r.0), + }; + LinkedAnchorConfig::Raw(rr) + }) + .collect()), + } + } + } + } +} diff --git a/crates/bridge-registry-backends/src/mocked.rs b/crates/bridge-registry-backends/src/mocked.rs new file mode 100644 index 000000000..9ebc28b1c --- /dev/null +++ b/crates/bridge-registry-backends/src/mocked.rs @@ -0,0 +1,40 @@ +use typed_builder::TypedBuilder; +use webb::substrate::tangle_runtime::api::runtime_types::pallet_bridge_registry::types::{BridgeInfo, BridgeMetadata, SerdeData}; +use webb::substrate::tangle_runtime::api::runtime_types::pallet_identity::types::Data; +use webb::substrate::scale::DecodeAll; +use crate::BridgeRegistryBackend;use hex_literal::hex; +use webb::substrate::tangle_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::ResourceId; + +#[derive(TypedBuilder)] +pub struct MockedBridgeRegistryBackend {} + +#[async_trait::async_trait] +impl BridgeRegistryBackend for MockedBridgeRegistryBackend { + async fn next_bridge_index(&self) -> webb_relayer_utils::Result { + Ok(2) + } + + async fn resource_to_bridge_index( + &self, + _resource_id: &webb_proposals::ResourceId, + ) -> Option { + Some(1) + } + + async fn bridges( + &self, + _index: u32, + ) -> webb_relayer_utils::Result> { + let display = SerdeData( + Data::decode_all(&mut "mock bridge".as_bytes()) + .expect("decode mock bridge data"), + ); + Ok(Some(BridgeMetadata { + resource_ids: BoundedVec(vec![ + ResourceId(hex!("0000000000000000e69a847cd5bc0c9480ada0b339d7f0a8cac2b6670000138a")), + ResourceId(hex!("000000000000d30c8839c1145609e564b986f667b273ddcb8496010000001389"))]), + info: BridgeInfo { additional: BoundedVec(vec![]), display }, + })) + } +} diff --git a/crates/bridge-registry-backends/tests/submit_anchor_update_proposal.rs b/crates/bridge-registry-backends/tests/submit_anchor_update_proposal.rs new file mode 100644 index 000000000..89d48f11f --- /dev/null +++ b/crates/bridge-registry-backends/tests/submit_anchor_update_proposal.rs @@ -0,0 +1,126 @@ +use futures::StreamExt; +use sp_core::sr25519::Pair; +use sp_core::Pair as _; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::ResourceId as DkgResourceId; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::TypedChainId as DkgTypedChainId; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::nonce::Nonce as DkgNonce; +use webb::substrate::tangle_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec; +use webb::substrate::subxt::tx::{PairSigner, TxProgress, TxStatus}; +use webb::substrate::subxt::{OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::proposal::Proposal; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::proposal::ProposalKind; +use webb_bridge_registry_backends::dkg::DkgBridgeRegistryBackend; +use webb_bridge_registry_backends::BridgeRegistryBackend; +use webb_proposals::evm::AnchorUpdateProposal; +use webb_proposals::ResourceId; +use webb_proposals::{FunctionSignature, Nonce, ProposalHeader}; + +/// Rust reimplementation of DKG submit proposal test +/// +/// +#[tokio::test] +#[ignore = "requires running dkg-substrate standalone node"] +async fn submit_anchor_update_proposal() { + let api = OnlineClient::::new().await.unwrap(); + let bridge_registry = DkgBridgeRegistryBackend::new(api.clone()); + + // retrieve resource ids from bridge registry + let next_bridge_index = bridge_registry.next_bridge_index().await.unwrap(); + let bridges = bridge_registry + .bridges(next_bridge_index - 1) + .await + .unwrap() + .unwrap(); + let resource_id = ResourceId(bridges.resource_ids.0[1].0); + let src_resource_id = ResourceId(bridges.resource_ids.0[0].0); + + // print resource IDs + println!("Resource ID: {}", hex::encode(resource_id.0)); + println!("Source Resource ID: {}", hex::encode(src_resource_id.0)); + + let tx_api = RuntimeApi::tx().dkg_proposals(); + let sudo_account: PairSigner = + PairSigner::new(Pair::from_string("//Alice", None).unwrap()); + let account_nonce = api + .rpc() + .system_account_next_index(sudo_account.account_id()) + .await + .unwrap(); + + // following code runs in a loop in original code + { + let proposal_header = ProposalHeader::new( + resource_id, + FunctionSignature([0, 0, 0, 0]), + Nonce(account_nonce), + ); + let anchor_update_proposal = AnchorUpdateProposal::new( + proposal_header, + Default::default(), + src_resource_id, + ); + let anchor_update_proposal = anchor_update_proposal.to_bytes(); + println!( + "anchor update proposal bytes: {}", + hex::encode(anchor_update_proposal) + ); + assert_eq!(104, anchor_update_proposal.len()); + let unsigned_proposal = Proposal::Unsigned { + kind: ProposalKind::AnchorUpdate, + data: BoundedVec(anchor_update_proposal.to_vec()), + }; + let xt = tx_api.acknowledge_proposal( + DkgNonce(account_nonce), + DkgTypedChainId::Evm(5001), + DkgResourceId(resource_id.into_bytes()), + unsigned_proposal, + ); + + let mut progress = api + .tx() + .sign_and_submit_then_watch_default(&xt, &sudo_account) + .await + .unwrap(); + watch_events(&mut progress).await + } +} + +async fn watch_events( + progress: &mut TxProgress>, +) { + while let Some(event) = progress.next().await { + let e = match event { + Ok(e) => e, + Err(err) => { + println!("failed to watch for tx events: {err}"); + return; + } + }; + + match e { + TxStatus::Ready => { + println!("tx ready"); + } + TxStatus::Broadcast(_) => { + println!("tx broadcast"); + } + TxStatus::InBlock(_) => { + println!("tx in block"); + } + TxStatus::Finalized(v) => { + let maybe_success = v.wait_for_success().await; + match &maybe_success { + Ok(_events) => { + println!("tx finalized"); + } + Err(err) => { + println!("tx failed: {err}"); + } + } + assert!(maybe_success.is_ok()); + } + _ => unreachable!(), + } + } +} diff --git a/crates/chains-info/Cargo.toml b/crates/chains-info/Cargo.toml new file mode 100644 index 000000000..566c141b9 --- /dev/null +++ b/crates/chains-info/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "webb-chains-info" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +documentation = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } + +[build-dependencies] +anyhow = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true, features = ["std"] } +quote = "1.0" +prettyplease = "0.2" +syn = { version = "2", default-features = false, features = ["full", "parsing"] } +proc-macro2 = "1.0" +toml = "0.7" diff --git a/crates/chains-info/build.rs b/crates/chains-info/build.rs new file mode 100644 index 000000000..c7661f013 --- /dev/null +++ b/crates/chains-info/build.rs @@ -0,0 +1,297 @@ +use std::{ + collections::HashMap, + fs::File, + io::{BufWriter, Write}, + path::Path, +}; + +use quote::ToTokens; + +/// The chains information build script. +/// +/// This script generates the `chains.rs` file that contains the information about the chains +/// that are supported by the relayer. +fn main() -> anyhow::Result<()> { + // return early if the `supported_chains.toml` file didn't change + // since the last build + println!("cargo:rerun-if-changed=supported_chains.toml"); + // or any of the fixtures changed. + println!("cargo:rerun-if-changed=fixtures/chains.json"); + println!("cargo:rerun-if-changed=fixtures/coingecko_coins_list.json"); + + let v = std::fs::read_to_string("supported_chains.toml")?; + let supported_chains = toml::from_str(&v)?; + generate_chains_info(&supported_chains) +} + +#[derive(Debug, Clone, serde::Deserialize)] +struct SupportedChains { + #[serde(rename = "chain-ids")] + ids: Vec, + /// A Map of Chain Identifier to Chain Override. + #[serde(default)] + overrides: HashMap, +} + +#[derive(Debug, Clone, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +struct ChainOverride { + name: Option, + short_name: Option, + native_currency_name: Option, + native_currency_symbol: Option, + native_currency_decimals: Option, + coingecko_coin_id: Option, +} + +fn generate_chains_info( + supported_chains: &SupportedChains, +) -> anyhow::Result<()> { + let all_chains = chains::read_all()?; + let mut chains = all_chains + .into_iter() + .filter(|chain| supported_chains.ids.contains(&chain.chain_id)) + .map(|chain| { + supported_chains + .overrides + .get(&chain.chain_id.to_string()) + .cloned() + .map_or(chain.clone(), |v| chain.overrides_with(v)) + }) + .collect::>(); + // sort the chains by the chain identifier + // so that the generated code is deterministic + chains.sort_by_key(|chain| chain.chain_id); + let coingecko_coins_list = coingecko::coins_list()?; + let simple_chains_info = + bake_simple_chains_info(&chains, &coingecko_coins_list); + let path = Path::new(&std::env::var("OUT_DIR")?).join("chains.rs"); + let mut file = BufWriter::new(File::create(path)?); + + let generated_code = quote::quote! { + //! Chains Information + //! + //! This file is generated by the `build.rs` script. + //! Do not edit this file manually. + //! + //! This file contains the information about the chains that are supported by the relayer. + //! The information is used to generate the `chains.rs` file and could be used by + //! the relayer to get the information about the chains. + + /// The Chain Information. + pub struct ChainInfo { + /// Chain Identifier. + pub chain_id: u64, + /// Chain Name. + pub name: &'static str, + /// Chain Short Name, usually the ticker. + pub short_name: &'static str, + /// Chain Native Currency Information. + pub native_currency: CurrencyInfo, + } + + /// The Currency Information. + pub struct CurrencyInfo { + /// Currency Name. + pub name: &'static str, + /// Currency Symbol. + pub symbol: &'static str, + /// Currency Decimals. + pub decimals: u8, + /// Coingecko's Coin Identifier. + /// + /// This is `None` if the chain is not supported by Coingecko. + pub coingecko_coin_id: Option<&'static str>, + } + + #simple_chains_info + }; + + let code = quote::quote! { + mod chains { + #generated_code + } + }; + + let syntax_tree = syn::parse_file(&code.to_string())?; + let formatted = prettyplease::unparse(&syntax_tree); + file.write_all(formatted.as_bytes())?; + Ok(()) +} + +/// Bake the simple chains information. +/// +/// This returns a token stream that contains the simple chains information. +fn bake_simple_chains_info( + chains: &[chains::Chain], + coingecko_coins_list: &[coingecko::Coin], +) -> proc_macro2::TokenStream { + let mut tokens = proc_macro2::TokenStream::new(); + for chain in chains { + let chain_id = chain.chain_id; + let name = &chain.name; + let short_name = &chain.short_name; + let native_currency = &chain.native_currency; + let native_currency_name = &native_currency.name; + let native_currency_symbol = &native_currency.symbol; + let native_currency_decimals = native_currency.decimals; + // if the chain has a coingecko coin id, use it + // otherwise use the one from the coingecko coins list + let maybe_coingecko_coin_id = + chain.coingecko_coin_id.clone().or_else(|| { + coingecko_coins_list.iter().find_map(|coin| { + coin.symbol + .to_lowercase() + .eq(&native_currency_symbol.to_lowercase()) + .then(|| coin.id.clone()) + }) + }); + let coingecko_coin_id = if let Some(id) = maybe_coingecko_coin_id { + quote::quote! { Some(#id) } + } else { + quote::quote! { None } + }; + let token = quote::quote! { + ( + #chain_id, + ChainInfo { + chain_id: #chain_id, + name: #name, + short_name: #short_name, + native_currency: CurrencyInfo { + name: #native_currency_name, + symbol: #native_currency_symbol, + decimals: #native_currency_decimals, + coingecko_coin_id: #coingecko_coin_id, + }, + } + ), + }; + tokens.extend(token); + } + let n = chains.len(); + let result = quote::quote! { + pub type ChainsInfo = [(u64, ChainInfo); #n]; + /// List of all the supported chains. + pub const CHAINS_INFO: ChainsInfo = [ + #tokens + ]; + }; + result.into_token_stream() +} + +/// Reads all the chains from `fixtures/chains.json`. +/// +/// You can refetch the file from `https://chainid.network/chains.json` endpoint. +mod chains { + pub type Chains = Vec; + + #[derive(Default, Debug, Clone, PartialEq, serde::Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Chain { + pub name: String, + pub chain: String, + pub native_currency: NativeCurrency, + pub short_name: String, + pub chain_id: u64, + pub network_id: u64, + pub title: Option, + /// Used internally to override the chain's coingecko coin id. + #[serde(skip)] + pub coingecko_coin_id: Option, + } + impl Chain { + pub(crate) fn overrides_with( + self, + overrides: crate::ChainOverride, + ) -> Chain { + if let Some(name) = overrides.name { + return Chain { name, ..self }; + } + + if let Some(short_name) = overrides.short_name { + return Chain { short_name, ..self }; + } + + if let Some(native_currency_name) = overrides.native_currency_name { + return Chain { + native_currency: NativeCurrency { + name: native_currency_name, + ..self.native_currency + }, + ..self + }; + } + + if let Some(native_currency_symbol) = + overrides.native_currency_symbol + { + return Chain { + native_currency: NativeCurrency { + symbol: native_currency_symbol, + ..self.native_currency + }, + ..self + }; + } + + if let Some(native_currency_decimals) = + overrides.native_currency_decimals + { + return Chain { + native_currency: NativeCurrency { + decimals: native_currency_decimals, + ..self.native_currency + }, + ..self + }; + } + if let Some(coingecko_coin_id) = overrides.coingecko_coin_id { + return Chain { + coingecko_coin_id: Some(coingecko_coin_id), + ..self + }; + } + self + } + } + + #[derive(Default, Debug, Clone, PartialEq, serde::Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct NativeCurrency { + pub name: String, + pub symbol: String, + pub decimals: u8, + } + + pub fn read_all() -> anyhow::Result { + let file = std::fs::File::open("fixtures/chains.json")?; + let reader = std::io::BufReader::new(file); + let chains: Chains = serde_json::from_reader(reader)?; + Ok(chains) + } +} + +/// Reads the list of all the coins supported by coingecko +/// from `fixtures/coingecko_coins_list.json`. +/// +/// +/// You can refetch the file from the `https://api.coingecko.com/api/v3/coins/list` endpoint. +mod coingecko { + pub type Coins = Vec; + + #[derive(Default, Debug, Clone, PartialEq, serde::Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Coin { + pub id: String, + pub symbol: String, + pub name: String, + } + + pub fn coins_list() -> anyhow::Result { + let file = std::fs::File::open("fixtures/coingecko_coins_list.json")?; + let reader = std::io::BufReader::new(file); + let coins: Coins = serde_json::from_reader(reader)?; + Ok(coins) + } +} diff --git a/crates/chains-info/fixtures/chains.json b/crates/chains-info/fixtures/chains.json new file mode 100644 index 000000000..164586692 --- /dev/null +++ b/crates/chains-info/fixtures/chains.json @@ -0,0 +1,14087 @@ +[ + { + "name": "Ethereum Mainnet", + "chain": "ETH", + "icon": "ethereum", + "rpc": [ + "https://mainnet.infura.io/v3/${INFURA_API_KEY}", + "wss://mainnet.infura.io/ws/v3/${INFURA_API_KEY}", + "https://api.mycryptoapi.com/eth", + "https://cloudflare-eth.com", + "https://ethereum.publicnode.com" + ], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://ethereum.org", + "shortName": "eth", + "chainId": 1, + "networkId": 1, + "slip44": 60, + "ens": { "registry": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" }, + "explorers": [{ "name": "etherscan", "url": "https://etherscan.io", "standard": "EIP3091" }] + }, + { + "name": "Expanse Network", + "chain": "EXP", + "rpc": ["https://node.expanse.tech"], + "faucets": [], + "nativeCurrency": { "name": "Expanse Network Ether", "symbol": "EXP", "decimals": 18 }, + "infoURL": "https://expanse.tech", + "shortName": "exp", + "chainId": 2, + "networkId": 1, + "slip44": 40 + }, + { + "name": "Ropsten", + "title": "Ethereum Testnet Ropsten", + "chain": "ETH", + "rpc": [ + "https://ropsten.infura.io/v3/${INFURA_API_KEY}", + "wss://ropsten.infura.io/ws/v3/${INFURA_API_KEY}" + ], + "faucets": [ + "http://fauceth.komputing.org?chain=3&address=${ADDRESS}", + "https://faucet.ropsten.be?${ADDRESS}" + ], + "nativeCurrency": { "name": "Ropsten Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://github.com/ethereum/ropsten", + "shortName": "rop", + "chainId": 3, + "networkId": 3, + "ens": { "registry": "0x112234455c3a32fd11230c42e7bccd4a84e02010" }, + "explorers": [ + { "name": "etherscan", "url": "https://ropsten.etherscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Rinkeby", + "title": "Ethereum Testnet Rinkeby", + "chain": "ETH", + "rpc": [ + "https://rinkeby.infura.io/v3/${INFURA_API_KEY}", + "wss://rinkeby.infura.io/ws/v3/${INFURA_API_KEY}" + ], + "faucets": [ + "http://fauceth.komputing.org?chain=4&address=${ADDRESS}", + "https://faucet.rinkeby.io" + ], + "nativeCurrency": { "name": "Rinkeby Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://www.rinkeby.io", + "shortName": "rin", + "chainId": 4, + "networkId": 4, + "ens": { "registry": "0xe7410170f87102df0055eb195163a03b7f2bff4a" }, + "explorers": [ + { "name": "etherscan-rinkeby", "url": "https://rinkeby.etherscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Goerli", + "title": "Ethereum Testnet Goerli", + "chain": "ETH", + "rpc": [ + "https://goerli.infura.io/v3/${INFURA_API_KEY}", + "wss://goerli.infura.io/v3/${INFURA_API_KEY}", + "https://rpc.goerli.mudit.blog/" + ], + "faucets": [ + "http://fauceth.komputing.org?chain=5&address=${ADDRESS}", + "https://goerli-faucet.slock.it?address=${ADDRESS}", + "https://faucet.goerli.mudit.blog" + ], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://goerli.net/#about", + "shortName": "gor", + "chainId": 5, + "networkId": 5, + "ens": { "registry": "0x112234455c3a32fd11230c42e7bccd4a84e02010" }, + "explorers": [ + { "name": "etherscan-goerli", "url": "https://goerli.etherscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Ethereum Classic Testnet Kotti", + "chain": "ETC", + "rpc": ["https://www.ethercluster.com/kotti"], + "faucets": [], + "nativeCurrency": { "name": "Kotti Ether", "symbol": "KOT", "decimals": 18 }, + "infoURL": "https://explorer.jade.builders/?network=kotti", + "shortName": "kot", + "chainId": 6, + "networkId": 6 + }, + { + "name": "ThaiChain", + "chain": "TCH", + "rpc": ["https://rpc.dome.cloud", "https://rpc.thaichain.org"], + "faucets": [], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "nativeCurrency": { "name": "ThaiChain Ether", "symbol": "TCH", "decimals": 18 }, + "infoURL": "https://thaichain.io", + "shortName": "tch", + "chainId": 7, + "networkId": 7, + "explorers": [ + { "name": "Thaichain Explorer", "url": "https://exp.thaichain.org", "standard": "EIP3091" } + ] + }, + { + "name": "Ubiq", + "chain": "UBQ", + "rpc": ["https://rpc.octano.dev", "https://pyrus2.ubiqscan.io"], + "faucets": [], + "nativeCurrency": { "name": "Ubiq Ether", "symbol": "UBQ", "decimals": 18 }, + "infoURL": "https://ubiqsmart.com", + "shortName": "ubq", + "chainId": 8, + "networkId": 8, + "slip44": 108, + "explorers": [{ "name": "ubiqscan", "url": "https://ubiqscan.io", "standard": "EIP3091" }] + }, + { + "name": "Ubiq Network Testnet", + "chain": "UBQ", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ubiq Testnet Ether", "symbol": "TUBQ", "decimals": 18 }, + "infoURL": "https://ethersocial.org", + "shortName": "tubq", + "chainId": 9, + "networkId": 2 + }, + { + "name": "Optimism", + "chain": "ETH", + "rpc": ["https://mainnet.optimism.io/"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://optimism.io", + "shortName": "oeth", + "chainId": 10, + "networkId": 10, + "explorers": [ + { "name": "etherscan", "url": "https://optimistic.etherscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Metadium Mainnet", + "chain": "META", + "rpc": ["https://api.metadium.com/prod"], + "faucets": [], + "nativeCurrency": { "name": "Metadium Mainnet Ether", "symbol": "META", "decimals": 18 }, + "infoURL": "https://metadium.com", + "shortName": "meta", + "chainId": 11, + "networkId": 11, + "slip44": 916 + }, + { + "name": "Metadium Testnet", + "chain": "META", + "rpc": ["https://api.metadium.com/dev"], + "faucets": [], + "nativeCurrency": { "name": "Metadium Testnet Ether", "symbol": "KAL", "decimals": 18 }, + "infoURL": "https://metadium.com", + "shortName": "kal", + "chainId": 12, + "networkId": 12 + }, + { + "name": "Diode Testnet Staging", + "chain": "DIODE", + "rpc": ["https://staging.diode.io:8443/", "wss://staging.diode.io:8443/ws"], + "faucets": [], + "nativeCurrency": { "name": "Staging Diodes", "symbol": "sDIODE", "decimals": 18 }, + "infoURL": "https://diode.io/staging", + "shortName": "dstg", + "chainId": 13, + "networkId": 13 + }, + { + "name": "Flare Mainnet", + "chain": "FLR", + "icon": "flare", + "rpc": ["https://flare-api.flare.network/ext/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Flare", "symbol": "FLR", "decimals": 18 }, + "infoURL": "https://flare.xyz", + "shortName": "flr", + "chainId": 14, + "networkId": 14, + "explorers": [ + { "name": "blockscout", "url": "https://flare-explorer.flare.network", "standard": "EIP3091" } + ] + }, + { + "name": "Diode Prenet", + "chain": "DIODE", + "rpc": ["https://prenet.diode.io:8443/", "wss://prenet.diode.io:8443/ws"], + "faucets": [], + "nativeCurrency": { "name": "Diodes", "symbol": "DIODE", "decimals": 18 }, + "infoURL": "https://diode.io/prenet", + "shortName": "diode", + "chainId": 15, + "networkId": 15 + }, + { + "name": "Flare Testnet Coston", + "chain": "FLR", + "icon": "coston", + "rpc": ["https://coston-api.flare.network/ext/bc/C/rpc"], + "faucets": [ + "https://faucet.towolabs.com", + "https://fauceth.komputing.org?chain=16&address=${ADDRESS}" + ], + "nativeCurrency": { "name": "Coston Flare", "symbol": "CFLR", "decimals": 18 }, + "infoURL": "https://flare.xyz", + "shortName": "cflr", + "chainId": 16, + "networkId": 16, + "explorers": [ + { + "name": "blockscout", + "url": "https://coston-explorer.flare.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "ThaiChain 2.0 ThaiFi", + "chain": "TCH", + "rpc": ["https://rpc.thaifi.com"], + "faucets": [], + "nativeCurrency": { "name": "Thaifi Ether", "symbol": "TFI", "decimals": 18 }, + "infoURL": "https://exp.thaifi.com", + "shortName": "tfi", + "chainId": 17, + "networkId": 17 + }, + { + "name": "ThunderCore Testnet", + "chain": "TST", + "rpc": ["https://testnet-rpc.thundercore.com"], + "faucets": ["https://faucet-testnet.thundercore.com"], + "nativeCurrency": { "name": "ThunderCore Testnet Token", "symbol": "TST", "decimals": 18 }, + "infoURL": "https://thundercore.com", + "shortName": "TST", + "chainId": 18, + "networkId": 18, + "explorers": [ + { + "name": "thundercore-blockscout-testnet", + "url": "https://explorer-testnet.thundercore.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Songbird Canary-Network", + "chain": "SGB", + "icon": "songbird", + "rpc": [ + "https://songbird-api.flare.network/ext/C/rpc", + "https://sgb.ftso.com.au/ext/bc/C/rpc", + "https://sgb.lightft.so/rpc", + "https://sgb-rpc.ftso.eu" + ], + "faucets": [], + "nativeCurrency": { "name": "Songbird", "symbol": "SGB", "decimals": 18 }, + "infoURL": "https://flare.xyz", + "shortName": "sgb", + "chainId": 19, + "networkId": 19, + "explorers": [ + { + "name": "blockscout", + "url": "https://songbird-explorer.flare.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Elastos Smart Chain", + "chain": "ETH", + "rpc": ["https://api.elastos.io/eth"], + "faucets": ["https://faucet.elastos.org/"], + "nativeCurrency": { "name": "Elastos", "symbol": "ELA", "decimals": 18 }, + "infoURL": "https://www.elastos.org/", + "shortName": "esc", + "chainId": 20, + "networkId": 20, + "explorers": [ + { "name": "elastos esc explorer", "url": "https://esc.elastos.io", "standard": "EIP3091" } + ] + }, + { + "name": "Elastos Smart Chain Testnet", + "chain": "ETH", + "rpc": ["https://api-testnet.elastos.io/eth"], + "faucets": ["https://esc-faucet.elastos.io/"], + "nativeCurrency": { "name": "Elastos", "symbol": "tELA", "decimals": 18 }, + "infoURL": "https://www.elastos.org/", + "shortName": "esct", + "chainId": 21, + "networkId": 21, + "explorers": [ + { + "name": "elastos esc explorer", + "url": "https://esc-testnet.elastos.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "ELA-DID-Sidechain Mainnet", + "chain": "ETH", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Elastos", "symbol": "ELA", "decimals": 18 }, + "infoURL": "https://www.elastos.org/", + "shortName": "eladid", + "chainId": 22, + "networkId": 22 + }, + { + "name": "ELA-DID-Sidechain Testnet", + "chain": "ETH", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Elastos", "symbol": "tELA", "decimals": 18 }, + "infoURL": "https://elaeth.io/", + "shortName": "eladidt", + "chainId": 23, + "networkId": 23 + }, + { + "name": "KardiaChain Mainnet", + "chain": "KAI", + "icon": "kardiachain", + "rpc": ["https://rpc.kardiachain.io"], + "faucets": [], + "nativeCurrency": { "name": "KardiaChain", "symbol": "KAI", "decimals": 18 }, + "infoURL": "https://kardiachain.io", + "shortName": "kardiachain", + "chainId": 24, + "networkId": 0, + "redFlags": ["reusedChainId"] + }, + { + "name": "Cronos Mainnet Beta", + "chain": "CRO", + "rpc": ["https://evm.cronos.org", "https://cronos-evm.publicnode.com"], + "features": [{ "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Cronos", "symbol": "CRO", "decimals": 18 }, + "infoURL": "https://cronos.org/", + "shortName": "cro", + "chainId": 25, + "networkId": 25, + "explorers": [{ "name": "Cronos Explorer", "url": "https://cronoscan.com", "standard": "none" }] + }, + { + "name": "Genesis L1 testnet", + "chain": "genesis", + "rpc": ["https://testrpc.genesisl1.org"], + "faucets": [], + "nativeCurrency": { "name": "L1 testcoin", "symbol": "L1test", "decimals": 18 }, + "infoURL": "https://www.genesisl1.com", + "shortName": "L1test", + "chainId": 26, + "networkId": 26, + "explorers": [ + { + "name": "Genesis L1 testnet explorer", + "url": "https://testnet.genesisl1.org", + "standard": "none" + } + ] + }, + { + "name": "ShibaChain", + "chain": "SHIB", + "rpc": ["https://rpc.shibachain.net"], + "faucets": [], + "nativeCurrency": { "name": "SHIBA INU COIN", "symbol": "SHIB", "decimals": 18 }, + "infoURL": "https://www.shibachain.net", + "shortName": "shib", + "chainId": 27, + "networkId": 27, + "explorers": [ + { "name": "Shiba Explorer", "url": "https://exp.shibachain.net", "standard": "none" } + ] + }, + { + "name": "Boba Network Rinkeby Testnet", + "chain": "ETH", + "rpc": ["https://rinkeby.boba.network/"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaRinkeby", + "chainId": 28, + "networkId": 28, + "explorers": [ + { + "name": "Blockscout", + "url": "https://blockexplorer.rinkeby.boba.network", + "standard": "none" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-4", + "bridges": [{ "url": "https://gateway.rinkeby.boba.network" }] + } + }, + { + "name": "Genesis L1", + "chain": "genesis", + "rpc": ["https://rpc.genesisl1.org"], + "faucets": [], + "nativeCurrency": { "name": "L1 coin", "symbol": "L1", "decimals": 18 }, + "infoURL": "https://www.genesisl1.com", + "shortName": "L1", + "chainId": 29, + "networkId": 29, + "explorers": [ + { + "name": "Genesis L1 blockchain explorer", + "url": "https://explorer.genesisl1.org", + "standard": "none" + } + ] + }, + { + "name": "RSK Mainnet", + "chain": "RSK", + "rpc": ["https://public-node.rsk.co", "https://mycrypto.rsk.co"], + "faucets": ["https://faucet.rsk.co/"], + "nativeCurrency": { "name": "Smart Bitcoin", "symbol": "RBTC", "decimals": 18 }, + "infoURL": "https://rsk.co", + "shortName": "rsk", + "chainId": 30, + "networkId": 30, + "slip44": 137, + "explorers": [ + { "name": "RSK Explorer", "url": "https://explorer.rsk.co", "standard": "EIP3091" } + ] + }, + { + "name": "RSK Testnet", + "chain": "RSK", + "rpc": ["https://public-node.testnet.rsk.co", "https://mycrypto.testnet.rsk.co"], + "faucets": ["https://faucet.rsk.co/"], + "nativeCurrency": { "name": "Testnet Smart Bitcoin", "symbol": "tRBTC", "decimals": 18 }, + "infoURL": "https://rsk.co", + "shortName": "trsk", + "chainId": 31, + "networkId": 31, + "explorers": [ + { + "name": "RSK Testnet Explorer", + "url": "https://explorer.testnet.rsk.co", + "standard": "EIP3091" + } + ] + }, + { + "name": "GoodData Testnet", + "chain": "GooD", + "rpc": ["https://test2.goodata.io"], + "faucets": [], + "nativeCurrency": { "name": "GoodData Testnet Ether", "symbol": "GooD", "decimals": 18 }, + "infoURL": "https://www.goodata.org", + "shortName": "GooDT", + "chainId": 32, + "networkId": 32 + }, + { + "name": "GoodData Mainnet", + "chain": "GooD", + "rpc": ["https://rpc.goodata.io"], + "faucets": [], + "nativeCurrency": { "name": "GoodData Mainnet Ether", "symbol": "GooD", "decimals": 18 }, + "infoURL": "https://www.goodata.org", + "shortName": "GooD", + "chainId": 33, + "networkId": 33 + }, + { + "name": "Dithereum Testnet", + "chain": "DTH", + "icon": "dithereum", + "rpc": ["https://node-testnet.dithereum.io"], + "faucets": ["https://faucet.dithereum.org"], + "nativeCurrency": { "name": "Dither", "symbol": "DTH", "decimals": 18 }, + "infoURL": "https://dithereum.org", + "shortName": "dth", + "chainId": 34, + "networkId": 34 + }, + { + "name": "TBWG Chain", + "chain": "TBWG", + "rpc": ["https://rpc.tbwg.io"], + "faucets": [], + "nativeCurrency": { "name": "TBWG Ether", "symbol": "TBG", "decimals": 18 }, + "infoURL": "https://tbwg.io", + "shortName": "tbwg", + "chainId": 35, + "networkId": 35 + }, + { + "name": "Dxchain Mainnet", + "chain": "Dxchain", + "icon": "dx", + "rpc": ["https://mainnet.dxchain.com"], + "faucets": [], + "nativeCurrency": { "name": "Dxchain", "symbol": "DX", "decimals": 18 }, + "infoURL": "https://www.dxchain.com/", + "shortName": "dx", + "chainId": 36, + "networkId": 36, + "explorers": [{ "name": "dxscan", "url": "https://dxscan.io", "standard": "EIP3091" }] + }, + { + "name": "SeedCoin-Network", + "chain": "SeedCoin-Network", + "rpc": ["https://node.seedcoin.network"], + "faucets": [], + "nativeCurrency": { "name": "SeedCoin", "symbol": "SEED", "decimals": 18 }, + "infoURL": "https://www.seedcoin.network/", + "shortName": "SEED", + "icon": "seedcoin", + "chainId": 37, + "networkId": 37 + }, + { + "name": "Valorbit", + "chain": "VAL", + "rpc": ["https://rpc.valorbit.com/v2"], + "faucets": [], + "nativeCurrency": { "name": "Valorbit", "symbol": "VAL", "decimals": 18 }, + "infoURL": "https://valorbit.com", + "shortName": "val", + "chainId": 38, + "networkId": 38, + "slip44": 538 + }, + { + "name": "Unicorn Ultra Testnet", + "chain": "u2u", + "rpc": ["https://rpc-testnet.uniultra.xyz"], + "faucets": ["https://faucet.uniultra.xyz"], + "nativeCurrency": { "name": "Unicorn Ultra", "symbol": "U2U", "decimals": 18 }, + "infoURL": "https://uniultra.xyz", + "shortName": "u2u", + "chainId": 39, + "networkId": 39, + "icon": "u2u", + "explorers": [ + { + "icon": "u2u", + "name": "U2U Explorer", + "url": "https://testnet.uniultra.xyz", + "standard": "EIP3091" + } + ] + }, + { + "name": "Telos EVM Mainnet", + "chain": "TLOS", + "rpc": ["https://mainnet.telos.net/evm"], + "faucets": [], + "nativeCurrency": { "name": "Telos", "symbol": "TLOS", "decimals": 18 }, + "infoURL": "https://telos.net", + "shortName": "TelosEVM", + "chainId": 40, + "networkId": 40, + "explorers": [{ "name": "teloscan", "url": "https://teloscan.io", "standard": "EIP3091" }] + }, + { + "name": "Telos EVM Testnet", + "chain": "TLOS", + "rpc": ["https://testnet.telos.net/evm"], + "faucets": ["https://app.telos.net/testnet/developers"], + "nativeCurrency": { "name": "Telos", "symbol": "TLOS", "decimals": 18 }, + "infoURL": "https://telos.net", + "shortName": "TelosEVMTestnet", + "chainId": 41, + "networkId": 41 + }, + { + "name": "Kovan", + "title": "Ethereum Testnet Kovan", + "chain": "ETH", + "rpc": [ + "https://kovan.poa.network", + "http://kovan.poa.network:8545", + "https://kovan.infura.io/v3/${INFURA_API_KEY}", + "wss://kovan.infura.io/ws/v3/${INFURA_API_KEY}", + "ws://kovan.poa.network:8546" + ], + "faucets": [ + "http://fauceth.komputing.org?chain=42&address=${ADDRESS}", + "https://faucet.kovan.network", + "https://gitter.im/kovan-testnet/faucet" + ], + "nativeCurrency": { "name": "Kovan Ether", "symbol": "ETH", "decimals": 18 }, + "explorers": [ + { "name": "etherscan", "url": "https://kovan.etherscan.io", "standard": "EIP3091" } + ], + "infoURL": "https://kovan-testnet.github.io/website", + "shortName": "kov", + "chainId": 42, + "networkId": 42 + }, + { + "name": "Darwinia Pangolin Testnet", + "chain": "pangolin", + "rpc": ["https://pangolin-rpc.darwinia.network"], + "faucets": ["https://docs.crab.network/dvm/wallets/dvm-metamask#apply-for-the-test-token"], + "nativeCurrency": { + "name": "Pangolin Network Native Token", + "symbol": "PRING", + "decimals": 18 + }, + "infoURL": "https://darwinia.network/", + "shortName": "pangolin", + "chainId": 43, + "networkId": 43, + "explorers": [{ "name": "subscan", "url": "https://pangolin.subscan.io", "standard": "none" }] + }, + { + "name": "Darwinia Crab Network", + "chain": "crab", + "rpc": ["https://crab-rpc.darwinia.network"], + "faucets": [], + "nativeCurrency": { "name": "Crab Network Native Token", "symbol": "CRAB", "decimals": 18 }, + "infoURL": "https://crab.network/", + "shortName": "crab", + "chainId": 44, + "networkId": 44, + "explorers": [{ "name": "subscan", "url": "https://crab.subscan.io", "standard": "none" }] + }, + { + "name": "Darwinia Pangoro Testnet", + "chain": "pangoro", + "rpc": ["https://pangoro-rpc.darwinia.network"], + "faucets": [], + "nativeCurrency": { "name": "Pangoro Network Native Token", "symbol": "ORING", "decimals": 18 }, + "infoURL": "https://darwinia.network/", + "shortName": "pangoro", + "chainId": 45, + "networkId": 45, + "explorers": [{ "name": "subscan", "url": "https://pangoro.subscan.io", "standard": "none" }] + }, + { + "name": "Darwinia Network", + "chain": "darwinia", + "rpc": ["https://rpc.darwinia.network"], + "faucets": [], + "nativeCurrency": { "name": "Darwinia Network Native Token", "symbol": "RING", "decimals": 18 }, + "infoURL": "https://darwinia.network/", + "shortName": "darwinia", + "chainId": 46, + "networkId": 46, + "explorers": [{ "name": "subscan", "url": "https://darwinia.subscan.io", "standard": "none" }] + }, + { + "name": "Acria IntelliChain", + "chain": "AIC", + "rpc": ["https://aic.acria.ai"], + "faucets": [], + "nativeCurrency": { "name": "ACRIA", "symbol": "ACRIA", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://acria.ai", + "shortName": "aic", + "chainId": 47, + "networkId": 47, + "explorers": [ + { + "name": "Acria IntelliChain-Explorer", + "url": "https://explorer.acria.ai", + "standard": "EIP3091" + } + ] + }, + { + "name": "Ennothem Mainnet Proterozoic", + "chain": "ETMP", + "rpc": ["https://rpc.etm.network"], + "faucets": [], + "nativeCurrency": { "name": "Ennothem", "symbol": "ETMP", "decimals": 18 }, + "infoURL": "https://etm.network", + "shortName": "etmp", + "chainId": 48, + "networkId": 48, + "icon": "etmp", + "explorers": [ + { + "name": "etmpscan", + "url": "https://etmscan.network", + "icon": "etmp", + "standard": "EIP3091" + } + ] + }, + { + "name": "Ennothem Testnet Pioneer", + "chain": "ETMP", + "rpc": ["https://rpc.pioneer.etm.network"], + "faucets": [], + "nativeCurrency": { "name": "Ennothem", "symbol": "ETMP", "decimals": 18 }, + "infoURL": "https://etm.network", + "shortName": "etmpTest", + "chainId": 49, + "networkId": 49, + "icon": "etmp", + "explorers": [ + { "name": "etmp", "url": "https://pioneer.etmscan.network", "standard": "EIP3091" } + ] + }, + { + "name": "XinFin XDC Network", + "chain": "XDC", + "rpc": [ + "https://erpc.xinfin.network", + "https://rpc.xinfin.network", + "https://rpc1.xinfin.network" + ], + "faucets": [], + "nativeCurrency": { "name": "XinFin", "symbol": "XDC", "decimals": 18 }, + "infoURL": "https://xinfin.org", + "shortName": "xdc", + "chainId": 50, + "networkId": 50, + "icon": "xdc", + "explorers": [ + { + "name": "xdcscan", + "url": "https://xdcscan.io", + "icon": "blocksscan", + "standard": "EIP3091" + }, + { + "name": "blocksscan", + "url": "https://xdc.blocksscan.io", + "icon": "blocksscan", + "standard": "EIP3091" + } + ] + }, + { + "name": "XDC Apothem Network", + "chain": "XDC", + "rpc": ["https://rpc.apothem.network", "https://erpc.apothem.network"], + "faucets": ["https://faucet.apothem.network"], + "nativeCurrency": { "name": "XinFin", "symbol": "TXDC", "decimals": 18 }, + "infoURL": "https://xinfin.org", + "shortName": "txdc", + "chainId": 51, + "networkId": 51, + "icon": "xdc", + "explorers": [ + { + "name": "xdcscan", + "url": "https://apothem.xinfinscan.com", + "icon": "blocksscan", + "standard": "EIP3091" + }, + { + "name": "blocksscan", + "url": "https://apothem.blocksscan.io", + "icon": "blocksscan", + "standard": "EIP3091" + } + ] + }, + { + "name": "CoinEx Smart Chain Mainnet", + "chain": "CSC", + "rpc": ["https://rpc.coinex.net"], + "faucets": [], + "nativeCurrency": { "name": "CoinEx Chain Native Token", "symbol": "cet", "decimals": 18 }, + "infoURL": "https://www.coinex.org/", + "shortName": "cet", + "chainId": 52, + "networkId": 52, + "explorers": [{ "name": "coinexscan", "url": "https://www.coinex.net", "standard": "none" }] + }, + { + "name": "CoinEx Smart Chain Testnet", + "chain": "CSC", + "rpc": ["https://testnet-rpc.coinex.net/"], + "faucets": [], + "nativeCurrency": { + "name": "CoinEx Chain Test Native Token", + "symbol": "cett", + "decimals": 18 + }, + "infoURL": "https://www.coinex.org/", + "shortName": "tcet", + "chainId": 53, + "networkId": 53, + "explorers": [{ "name": "coinexscan", "url": "https://testnet.coinex.net", "standard": "none" }] + }, + { + "name": "Openpiece Mainnet", + "chain": "OPENPIECE", + "icon": "openpiece", + "rpc": ["https://mainnet.openpiece.io"], + "faucets": [], + "nativeCurrency": { "name": "Belly", "symbol": "BELLY", "decimals": 18 }, + "infoURL": "https://cryptopiece.online", + "shortName": "OP", + "chainId": 54, + "networkId": 54, + "explorers": [{ "name": "Belly Scan", "url": "https://bellyscan.com", "standard": "none" }] + }, + { + "name": "Zyx Mainnet", + "chain": "ZYX", + "rpc": [ + "https://rpc-1.zyx.network/", + "https://rpc-2.zyx.network/", + "https://rpc-3.zyx.network/", + "https://rpc-4.zyx.network/", + "https://rpc-5.zyx.network/", + "https://rpc-6.zyx.network/" + ], + "faucets": [], + "nativeCurrency": { "name": "Zyx", "symbol": "ZYX", "decimals": 18 }, + "infoURL": "https://zyx.network/", + "shortName": "ZYX", + "chainId": 55, + "networkId": 55, + "explorers": [{ "name": "zyxscan", "url": "https://zyxscan.com", "standard": "none" }] + }, + { + "name": "Binance Smart Chain Mainnet", + "chain": "BSC", + "rpc": [ + "https://bsc-dataseed1.binance.org", + "https://bsc-dataseed2.binance.org", + "https://bsc-dataseed3.binance.org", + "https://bsc-dataseed4.binance.org", + "https://bsc-dataseed1.defibit.io", + "https://bsc-dataseed2.defibit.io", + "https://bsc-dataseed3.defibit.io", + "https://bsc-dataseed4.defibit.io", + "https://bsc-dataseed1.ninicoin.io", + "https://bsc-dataseed2.ninicoin.io", + "https://bsc-dataseed3.ninicoin.io", + "https://bsc-dataseed4.ninicoin.io", + "https://bsc.publicnode.com", + "wss://bsc-ws-node.nariox.org" + ], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "Binance Chain Native Token", "symbol": "BNB", "decimals": 18 }, + "infoURL": "https://www.binance.org", + "shortName": "bnb", + "chainId": 56, + "networkId": 56, + "slip44": 714, + "explorers": [{ "name": "bscscan", "url": "https://bscscan.com", "standard": "EIP3091" }] + }, + { + "name": "Syscoin Mainnet", + "chain": "SYS", + "rpc": ["https://rpc.syscoin.org", "wss://rpc.syscoin.org/wss"], + "faucets": ["https://faucet.syscoin.org"], + "nativeCurrency": { "name": "Syscoin", "symbol": "SYS", "decimals": 18 }, + "infoURL": "https://www.syscoin.org", + "shortName": "sys", + "chainId": 57, + "networkId": 57, + "explorers": [ + { + "name": "Syscoin Block Explorer", + "url": "https://explorer.syscoin.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Ontology Mainnet", + "chain": "Ontology", + "icon": "ontology", + "rpc": [ + "http://dappnode1.ont.io:20339", + "http://dappnode2.ont.io:20339", + "http://dappnode3.ont.io:20339", + "http://dappnode4.ont.io:20339", + "https://dappnode1.ont.io:10339", + "https://dappnode2.ont.io:10339", + "https://dappnode3.ont.io:10339", + "https://dappnode4.ont.io:10339" + ], + "faucets": [], + "nativeCurrency": { "name": "ONG", "symbol": "ONG", "decimals": 18 }, + "infoURL": "https://ont.io/", + "shortName": "OntologyMainnet", + "chainId": 58, + "networkId": 58, + "explorers": [{ "name": "explorer", "url": "https://explorer.ont.io", "standard": "EIP3091" }] + }, + { + "name": "EOS Mainnet", + "chain": "EOS", + "rpc": ["https://api.eosargentina.io"], + "faucets": [], + "nativeCurrency": { "name": "EOS", "symbol": "EOS", "decimals": 18 }, + "infoURL": "https://eoscommunity.org/", + "shortName": "EOSMainnet", + "chainId": 59, + "networkId": 59, + "explorers": [ + { "name": "bloks", "url": "https://bloks.eosargentina.io", "standard": "EIP3091" } + ] + }, + { + "name": "GoChain", + "chain": "GO", + "rpc": ["https://rpc.gochain.io"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "GoChain Ether", "symbol": "GO", "decimals": 18 }, + "infoURL": "https://gochain.io", + "shortName": "go", + "chainId": 60, + "networkId": 60, + "slip44": 6060, + "explorers": [ + { "name": "GoChain Explorer", "url": "https://explorer.gochain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Ethereum Classic Mainnet", + "chain": "ETC", + "rpc": ["https://www.ethercluster.com/etc"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/?"], + "nativeCurrency": { "name": "Ethereum Classic Ether", "symbol": "ETC", "decimals": 18 }, + "infoURL": "https://ethereumclassic.org", + "shortName": "etc", + "chainId": 61, + "networkId": 1, + "slip44": 61, + "explorers": [ + { "name": "blockscout", "url": "https://blockscout.com/etc/mainnet", "standard": "none" } + ] + }, + { + "name": "Ethereum Classic Testnet Morden", + "chain": "ETC", + "rpc": [], + "faucets": [], + "nativeCurrency": { + "name": "Ethereum Classic Testnet Ether", + "symbol": "TETC", + "decimals": 18 + }, + "infoURL": "https://ethereumclassic.org", + "shortName": "tetc", + "chainId": 62, + "networkId": 2 + }, + { + "name": "Ethereum Classic Testnet Mordor", + "chain": "ETC", + "rpc": ["https://www.ethercluster.com/mordor"], + "faucets": [], + "nativeCurrency": { "name": "Mordor Classic Testnet Ether", "symbol": "METC", "decimals": 18 }, + "infoURL": "https://github.com/eth-classic/mordor/", + "shortName": "metc", + "chainId": 63, + "networkId": 7 + }, + { + "name": "Ellaism", + "chain": "ELLA", + "rpc": ["https://jsonrpc.ellaism.org"], + "faucets": [], + "nativeCurrency": { "name": "Ellaism Ether", "symbol": "ELLA", "decimals": 18 }, + "infoURL": "https://ellaism.org", + "shortName": "ellaism", + "chainId": 64, + "networkId": 64, + "slip44": 163 + }, + { + "name": "OKExChain Testnet", + "chain": "okexchain", + "rpc": ["https://exchaintestrpc.okex.org"], + "faucets": ["https://www.okex.com/drawdex"], + "nativeCurrency": { + "name": "OKExChain Global Utility Token in testnet", + "symbol": "OKT", + "decimals": 18 + }, + "infoURL": "https://www.okex.com/okexchain", + "shortName": "tokt", + "chainId": 65, + "networkId": 65, + "explorers": [ + { "name": "OKLink", "url": "https://www.oklink.com/okexchain-test", "standard": "EIP3091" } + ] + }, + { + "name": "OKXChain Mainnet", + "chain": "okxchain", + "rpc": [ + "https://exchainrpc.okex.org", + "https://okc-mainnet.gateway.pokt.network/v1/lb/6275309bea1b320039c893ff" + ], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/?"], + "nativeCurrency": { "name": "OKXChain Global Utility Token", "symbol": "OKT", "decimals": 18 }, + "infoURL": "https://www.okex.com/okc", + "shortName": "okt", + "chainId": 66, + "networkId": 66, + "explorers": [ + { "name": "OKLink", "url": "https://www.oklink.com/en/okc", "standard": "EIP3091" } + ] + }, + { + "name": "DBChain Testnet", + "chain": "DBM", + "rpc": ["http://test-rpc.dbmbp.com"], + "faucets": [], + "nativeCurrency": { "name": "DBChain Testnet", "symbol": "DBM", "decimals": 18 }, + "infoURL": "http://test.dbmbp.com", + "shortName": "dbm", + "chainId": 67, + "networkId": 67 + }, + { + "name": "SoterOne Mainnet", + "chain": "SOTER", + "rpc": ["https://rpc.soter.one"], + "faucets": [], + "nativeCurrency": { "name": "SoterOne Mainnet Ether", "symbol": "SOTER", "decimals": 18 }, + "infoURL": "https://www.soterone.com", + "shortName": "SO1", + "chainId": 68, + "networkId": 68 + }, + { + "name": "Optimism Kovan", + "title": "Optimism Testnet Kovan", + "chain": "ETH", + "rpc": ["https://kovan.optimism.io/"], + "faucets": ["http://fauceth.komputing.org?chain=69&address=${ADDRESS}"], + "nativeCurrency": { "name": "Kovan Ether", "symbol": "ETH", "decimals": 18 }, + "explorers": [ + { "name": "etherscan", "url": "https://kovan-optimistic.etherscan.io", "standard": "EIP3091" } + ], + "infoURL": "https://optimism.io", + "shortName": "okov", + "chainId": 69, + "networkId": 69 + }, + { + "name": "Hoo Smart Chain", + "chain": "HSC", + "rpc": [ + "https://http-mainnet.hoosmartchain.com", + "https://http-mainnet2.hoosmartchain.com", + "wss://ws-mainnet.hoosmartchain.com", + "wss://ws-mainnet2.hoosmartchain.com" + ], + "faucets": [], + "nativeCurrency": { "name": "Hoo Smart Chain Native Token", "symbol": "HOO", "decimals": 18 }, + "infoURL": "https://www.hoosmartchain.com", + "shortName": "hsc", + "chainId": 70, + "networkId": 70, + "slip44": 1170, + "explorers": [{ "name": "hooscan", "url": "https://www.hooscan.com", "standard": "EIP3091" }] + }, + { + "name": "Conflux eSpace (Testnet)", + "chain": "Conflux", + "rpc": ["https://evmtestnet.confluxrpc.com"], + "faucets": ["https://faucet.confluxnetwork.org"], + "nativeCurrency": { "name": "CFX", "symbol": "CFX", "decimals": 18 }, + "infoURL": "https://confluxnetwork.org", + "shortName": "cfxtest", + "chainId": 71, + "networkId": 71, + "icon": "conflux", + "explorers": [ + { "name": "Conflux Scan", "url": "https://evmtestnet.confluxscan.net", "standard": "none" } + ] + }, + { + "name": "DxChain Testnet", + "chain": "DxChain", + "rpc": ["https://testnet-http.dxchain.com"], + "faucets": ["https://faucet.dxscan.io"], + "nativeCurrency": { "name": "DxChain Testnet", "symbol": "DX", "decimals": 18 }, + "infoURL": "https://testnet.dxscan.io/", + "shortName": "dxc", + "chainId": 72, + "networkId": 72 + }, + { + "name": "FNCY", + "chain": "FNCY", + "rpc": ["https://fncy-seed1.fncy.world"], + "faucets": ["https://faucet-testnet.fncy.world"], + "nativeCurrency": { "name": "FNCY", "symbol": "FNCY", "decimals": 18 }, + "infoURL": "https://fncyscan.fncy.world", + "shortName": "FNCY", + "chainId": 73, + "networkId": 73, + "icon": "fncy", + "explorers": [ + { + "name": "fncy scan", + "url": "https://fncyscan.fncy.world", + "icon": "fncy", + "standard": "EIP3091" + } + ] + }, + { + "name": "IDChain Mainnet", + "chain": "IDChain", + "rpc": ["https://idchain.one/rpc/", "wss://idchain.one/ws/"], + "faucets": [], + "nativeCurrency": { "name": "EIDI", "symbol": "EIDI", "decimals": 18 }, + "infoURL": "https://idchain.one/begin/", + "shortName": "idchain", + "chainId": 74, + "networkId": 74, + "icon": "idchain", + "explorers": [ + { "name": "explorer", "url": "https://explorer.idchain.one", "standard": "EIP3091" } + ] + }, + { + "name": "Decimal Smart Chain Mainnet", + "chain": "DSC", + "rpc": ["https://node.decimalchain.com/web3"], + "faucets": [], + "nativeCurrency": { "name": "Decimal", "symbol": "DEL", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://decimalchain.com", + "shortName": "DSC", + "chainId": 75, + "networkId": 75, + "icon": "dsc", + "explorers": [ + { + "name": "DSC Explorer Mainnet", + "url": "https://explorer.decimalchain.com", + "icon": "dsc", + "standard": "EIP3091" + } + ] + }, + { + "name": "Mix", + "chain": "MIX", + "rpc": ["https://rpc2.mix-blockchain.org:8647"], + "faucets": [], + "nativeCurrency": { "name": "Mix Ether", "symbol": "MIX", "decimals": 18 }, + "infoURL": "https://mix-blockchain.org", + "shortName": "mix", + "chainId": 76, + "networkId": 76, + "slip44": 76 + }, + { + "name": "POA Network Sokol", + "chain": "POA", + "rpc": [ + "https://sokol.poa.network", + "wss://sokol.poa.network/wss", + "ws://sokol.poa.network:8546" + ], + "faucets": ["https://faucet.poa.network"], + "nativeCurrency": { "name": "POA Sokol Ether", "symbol": "SPOA", "decimals": 18 }, + "infoURL": "https://poa.network", + "shortName": "spoa", + "chainId": 77, + "networkId": 77, + "explorers": [ + { "name": "blockscout", "url": "https://blockscout.com/poa/sokol", "standard": "none" } + ] + }, + { + "name": "PrimusChain mainnet", + "chain": "PC", + "rpc": ["https://ethnode.primusmoney.com/mainnet"], + "faucets": [], + "nativeCurrency": { "name": "Primus Ether", "symbol": "PETH", "decimals": 18 }, + "infoURL": "https://primusmoney.com", + "shortName": "primuschain", + "chainId": 78, + "networkId": 78 + }, + { + "name": "Zenith Mainnet", + "chain": "Zenith", + "rpc": [ + "https://dataserver-us-1.zenithchain.co/", + "https://dataserver-asia-3.zenithchain.co/", + "https://dataserver-asia-4.zenithchain.co/", + "https://dataserver-asia-2.zenithchain.co/", + "https://dataserver-asia-5.zenithchain.co/", + "https://dataserver-asia-6.zenithchain.co/", + "https://dataserver-asia-7.zenithchain.co/" + ], + "faucets": [], + "nativeCurrency": { "name": "ZENITH", "symbol": "ZENITH", "decimals": 18 }, + "infoURL": "https://www.zenithchain.co/", + "chainId": 79, + "networkId": 79, + "shortName": "zenith", + "explorers": [ + { "name": "zenith scan", "url": "https://scan.zenithchain.co", "standard": "EIP3091" } + ] + }, + { + "name": "GeneChain", + "chain": "GeneChain", + "rpc": ["https://rpc.genechain.io"], + "faucets": [], + "nativeCurrency": { "name": "RNA", "symbol": "RNA", "decimals": 18 }, + "infoURL": "https://scan.genechain.io/", + "shortName": "GeneChain", + "chainId": 80, + "networkId": 80, + "explorers": [ + { "name": "GeneChain Scan", "url": "https://scan.genechain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Zenith Testnet (Vilnius)", + "chain": "Zenith", + "rpc": ["https://vilnius.zenithchain.co/http"], + "faucets": ["https://faucet.zenithchain.co/"], + "nativeCurrency": { "name": "Vilnius", "symbol": "VIL", "decimals": 18 }, + "infoURL": "https://www.zenithchain.co/", + "chainId": 81, + "networkId": 81, + "shortName": "VIL", + "explorers": [ + { + "name": "vilnius scan", + "url": "https://vilnius.scan.zenithchain.co", + "standard": "EIP3091" + } + ] + }, + { + "name": "Meter Mainnet", + "chain": "METER", + "rpc": ["https://rpc.meter.io"], + "faucets": ["https://faucet.meter.io"], + "nativeCurrency": { "name": "Meter", "symbol": "MTR", "decimals": 18 }, + "infoURL": "https://www.meter.io", + "shortName": "Meter", + "chainId": 82, + "networkId": 82, + "explorers": [ + { "name": "Meter Mainnet Scan", "url": "https://scan.meter.io", "standard": "EIP3091" } + ] + }, + { + "name": "Meter Testnet", + "chain": "METER Testnet", + "rpc": ["https://rpctest.meter.io"], + "faucets": ["https://faucet-warringstakes.meter.io"], + "nativeCurrency": { "name": "Meter", "symbol": "MTR", "decimals": 18 }, + "infoURL": "https://www.meter.io", + "shortName": "MeterTest", + "chainId": 83, + "networkId": 83, + "explorers": [ + { + "name": "Meter Testnet Scan", + "url": "https://scan-warringstakes.meter.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "GateChain Testnet", + "chainId": 85, + "shortName": "gttest", + "chain": "GTTEST", + "networkId": 85, + "nativeCurrency": { "name": "GateToken", "symbol": "GT", "decimals": 18 }, + "rpc": ["https://testnet.gatenode.cc"], + "faucets": ["https://www.gatescan.org/testnet/faucet"], + "explorers": [ + { "name": "GateScan", "url": "https://www.gatescan.org/testnet", "standard": "EIP3091" } + ], + "infoURL": "https://www.gatechain.io" + }, + { + "name": "GateChain Mainnet", + "chainId": 86, + "shortName": "gt", + "chain": "GT", + "networkId": 86, + "nativeCurrency": { "name": "GateToken", "symbol": "GT", "decimals": 18 }, + "rpc": ["https://evm.gatenode.cc"], + "faucets": ["https://www.gatescan.org/faucet"], + "explorers": [{ "name": "GateScan", "url": "https://www.gatescan.org", "standard": "EIP3091" }], + "infoURL": "https://www.gatechain.io" + }, + { + "name": "Nova Network", + "chain": "NNW", + "icon": "novanetwork", + "rpc": [ + "https://connect.novanetwork.io", + "https://0x57.redjackstudio.com", + "https://rpc.novanetwork.io:9070" + ], + "faucets": [], + "nativeCurrency": { "name": "Supernova", "symbol": "SNT", "decimals": 18 }, + "infoURL": "https://novanetwork.io", + "shortName": "nnw", + "chainId": 87, + "networkId": 87, + "explorers": [ + { "name": "novanetwork", "url": "https://explorer.novanetwork.io", "standard": "EIP3091" } + ] + }, + { + "name": "TomoChain", + "chain": "TOMO", + "rpc": ["https://rpc.tomochain.com"], + "faucets": [], + "nativeCurrency": { "name": "TomoChain", "symbol": "TOMO", "decimals": 18 }, + "infoURL": "https://tomochain.com", + "shortName": "tomo", + "chainId": 88, + "networkId": 88, + "slip44": 889 + }, + { + "name": "TomoChain Testnet", + "chain": "TOMO", + "rpc": ["https://rpc.testnet.tomochain.com"], + "faucets": [], + "nativeCurrency": { "name": "TomoChain", "symbol": "TOMO", "decimals": 18 }, + "infoURL": "https://tomochain.com", + "shortName": "tomot", + "chainId": 89, + "networkId": 89, + "slip44": 889 + }, + { + "name": "Garizon Stage0", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s0.garizon.net/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-s0", + "chainId": 90, + "networkId": 90, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ] + }, + { + "name": "Garizon Stage1", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s1.garizon.net/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-s1", + "chainId": 91, + "networkId": 91, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-90", "type": "shard" } + }, + { + "name": "Garizon Stage2", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s2.garizon.net/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-s2", + "chainId": 92, + "networkId": 92, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-90", "type": "shard" } + }, + { + "name": "Garizon Stage3", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s3.garizon.net/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-s3", + "chainId": 93, + "networkId": 93, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-90", "type": "shard" } + }, + { + "name": "CryptoKylin Testnet", + "chain": "EOS", + "rpc": ["https://kylin.eosargentina.io"], + "faucets": [], + "nativeCurrency": { "name": "EOS", "symbol": "EOS", "decimals": 18 }, + "infoURL": "https://www.cryptokylin.io/", + "shortName": "KylinTestnet", + "chainId": 95, + "networkId": 95, + "explorers": [{ "name": "eosq", "url": "https://kylin.eosargentina.io", "standard": "EIP3091" }] + }, + { + "name": "Bitkub Chain", + "chain": "BKC", + "icon": "bkc", + "rpc": ["https://rpc.bitkubchain.io", "wss://wss.bitkubchain.io"], + "faucets": [], + "nativeCurrency": { "name": "Bitkub Coin", "symbol": "KUB", "decimals": 18 }, + "infoURL": "https://www.bitkubchain.com/", + "shortName": "bkc", + "chainId": 96, + "networkId": 96, + "explorers": [ + { + "name": "Bitkub Chain Explorer", + "url": "https://bkcscan.com", + "standard": "none", + "icon": "bkc" + } + ], + "redFlags": ["reusedChainId"] + }, + { + "name": "Binance Smart Chain Testnet", + "chain": "BSC", + "rpc": [ + "https://data-seed-prebsc-1-s1.binance.org:8545", + "https://data-seed-prebsc-2-s1.binance.org:8545", + "https://data-seed-prebsc-1-s2.binance.org:8545", + "https://data-seed-prebsc-2-s2.binance.org:8545", + "https://data-seed-prebsc-1-s3.binance.org:8545", + "https://data-seed-prebsc-2-s3.binance.org:8545" + ], + "faucets": ["https://testnet.binance.org/faucet-smart"], + "nativeCurrency": { "name": "Binance Chain Native Token", "symbol": "tBNB", "decimals": 18 }, + "infoURL": "https://testnet.binance.org/", + "shortName": "bnbt", + "chainId": 97, + "networkId": 97, + "explorers": [ + { "name": "bscscan-testnet", "url": "https://testnet.bscscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "POA Network Core", + "chain": "POA", + "rpc": ["https://core.poa.network"], + "faucets": [], + "nativeCurrency": { "name": "POA Network Core Ether", "symbol": "POA", "decimals": 18 }, + "infoURL": "https://poa.network", + "shortName": "poa", + "chainId": 99, + "networkId": 99, + "slip44": 178, + "explorers": [ + { "name": "blockscout", "url": "https://blockscout.com/poa/core", "standard": "none" } + ] + }, + { + "name": "Gnosis", + "chain": "GNO", + "icon": "gnosis", + "rpc": [ + "https://rpc.gnosischain.com", + "https://rpc.ankr.com/gnosis", + "https://gnosischain-rpc.gateway.pokt.network", + "https://gnosis-mainnet.public.blastapi.io", + "wss://rpc.gnosischain.com/wss" + ], + "faucets": [ + "https://gnosisfaucet.com", + "https://faucet.gimlu.com/gnosis", + "https://stakely.io/faucet/gnosis-chain-xdai", + "https://faucet.prussia.dev/xdai" + ], + "nativeCurrency": { "name": "xDAI", "symbol": "xDAI", "decimals": 18 }, + "infoURL": "https://docs.gnosischain.com", + "shortName": "gno", + "chainId": 100, + "networkId": 100, + "slip44": 700, + "explorers": [ + { "name": "gnosisscan", "url": "https://gnosisscan.io", "standard": "EIP3091" }, + { + "name": "blockscout", + "url": "https://blockscout.com/xdai/mainnet", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "EtherInc", + "chain": "ETI", + "rpc": ["https://api.einc.io/jsonrpc/mainnet"], + "faucets": [], + "nativeCurrency": { "name": "EtherInc Ether", "symbol": "ETI", "decimals": 18 }, + "infoURL": "https://einc.io", + "shortName": "eti", + "chainId": 101, + "networkId": 1, + "slip44": 464 + }, + { + "name": "Web3Games Testnet", + "chain": "Web3Games", + "icon": "web3games", + "rpc": [ + "https://testnet-rpc-0.web3games.org/evm", + "https://testnet-rpc-1.web3games.org/evm", + "https://testnet-rpc-2.web3games.org/evm" + ], + "faucets": [], + "nativeCurrency": { "name": "Web3Games", "symbol": "W3G", "decimals": 18 }, + "infoURL": "https://web3games.org/", + "shortName": "tw3g", + "chainId": 102, + "networkId": 102 + }, + { + "name": "Kaiba Lightning Chain Testnet", + "chain": "tKLC", + "rpc": ["https://klc.live/"], + "faucets": [], + "nativeCurrency": { "name": "Kaiba Testnet Token", "symbol": "tKAIBA", "decimals": 18 }, + "infoURL": "https://kaibadefi.com", + "shortName": "tklc", + "chainId": 104, + "networkId": 104, + "icon": "kaiba", + "explorers": [ + { + "name": "kaibascan", + "url": "https://kaibascan.io", + "icon": "kaibascan", + "standard": "EIP3091" + } + ] + }, + { + "name": "Web3Games Devnet", + "chain": "Web3Games", + "icon": "web3games", + "rpc": ["https://devnet.web3games.org/evm"], + "faucets": [], + "nativeCurrency": { "name": "Web3Games", "symbol": "W3G", "decimals": 18 }, + "infoURL": "https://web3games.org/", + "shortName": "dw3g", + "chainId": 105, + "networkId": 105, + "explorers": [ + { + "name": "Web3Games Explorer", + "url": "https://explorer-devnet.web3games.org", + "standard": "none" + } + ] + }, + { + "name": "Velas EVM Mainnet", + "chain": "Velas", + "icon": "velas", + "rpc": ["https://evmexplorer.velas.com/rpc", "https://explorer.velas.com/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Velas", "symbol": "VLX", "decimals": 18 }, + "infoURL": "https://velas.com", + "shortName": "vlx", + "chainId": 106, + "networkId": 106, + "explorers": [ + { "name": "Velas Explorer", "url": "https://evmexplorer.velas.com", "standard": "EIP3091" } + ] + }, + { + "name": "Nebula Testnet", + "chain": "NTN", + "icon": "nebulatestnet", + "rpc": ["https://testnet.rpc.novanetwork.io:9070"], + "faucets": ["https://faucet.novanetwork.io"], + "nativeCurrency": { "name": "Nebula X", "symbol": "NBX", "decimals": 18 }, + "infoURL": "https://novanetwork.io", + "shortName": "ntn", + "chainId": 107, + "networkId": 107, + "explorers": [ + { "name": "nebulatestnet", "url": "https://explorer.novanetwork.io", "standard": "EIP3091" } + ] + }, + { + "name": "ThunderCore Mainnet", + "chain": "TT", + "rpc": [ + "https://mainnet-rpc.thundercore.com", + "https://mainnet-rpc.thundertoken.net", + "https://mainnet-rpc.thundercore.io" + ], + "faucets": ["https://faucet.thundercore.com"], + "nativeCurrency": { "name": "ThunderCore Token", "symbol": "TT", "decimals": 18 }, + "infoURL": "https://thundercore.com", + "shortName": "TT", + "chainId": 108, + "networkId": 108, + "slip44": 1001, + "explorers": [ + { + "name": "thundercore-viewblock", + "url": "https://viewblock.io/thundercore", + "standard": "EIP3091" + } + ] + }, + { + "name": "Proton Testnet", + "chain": "XPR", + "rpc": ["https://protontestnet.greymass.com/"], + "faucets": [], + "nativeCurrency": { "name": "Proton", "symbol": "XPR", "decimals": 4 }, + "infoURL": "https://protonchain.com", + "shortName": "xpr", + "chainId": 110, + "networkId": 110 + }, + { + "name": "EtherLite Chain", + "chain": "ETL", + "rpc": ["https://rpc.etherlite.org"], + "faucets": ["https://etherlite.org/faucets"], + "nativeCurrency": { "name": "EtherLite", "symbol": "ETL", "decimals": 18 }, + "infoURL": "https://etherlite.org", + "shortName": "ETL", + "chainId": 111, + "networkId": 111, + "icon": "etherlite" + }, + { + "name": "Dehvo", + "chain": "Dehvo", + "rpc": [ + "https://connect.dehvo.com", + "https://rpc.dehvo.com", + "https://rpc1.dehvo.com", + "https://rpc2.dehvo.com" + ], + "faucets": ["https://buy.dehvo.com"], + "nativeCurrency": { "name": "Dehvo", "symbol": "Deh", "decimals": 18 }, + "infoURL": "https://dehvo.com", + "shortName": "deh", + "chainId": 113, + "networkId": 113, + "slip44": 714, + "explorers": [ + { "name": "Dehvo Explorer", "url": "https://explorer.dehvo.com", "standard": "EIP3091" } + ] + }, + { + "name": "Flare Testnet Coston2", + "chain": "FLR", + "icon": "coston2", + "rpc": ["https://coston2-api.flare.network/ext/bc/C/rpc"], + "faucets": ["https://coston2-faucet.towolabs.com"], + "nativeCurrency": { "name": "Coston2 Flare", "symbol": "C2FLR", "decimals": 18 }, + "infoURL": "https://flare.xyz", + "shortName": "c2flr", + "chainId": 114, + "networkId": 114, + "explorers": [ + { + "name": "blockscout", + "url": "https://coston2-explorer.flare.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "DeBank Testnet", + "chain": "DeBank", + "rpc": [], + "faucets": [], + "icon": "debank", + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://debank.com", + "shortName": "debank-testnet", + "chainId": 115, + "networkId": 115, + "explorers": [] + }, + { + "name": "DeBank Mainnet", + "chain": "DeBank", + "rpc": [], + "faucets": [], + "icon": "debank", + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://debank.com", + "shortName": "debank-mainnet", + "chainId": 116, + "networkId": 116, + "explorers": [] + }, + { + "name": "Uptick Mainnet", + "chain": "Uptick", + "rpc": ["https://json-rpc.uptick.network"], + "faucets": [], + "nativeCurrency": { "name": "Uptick", "symbol": "UPTICK", "decimals": 18 }, + "infoURL": "https://www.uptick.network", + "shortName": "auptick", + "chainId": 117, + "networkId": 117, + "icon": "uptick", + "explorers": [ + { + "name": "Uptick Explorer", + "url": "https://evm-explorer.uptick.network", + "icon": "uptick", + "standard": "none" + } + ] + }, + { + "name": "Arcology Testnet", + "chain": "Arcology", + "icon": "acolicon", + "rpc": ["https://testnet.arcology.network/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Arcology Coin", "symbol": "Acol", "decimals": 18 }, + "infoURL": "https://arcology.network/", + "shortName": "arcology", + "chainId": 118, + "networkId": 118, + "explorers": [ + { "name": "arcology", "url": "https://testnet.arcology.network/explorer", "standard": "none" } + ] + }, + { + "name": "ENULS Mainnet", + "chain": "ENULS", + "rpc": ["https://evmapi.nuls.io", "https://evmapi2.nuls.io"], + "faucets": [], + "nativeCurrency": { "name": "NULS", "symbol": "NULS", "decimals": 18 }, + "infoURL": "https://nuls.io", + "shortName": "enuls", + "chainId": 119, + "networkId": 119, + "icon": "enuls", + "explorers": [ + { + "name": "enulsscan", + "url": "https://evmscan.nuls.io", + "icon": "enuls", + "standard": "EIP3091" + } + ] + }, + { + "name": "ENULS Testnet", + "chain": "ENULS", + "rpc": ["https://beta.evmapi.nuls.io", "https://beta.evmapi2.nuls.io"], + "faucets": ["http://faucet.nuls.io"], + "nativeCurrency": { "name": "NULS", "symbol": "NULS", "decimals": 18 }, + "infoURL": "https://nuls.io", + "shortName": "enulst", + "chainId": 120, + "networkId": 120, + "icon": "enuls", + "explorers": [ + { + "name": "enulsscan", + "url": "https://beta.evmscan.nuls.io", + "icon": "enuls", + "standard": "EIP3091" + } + ] + }, + { + "name": "Realchain Mainnet", + "chain": "REAL", + "rpc": [ + "https://rcl-dataseed1.rclsidechain.com", + "https://rcl-dataseed2.rclsidechain.com", + "https://rcl-dataseed3.rclsidechain.com", + "https://rcl-dataseed4.rclsidechain.com", + "wss://rcl-dataseed1.rclsidechain.com/v1/", + "wss://rcl-dataseed2.rclsidechain.com/v1/", + "wss://rcl-dataseed3.rclsidechain.com/v1/", + "wss://rcl-dataseed4.rclsidechain.com/v1/" + ], + "faucets": ["https://faucet.rclsidechain.com"], + "nativeCurrency": { "name": "Realchain", "symbol": "REAL", "decimals": 18 }, + "infoURL": "https://www.rclsidechain.com/", + "shortName": "REAL", + "chainId": 121, + "networkId": 121, + "slip44": 714, + "explorers": [{ "name": "realscan", "url": "https://rclscan.com", "standard": "EIP3091" }] + }, + { + "name": "Fuse Mainnet", + "chain": "FUSE", + "rpc": ["https://rpc.fuse.io"], + "faucets": [], + "nativeCurrency": { "name": "Fuse", "symbol": "FUSE", "decimals": 18 }, + "infoURL": "https://fuse.io/", + "shortName": "fuse", + "chainId": 122, + "networkId": 122 + }, + { + "name": "Fuse Sparknet", + "chain": "fuse", + "rpc": ["https://rpc.fusespark.io"], + "faucets": ["https://get.fusespark.io"], + "nativeCurrency": { "name": "Spark", "symbol": "SPARK", "decimals": 18 }, + "infoURL": "https://docs.fuse.io/general/fuse-network-blockchain/fuse-testnet", + "shortName": "spark", + "chainId": 123, + "networkId": 123 + }, + { + "name": "Decentralized Web Mainnet", + "shortName": "dwu", + "chain": "DWU", + "chainId": 124, + "networkId": 124, + "rpc": ["https://decentralized-web.tech/dw_rpc.php"], + "faucets": [], + "infoURL": "https://decentralized-web.tech/dw_chain.php", + "nativeCurrency": { "name": "Decentralized Web Utility", "symbol": "DWU", "decimals": 18 } + }, + { + "name": "OYchain Testnet", + "chain": "OYchain", + "rpc": ["https://rpc.testnet.oychain.io"], + "faucets": ["https://faucet.oychain.io"], + "nativeCurrency": { "name": "OYchain Token", "symbol": "OY", "decimals": 18 }, + "infoURL": "https://www.oychain.io", + "shortName": "OYchainTestnet", + "chainId": 125, + "networkId": 125, + "slip44": 125, + "explorers": [ + { + "name": "OYchain Testnet Explorer", + "url": "https://explorer.testnet.oychain.io", + "standard": "none" + } + ] + }, + { + "name": "OYchain Mainnet", + "chain": "OYchain", + "icon": "oychain", + "rpc": ["https://rpc.mainnet.oychain.io"], + "faucets": [], + "nativeCurrency": { "name": "OYchain Token", "symbol": "OY", "decimals": 18 }, + "infoURL": "https://www.oychain.io", + "shortName": "OYchainMainnet", + "chainId": 126, + "networkId": 126, + "slip44": 126, + "explorers": [ + { + "name": "OYchain Mainnet Explorer", + "url": "https://explorer.oychain.io", + "standard": "none" + } + ] + }, + { + "name": "Factory 127 Mainnet", + "chain": "FETH", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Factory 127 Token", "symbol": "FETH", "decimals": 18 }, + "infoURL": "https://www.factory127.com", + "shortName": "feth", + "chainId": 127, + "networkId": 127, + "slip44": 127 + }, + { + "name": "Huobi ECO Chain Mainnet", + "chain": "Heco", + "rpc": ["https://http-mainnet.hecochain.com", "wss://ws-mainnet.hecochain.com"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "Huobi ECO Chain Native Token", "symbol": "HT", "decimals": 18 }, + "infoURL": "https://www.hecochain.com", + "shortName": "heco", + "chainId": 128, + "networkId": 128, + "slip44": 1010, + "explorers": [{ "name": "hecoinfo", "url": "https://hecoinfo.com", "standard": "EIP3091" }] + }, + { + "name": "iExec Sidechain", + "chain": "Bellecour", + "icon": "rlc", + "rpc": ["https://bellecour.iex.ec"], + "faucets": [], + "nativeCurrency": { "name": "xRLC", "symbol": "xRLC", "decimals": 18 }, + "infoURL": "https://iex.ec", + "shortName": "rlc", + "chainId": 134, + "networkId": 134, + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout.bellecour.iex.ec", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "Alyx Chain Testnet", + "chain": "Alyx Chain Testnet", + "rpc": ["https://testnet-rpc.alyxchain.com"], + "faucets": ["https://faucet.alyxchain.com"], + "nativeCurrency": { "name": "Alyx Testnet Native Token", "symbol": "ALYX", "decimals": 18 }, + "infoURL": "https://www.alyxchain.com", + "shortName": "AlyxTestnet", + "chainId": 135, + "networkId": 135, + "explorers": [ + { "name": "alyx testnet scan", "url": "https://testnet.alyxscan.com", "standard": "EIP3091" } + ], + "icon": "alyx" + }, + { + "name": "Polygon Mainnet", + "chain": "Polygon", + "icon": "polygon", + "rpc": [ + "https://polygon-rpc.com/", + "https://rpc-mainnet.matic.network", + "https://matic-mainnet.chainstacklabs.com", + "https://rpc-mainnet.maticvigil.com", + "https://rpc-mainnet.matic.quiknode.pro", + "https://matic-mainnet-full-rpc.bwarelabs.com", + "https://polygon-bor.publicnode.com" + ], + "faucets": [], + "nativeCurrency": { "name": "MATIC", "symbol": "MATIC", "decimals": 18 }, + "infoURL": "https://polygon.technology/", + "shortName": "matic", + "chainId": 137, + "networkId": 137, + "slip44": 966, + "explorers": [ + { "name": "polygonscan", "url": "https://polygonscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Defi Oracle Meta Mainnet", + "chain": "dfiometa", + "icon": "defioraclemeta", + "rpc": ["https://rpc.public-0138.defi-oracle.io", "wss://rpc.public-0138.defi-oracle.io"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://defi-oracle.io/", + "shortName": "dfio-meta-main", + "chainId": 138, + "networkId": 1, + "slip44": 60, + "ens": { "registry": "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85" }, + "explorers": [ + { "name": "Quorum Explorer", "url": "https://public-0138.defi-oracle.io", "standard": "none" } + ] + }, + { + "name": "WoopChain Mainnet", + "chain": "WOOP", + "icon": "woopchain", + "rpc": ["https://rpc.woop.ai/rpc"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "WoopCoin", "symbol": "WOOC", "decimals": 18 }, + "infoURL": "https://wikiwoop.com", + "shortName": "woop", + "chainId": 139, + "networkId": 139, + "explorers": [ + { "name": "wikiwoop", "url": "https://explorer.wikiwoop.com", "standard": "EIP3091" } + ] + }, + { + "name": "Openpiece Testnet", + "chain": "OPENPIECE", + "icon": "openpiece", + "rpc": ["https://testnet.openpiece.io"], + "faucets": [], + "nativeCurrency": { "name": "Belly", "symbol": "BELLY", "decimals": 18 }, + "infoURL": "https://cryptopiece.online", + "shortName": "OPtest", + "chainId": 141, + "networkId": 141, + "explorers": [ + { "name": "Belly Scan", "url": "https://testnet.bellyscan.com", "standard": "none" } + ] + }, + { + "name": "DAX CHAIN", + "chain": "DAX", + "rpc": ["https://rpc.prodax.io"], + "faucets": [], + "nativeCurrency": { "name": "Prodax", "symbol": "DAX", "decimals": 18 }, + "infoURL": "https://prodax.io/", + "shortName": "dax", + "chainId": 142, + "networkId": 142 + }, + { + "name": "PHI Network v2", + "chain": "PHI", + "rpc": ["https://connect.phi.network"], + "faucets": [], + "nativeCurrency": { "name": "PHI", "symbol": "Φ", "decimals": 18 }, + "infoURL": "https://phi.network", + "shortName": "PHI", + "chainId": 144, + "networkId": 144, + "icon": "phi", + "explorers": [ + { "name": "Phiscan", "url": "https://phiscan.com", "icon": "phi", "standard": "none" } + ] + }, + { + "name": "Armonia Eva Chain Mainnet", + "chain": "Eva", + "rpc": ["https://evascan.io/api/eth-rpc/"], + "faucets": [], + "nativeCurrency": { + "name": "Armonia Multichain Native Token", + "symbol": "AMAX", + "decimals": 18 + }, + "infoURL": "https://amax.network", + "shortName": "eva", + "chainId": 160, + "networkId": 160, + "status": "incubating" + }, + { + "name": "Armonia Eva Chain Testnet", + "chain": "Wall-e", + "rpc": ["https://testnet.evascan.io/api/eth-rpc/"], + "faucets": [], + "nativeCurrency": { + "name": "Armonia Multichain Native Token", + "symbol": "AMAX", + "decimals": 18 + }, + "infoURL": "https://amax.network", + "shortName": "wall-e", + "chainId": 161, + "networkId": 161, + "explorers": [ + { "name": "blockscout - evascan", "url": "https://testnet.evascan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Lightstreams Testnet", + "chain": "PHT", + "rpc": ["https://node.sirius.lightstreams.io"], + "faucets": ["https://discuss.lightstreams.network/t/request-test-tokens"], + "nativeCurrency": { "name": "Lightstreams PHT", "symbol": "PHT", "decimals": 18 }, + "infoURL": "https://explorer.sirius.lightstreams.io", + "shortName": "tpht", + "chainId": 162, + "networkId": 162 + }, + { + "name": "Lightstreams Mainnet", + "chain": "PHT", + "rpc": ["https://node.mainnet.lightstreams.io"], + "faucets": [], + "nativeCurrency": { "name": "Lightstreams PHT", "symbol": "PHT", "decimals": 18 }, + "infoURL": "https://explorer.lightstreams.io", + "shortName": "pht", + "chainId": 163, + "networkId": 163 + }, + { + "name": "Atoshi Testnet", + "chain": "ATOSHI", + "icon": "atoshi", + "rpc": ["https://node.atoshi.io/"], + "faucets": [], + "nativeCurrency": { "name": "ATOSHI", "symbol": "ATOS", "decimals": 18 }, + "infoURL": "https://atoshi.org", + "shortName": "atoshi", + "chainId": 167, + "networkId": 167, + "explorers": [ + { "name": "atoshiscan", "url": "https://scan.atoverse.info", "standard": "EIP3091" } + ] + }, + { + "name": "AIOZ Network", + "chain": "AIOZ", + "icon": "aioz", + "rpc": ["https://eth-dataseed.aioz.network"], + "faucets": [], + "nativeCurrency": { "name": "AIOZ", "symbol": "AIOZ", "decimals": 18 }, + "infoURL": "https://aioz.network", + "shortName": "aioz", + "chainId": 168, + "networkId": 168, + "slip44": 60, + "explorers": [ + { + "name": "AIOZ Network Explorer", + "url": "https://explorer.aioz.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "HOO Smart Chain Testnet", + "chain": "ETH", + "rpc": ["https://http-testnet.hoosmartchain.com"], + "faucets": ["https://faucet-testnet.hscscan.com/"], + "nativeCurrency": { "name": "HOO", "symbol": "HOO", "decimals": 18 }, + "infoURL": "https://www.hoosmartchain.com", + "shortName": "hoosmartchain", + "chainId": 170, + "networkId": 170 + }, + { + "name": "Latam-Blockchain Resil Testnet", + "chain": "Resil", + "rpc": ["https://rpc.latam-blockchain.com", "wss://ws.latam-blockchain.com"], + "faucets": ["https://faucet.latam-blockchain.com"], + "nativeCurrency": { + "name": "Latam-Blockchain Resil Test Native Token", + "symbol": "usd", + "decimals": 18 + }, + "infoURL": "https://latam-blockchain.com", + "shortName": "resil", + "chainId": 172, + "networkId": 172 + }, + { + "name": "AME Chain Mainnet", + "chain": "AME", + "rpc": ["https://node1.amechain.io/"], + "faucets": [], + "nativeCurrency": { "name": "AME", "symbol": "AME", "decimals": 18 }, + "infoURL": "https://amechain.io/", + "shortName": "ame", + "chainId": 180, + "networkId": 180, + "explorers": [{ "name": "AME Scan", "url": "https://amescan.io", "standard": "EIP3091" }] + }, + { + "name": "Seele Mainnet", + "chain": "Seele", + "rpc": ["https://rpc.seelen.pro/"], + "faucets": [], + "nativeCurrency": { "name": "Seele", "symbol": "Seele", "decimals": 18 }, + "infoURL": "https://seelen.pro/", + "shortName": "Seele", + "chainId": 186, + "networkId": 186, + "explorers": [{ "name": "seeleview", "url": "https://seeleview.net", "standard": "none" }] + }, + { + "name": "BMC Mainnet", + "chain": "BMC", + "rpc": ["https://mainnet.bmcchain.com/"], + "faucets": [], + "nativeCurrency": { "name": "BTM", "symbol": "BTM", "decimals": 18 }, + "infoURL": "https://bmc.bytom.io/", + "shortName": "BMC", + "chainId": 188, + "networkId": 188, + "explorers": [{ "name": "Blockmeta", "url": "https://bmc.blockmeta.com", "standard": "none" }] + }, + { + "name": "BMC Testnet", + "chain": "BMC", + "rpc": ["https://testnet.bmcchain.com"], + "faucets": [], + "nativeCurrency": { "name": "BTM", "symbol": "BTM", "decimals": 18 }, + "infoURL": "https://bmc.bytom.io/", + "shortName": "BMCT", + "chainId": 189, + "networkId": 189, + "explorers": [ + { "name": "Blockmeta", "url": "https://bmctestnet.blockmeta.com", "standard": "none" } + ] + }, + { + "name": "Crypto Emergency", + "chain": "CEM", + "rpc": ["https://cemchain.com"], + "faucets": [], + "nativeCurrency": { "name": "Crypto Emergency", "symbol": "CEM", "decimals": 18 }, + "infoURL": "https://cemblockchain.com/", + "shortName": "cem", + "chainId": 193, + "networkId": 193, + "explorers": [{ "name": "cemscan", "url": "https://cemscan.com", "standard": "EIP3091" }] + }, + { + "name": "OKBChain Testnet", + "chain": "okbchain", + "rpc": ["https://okbtestrpc.okbchain.org"], + "faucets": ["https://www.oklink.com/okbc-test"], + "nativeCurrency": { + "name": "OKBChain Global Utility Token in testnet", + "symbol": "OKB", + "decimals": 18 + }, + "features": [], + "infoURL": "https://www.okx.com/okbc/docs/dev/quick-start/introduction/introduction-to-okbchain", + "shortName": "tokb", + "chainId": 195, + "networkId": 195, + "explorers": [ + { "name": "OKLink", "url": "https://www.oklink.com/okbc-test", "standard": "EIP3091" } + ], + "status": "active" + }, + { + "name": "OKBChain Mainnet", + "chain": "okbchain", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "OKBChain Global Utility Token", "symbol": "OKB", "decimals": 18 }, + "features": [], + "infoURL": "https://www.okex.com/okc", + "shortName": "okb", + "chainId": 196, + "networkId": 196, + "explorers": [], + "status": "incubating" + }, + { + "name": "Neutrinos TestNet", + "chain": "NEUTR", + "rpc": ["https://testnet-rpc.neutrinoschain.com"], + "faucets": ["https://neutrinoschain.com/faucet"], + "nativeCurrency": { "name": "Neutrinos", "symbol": "NEUTR", "decimals": 18 }, + "infoURL": "https://docs.neutrinoschain.com", + "shortName": "NEUTR", + "chainId": 197, + "networkId": 197, + "explorers": [ + { "name": "blockscout", "url": "https://testnet.neutrinoschain.com", "standard": "EIP3091" } + ] + }, + { + "name": "BitTorrent Chain Mainnet", + "chain": "BTTC", + "rpc": ["https://rpc.bittorrentchain.io/"], + "faucets": [], + "nativeCurrency": { "name": "BitTorrent", "symbol": "BTT", "decimals": 18 }, + "infoURL": "https://bittorrentchain.io/", + "shortName": "BTT", + "chainId": 199, + "networkId": 199, + "explorers": [ + { "name": "bttcscan", "url": "https://scan.bittorrentchain.io", "standard": "none" } + ] + }, + { + "name": "Arbitrum on xDai", + "chain": "AOX", + "rpc": ["https://arbitrum.xdaichain.com/"], + "faucets": [], + "nativeCurrency": { "name": "xDAI", "symbol": "xDAI", "decimals": 18 }, + "infoURL": "https://xdaichain.com", + "shortName": "aox", + "chainId": 200, + "networkId": 200, + "explorers": [ + { "name": "blockscout", "url": "https://blockscout.com/xdai/arbitrum", "standard": "EIP3091" } + ], + "parent": { "chain": "eip155-100", "type": "L2" } + }, + { + "name": "MOAC testnet", + "chain": "MOAC", + "rpc": ["https://gateway.moac.io/testnet"], + "faucets": [], + "nativeCurrency": { "name": "MOAC", "symbol": "mc", "decimals": 18 }, + "infoURL": "https://moac.io", + "shortName": "moactest", + "chainId": 201, + "networkId": 201, + "explorers": [ + { "name": "moac testnet explorer", "url": "https://testnet.moac.io", "standard": "none" } + ] + }, + { + "name": "Freight Trust Network", + "chain": "EDI", + "rpc": ["http://13.57.207.168:3435", "https://app.freighttrust.net/ftn/${API_KEY}"], + "faucets": ["http://faucet.freight.sh"], + "nativeCurrency": { "name": "Freight Trust Native", "symbol": "0xF", "decimals": 18 }, + "infoURL": "https://freighttrust.com", + "shortName": "EDI", + "chainId": 211, + "networkId": 0 + }, + { + "name": "MAP Makalu", + "title": "MAP Testnet Makalu", + "chain": "MAP", + "rpc": ["https://testnet-rpc.maplabs.io"], + "faucets": ["https://faucet.maplabs.io"], + "nativeCurrency": { "name": "Makalu MAP", "symbol": "MAP", "decimals": 18 }, + "infoURL": "https://maplabs.io", + "shortName": "makalu", + "chainId": 212, + "networkId": 212, + "explorers": [{ "name": "mapscan", "url": "https://testnet.mapscan.io", "standard": "EIP3091" }] + }, + { + "name": "SiriusNet V2", + "chain": "SIN2", + "faucets": [], + "rpc": ["https://rpc2.siriusnet.io"], + "icon": "siriusnet", + "nativeCurrency": { "name": "MCD", "symbol": "MCD", "decimals": 18 }, + "infoURL": "https://siriusnet.io", + "shortName": "SIN2", + "chainId": 217, + "networkId": 217, + "explorers": [ + { "name": "siriusnet explorer", "url": "https://scan.siriusnet.io", "standard": "none" } + ] + }, + { + "name": "SoterOne Mainnet old", + "chain": "SOTER", + "rpc": ["https://rpc.soter.one"], + "faucets": [], + "nativeCurrency": { "name": "SoterOne Mainnet Ether", "symbol": "SOTER", "decimals": 18 }, + "infoURL": "https://www.soterone.com", + "shortName": "SO1-old", + "chainId": 218, + "networkId": 218, + "status": "deprecated" + }, + { + "name": "Permission", + "chain": "ASK", + "rpc": ["https://blockchain-api-mainnet.permission.io/rpc"], + "faucets": [], + "nativeCurrency": { "name": "ASK", "symbol": "ASK", "decimals": 18 }, + "infoURL": "https://permission.io/", + "shortName": "ASK", + "chainId": 222, + "networkId": 2221, + "slip44": 2221, + "status": "deprecated" + }, + { + "name": "LACHAIN Mainnet", + "chain": "LA", + "icon": "lachain", + "rpc": ["https://rpc-mainnet.lachain.io"], + "faucets": [], + "nativeCurrency": { "name": "LA", "symbol": "LA", "decimals": 18 }, + "infoURL": "https://lachain.io", + "shortName": "LA", + "chainId": 225, + "networkId": 225, + "explorers": [{ "name": "blockscout", "url": "https://scan.lachain.io", "standard": "EIP3091" }] + }, + { + "name": "LACHAIN Testnet", + "chain": "TLA", + "icon": "lachain", + "rpc": ["https://rpc-testnet.lachain.io"], + "faucets": [], + "nativeCurrency": { "name": "TLA", "symbol": "TLA", "decimals": 18 }, + "infoURL": "https://lachain.io", + "shortName": "TLA", + "chainId": 226, + "networkId": 226, + "explorers": [ + { "name": "blockscout", "url": "https://scan-test.lachain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Energy Web Chain", + "chain": "Energy Web Chain", + "rpc": ["https://rpc.energyweb.org", "wss://rpc.energyweb.org/ws"], + "faucets": [ + "https://faucet.carbonswap.exchange", + "https://free-online-app.com/faucet-for-eth-evm-chains/" + ], + "nativeCurrency": { "name": "Energy Web Token", "symbol": "EWT", "decimals": 18 }, + "infoURL": "https://energyweb.org", + "shortName": "ewt", + "chainId": 246, + "networkId": 246, + "slip44": 246, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.energyweb.org", "standard": "none" } + ] + }, + { + "name": "Oasys Mainnet", + "chain": "Oasys", + "icon": "oasys", + "rpc": ["https://rpc.mainnet.oasys.games"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://oasys.games", + "shortName": "OAS", + "chainId": 248, + "networkId": 248, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.oasys.games", "standard": "EIP3091" } + ] + }, + { + "name": "Fantom Opera", + "chain": "FTM", + "rpc": ["https://rpc.ftm.tools", "https://fantom.publicnode.com"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "Fantom", "symbol": "FTM", "decimals": 18 }, + "infoURL": "https://fantom.foundation", + "shortName": "ftm", + "chainId": 250, + "networkId": 250, + "icon": "fantom", + "explorers": [ + { "name": "ftmscan", "url": "https://ftmscan.com", "icon": "ftmscan", "standard": "EIP3091" } + ] + }, + { + "name": "Huobi ECO Chain Testnet", + "chain": "Heco", + "rpc": ["https://http-testnet.hecochain.com", "wss://ws-testnet.hecochain.com"], + "faucets": ["https://scan-testnet.hecochain.com/faucet"], + "nativeCurrency": { + "name": "Huobi ECO Chain Test Native Token", + "symbol": "htt", + "decimals": 18 + }, + "infoURL": "https://testnet.hecoinfo.com", + "shortName": "hecot", + "chainId": 256, + "networkId": 256 + }, + { + "name": "Setheum", + "chain": "Setheum", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Setheum", "symbol": "SETM", "decimals": 18 }, + "infoURL": "https://setheum.xyz", + "shortName": "setm", + "chainId": 258, + "networkId": 258 + }, + { + "name": "SUR Blockchain Network", + "chain": "SUR", + "rpc": ["https://sur.nilin.org"], + "faucets": [], + "nativeCurrency": { "name": "Suren", "symbol": "SRN", "decimals": 18 }, + "infoURL": "https://surnet.org", + "shortName": "SUR", + "chainId": 262, + "networkId": 1, + "icon": "SUR", + "explorers": [ + { + "name": "Surnet Explorer", + "url": "https://explorer.surnet.org", + "icon": "SUR", + "standard": "EIP3091" + } + ] + }, + { + "name": "High Performance Blockchain", + "chain": "HPB", + "rpc": ["https://hpbnode.com", "wss://ws.hpbnode.com"], + "faucets": ["https://myhpbwallet.com/"], + "nativeCurrency": { + "name": "High Performance Blockchain Ether", + "symbol": "HPB", + "decimals": 18 + }, + "infoURL": "https://hpb.io", + "shortName": "hpb", + "chainId": 269, + "networkId": 269, + "slip44": 269, + "explorers": [{ "name": "hscan", "url": "https://hscan.org", "standard": "EIP3091" }] + }, + { + "name": "zkSync Era Testnet", + "chain": "ETH", + "rpc": ["https://testnet.era.zksync.dev"], + "faucets": ["https://goerli.portal.zksync.io/faucet"], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://era.zksync.io/docs/", + "shortName": "zksync-goerli", + "chainId": 280, + "networkId": 280, + "icon": "zksync-era", + "explorers": [ + { + "name": "zkSync Era Block Explorer", + "url": "https://goerli.explorer.zksync.io", + "icon": "zksync-era", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://goerli.portal.zksync.io/bridge" }] + } + }, + { + "name": "Boba Network", + "chain": "ETH", + "rpc": ["https://mainnet.boba.network/"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "Boba", + "chainId": 288, + "networkId": 288, + "explorers": [ + { "name": "Bobascan", "url": "https://bobascan.com", "standard": "none" }, + { "name": "Blockscout", "url": "https://blockexplorer.boba.network", "standard": "none" } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://gateway.boba.network" }] + } + }, + { + "name": "Hedera Mainnet", + "chain": "Hedera", + "icon": "hedera", + "rpc": ["https://mainnet.hashio.io/api"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "hbar", "symbol": "HBAR", "decimals": 18 }, + "infoURL": "https://hedera.com", + "shortName": "hedera-mainnet", + "chainId": 295, + "networkId": 295, + "slip44": 3030, + "explorers": [ + { "name": "HashScan", "url": "https://hashscan.io/mainnet/dashboard", "standard": "none" }, + { "name": "Arkhia Explorer", "url": "https://explorer.arkhia.io", "standard": "none" }, + { "name": "DragonGlass", "url": "https://app.dragonglass.me", "standard": "none" }, + { "name": "Hedera Explorer", "url": "https://hederaexplorer.io", "standard": "none" }, + { "name": "Ledger Works Explore", "url": "https://explore.lworks.io", "standard": "none" } + ] + }, + { + "name": "Hedera Testnet", + "chain": "Hedera", + "icon": "hedera", + "rpc": ["https://testnet.hashio.io/api"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": ["https://portal.hedera.com"], + "nativeCurrency": { "name": "hbar", "symbol": "HBAR", "decimals": 18 }, + "infoURL": "https://hedera.com", + "shortName": "hedera-testnet", + "chainId": 296, + "networkId": 296, + "slip44": 3030, + "explorers": [ + { "name": "HashScan", "url": "https://hashscan.io/testnet/dashboard", "standard": "none" }, + { "name": "Arkhia Explorer", "url": "https://explorer.arkhia.io", "standard": "none" }, + { "name": "DragonGlass", "url": "https://app.dragonglass.me", "standard": "none" }, + { "name": "Hedera Explorer", "url": "https://hederaexplorer.io", "standard": "none" }, + { "name": "Ledger Works Explore", "url": "https://explore.lworks.io", "standard": "none" } + ] + }, + { + "name": "Hedera Previewnet", + "chain": "Hedera", + "icon": "hedera", + "rpc": ["https://previewnet.hashio.io/api"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": ["https://portal.hedera.com"], + "nativeCurrency": { "name": "hbar", "symbol": "HBAR", "decimals": 18 }, + "infoURL": "https://hedera.com", + "shortName": "hedera-previewnet", + "chainId": 297, + "networkId": 297, + "slip44": 3030, + "explorers": [ + { "name": "HashScan", "url": "https://hashscan.io/previewnet/dashboard", "standard": "none" } + ] + }, + { + "name": "Hedera Localnet", + "chain": "Hedera", + "icon": "hedera", + "rpc": [], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "hbar", "symbol": "HBAR", "decimals": 18 }, + "infoURL": "https://hedera.com", + "shortName": "hedera-localnet", + "chainId": 298, + "networkId": 298, + "slip44": 3030, + "explorers": [] + }, + { + "name": "Optimism on Gnosis", + "chain": "OGC", + "rpc": ["https://optimism.gnosischain.com", "wss://optimism.gnosischain.com/wss"], + "faucets": ["https://faucet.gimlu.com/gnosis"], + "nativeCurrency": { "name": "xDAI", "symbol": "xDAI", "decimals": 18 }, + "infoURL": "https://www.xdaichain.com/for-developers/optimism-optimistic-rollups-on-gc", + "shortName": "ogc", + "chainId": 300, + "networkId": 300, + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout.com/xdai/optimism", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bobaopera", + "chain": "Bobaopera", + "rpc": [ + "https://bobaopera.boba.network", + "wss://wss.bobaopera.boba.network", + "https://replica.bobaopera.boba.network", + "wss://replica-wss.bobaopera.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "Bobaopera", + "chainId": 301, + "networkId": 301, + "explorers": [ + { + "name": "Bobaopera block explorer", + "url": "https://blockexplorer.bobaopera.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Omax Mainnet", + "chain": "OMAX Chain", + "rpc": ["https://mainapi.omaxray.com"], + "faucets": ["https://faucet.omaxray.com/"], + "nativeCurrency": { "name": "OMAX COIN", "symbol": "OMAX", "decimals": 18 }, + "infoURL": "https://www.omaxcoin.com/", + "shortName": "omax", + "chainId": 311, + "networkId": 311, + "icon": "omaxchain", + "explorers": [ + { + "name": "Omax Chain Explorer", + "url": "https://omaxray.com", + "icon": "omaxray", + "standard": "EIP3091" + } + ] + }, + { + "name": "Filecoin - Mainnet", + "chain": "FIL", + "icon": "filecoin", + "rpc": [ + "https://api.node.glif.io/", + "https://rpc.ankr.com/filecoin", + "https://filecoin-mainnet.chainstacklabs.com/rpc/v1" + ], + "faucets": [], + "nativeCurrency": { "name": "filecoin", "symbol": "FIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin", + "chainId": 314, + "networkId": 314, + "slip44": 461, + "explorers": [ + { "name": "Filfox", "url": "https://filfox.info/en", "standard": "none" }, + { "name": "Beryx", "url": "https://beryx.zondax.ch", "standard": "none" }, + { "name": "Glif Explorer", "url": "https://explorer.glif.io", "standard": "EIP3091" }, + { "name": "Dev.storage", "url": "https://dev.storage", "standard": "none" }, + { "name": "Filscan", "url": "https://filscan.io", "standard": "none" }, + { "name": "Filscout", "url": "https://filscout.io/en", "standard": "none" } + ] + }, + { + "name": "KCC Mainnet", + "chain": "KCC", + "rpc": [ + "https://rpc-mainnet.kcc.network", + "https://kcc.mytokenpocket.vip", + "https://public-rpc.blockpi.io/http/kcc" + ], + "faucets": ["https://faucet.kcc.io/", "https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "KuCoin Token", "symbol": "KCS", "decimals": 18 }, + "infoURL": "https://kcc.io", + "shortName": "kcs", + "chainId": 321, + "networkId": 321, + "slip44": 641, + "explorers": [ + { "name": "KCC Explorer", "url": "https://explorer.kcc.io/en", "standard": "EIP3091" } + ] + }, + { + "name": "KCC Testnet", + "chain": "KCC", + "rpc": ["https://rpc-testnet.kcc.network"], + "faucets": ["https://faucet-testnet.kcc.network"], + "nativeCurrency": { "name": "KuCoin Testnet Token", "symbol": "tKCS", "decimals": 18 }, + "infoURL": "https://scan-testnet.kcc.network", + "shortName": "kcst", + "chainId": 322, + "networkId": 322, + "explorers": [ + { + "name": "kcc-scan-testnet", + "url": "https://scan-testnet.kcc.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "zkSync Era Mainnet", + "chain": "ETH", + "rpc": ["https://mainnet.era.zksync.io"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://zksync.io/", + "shortName": "zksync", + "chainId": 324, + "networkId": 324, + "icon": "zksync-era", + "explorers": [ + { + "name": "zkSync Era Block Explorer", + "url": "https://explorer.zksync.io", + "icon": "zksync-era", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.zksync.io/" }] + } + }, + { + "name": "Web3Q Mainnet", + "chain": "Web3Q", + "rpc": ["https://mainnet.web3q.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "Web3Q", "symbol": "W3Q", "decimals": 18 }, + "infoURL": "https://web3q.io/home.w3q/", + "shortName": "w3q", + "chainId": 333, + "networkId": 333, + "explorers": [ + { "name": "w3q-mainnet", "url": "https://explorer.mainnet.web3q.io", "standard": "EIP3091" } + ] + }, + { + "name": "DFK Chain Test", + "chain": "DFK", + "icon": "dfk", + "rpc": ["https://subnets.avax.network/defi-kingdoms/dfk-chain-testnet/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Jewel", "symbol": "JEWEL", "decimals": 18 }, + "infoURL": "https://defikingdoms.com", + "shortName": "DFKTEST", + "chainId": 335, + "networkId": 335, + "explorers": [ + { + "name": "ethernal", + "url": "https://explorer-test.dfkchain.com", + "icon": "ethereum", + "standard": "none" + } + ] + }, + { + "name": "Shiden", + "chain": "SDN", + "rpc": [ + "https://shiden.api.onfinality.io/public", + "https://shiden-rpc.dwellir.com", + "https://shiden.public.blastapi.io", + "wss://shiden.api.onfinality.io/public-ws", + "wss://shiden.public.blastapi.io", + "wss://shiden-rpc.dwellir.com" + ], + "faucets": [], + "nativeCurrency": { "name": "Shiden", "symbol": "SDN", "decimals": 18 }, + "infoURL": "https://shiden.astar.network/", + "shortName": "sdn", + "chainId": 336, + "networkId": 336, + "icon": "shiden", + "explorers": [ + { + "name": "subscan", + "url": "https://shiden.subscan.io", + "standard": "none", + "icon": "subscan" + } + ] + }, + { + "name": "Cronos Testnet", + "chain": "CRO", + "rpc": ["https://evm-t3.cronos.org"], + "faucets": ["https://cronos.org/faucet"], + "nativeCurrency": { "name": "Cronos Test Coin", "symbol": "TCRO", "decimals": 18 }, + "infoURL": "https://cronos.org", + "shortName": "tcro", + "chainId": 338, + "networkId": 338, + "explorers": [ + { + "name": "Cronos Testnet Explorer", + "url": "https://testnet.cronoscan.com", + "standard": "none" + } + ] + }, + { + "name": "Theta Mainnet", + "chain": "Theta", + "rpc": ["https://eth-rpc-api.thetatoken.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Theta Fuel", "symbol": "TFUEL", "decimals": 18 }, + "infoURL": "https://www.thetatoken.org/", + "shortName": "theta-mainnet", + "chainId": 361, + "networkId": 361, + "explorers": [ + { + "name": "Theta Mainnet Explorer", + "url": "https://explorer.thetatoken.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Theta Sapphire Testnet", + "chain": "Theta", + "rpc": ["https://eth-rpc-api-sapphire.thetatoken.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Theta Fuel", "symbol": "TFUEL", "decimals": 18 }, + "infoURL": "https://www.thetatoken.org/", + "shortName": "theta-sapphire", + "chainId": 363, + "networkId": 363, + "explorers": [ + { + "name": "Theta Sapphire Testnet Explorer", + "url": "https://guardian-testnet-sapphire-explorer.thetatoken.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Theta Amber Testnet", + "chain": "Theta", + "rpc": ["https://eth-rpc-api-amber.thetatoken.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Theta Fuel", "symbol": "TFUEL", "decimals": 18 }, + "infoURL": "https://www.thetatoken.org/", + "shortName": "theta-amber", + "chainId": 364, + "networkId": 364, + "explorers": [ + { + "name": "Theta Amber Testnet Explorer", + "url": "https://guardian-testnet-amber-explorer.thetatoken.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Theta Testnet", + "chain": "Theta", + "rpc": ["https://eth-rpc-api-testnet.thetatoken.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Theta Fuel", "symbol": "TFUEL", "decimals": 18 }, + "infoURL": "https://www.thetatoken.org/", + "shortName": "theta-testnet", + "chainId": 365, + "networkId": 365, + "explorers": [ + { + "name": "Theta Testnet Explorer", + "url": "https://testnet-explorer.thetatoken.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "PulseChain Mainnet", + "shortName": "pls", + "chain": "PLS", + "chainId": 369, + "networkId": 369, + "infoURL": "https://pulsechain.com/", + "rpc": ["https://rpc.mainnet.pulsechain.com/", "wss://rpc.mainnet.pulsechain.com/"], + "faucets": [], + "nativeCurrency": { "name": "Pulse", "symbol": "PLS", "decimals": 18 } + }, + { + "name": "Consta Testnet", + "chain": "tCNT", + "rpc": ["https://rpc-testnet.theconsta.com"], + "faucets": [], + "nativeCurrency": { "name": "tCNT", "symbol": "tCNT", "decimals": 18 }, + "infoURL": "http://theconsta.com", + "shortName": "tCNT", + "chainId": 371, + "networkId": 371, + "icon": "constachain", + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer-testnet.theconsta.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Lisinski", + "chain": "CRO", + "rpc": ["https://rpc-bitfalls1.lisinski.online"], + "faucets": ["https://pipa.lisinski.online"], + "nativeCurrency": { "name": "Lisinski Ether", "symbol": "LISINS", "decimals": 18 }, + "infoURL": "https://lisinski.online", + "shortName": "lisinski", + "chainId": 385, + "networkId": 385 + }, + { + "name": "HyperonChain TestNet", + "chain": "HPN", + "icon": "hyperonchain", + "rpc": ["https://testnet-rpc.hyperonchain.com"], + "faucets": ["https://faucet.hyperonchain.com"], + "nativeCurrency": { "name": "HyperonChain", "symbol": "HPN", "decimals": 18 }, + "infoURL": "https://docs.hyperonchain.com", + "shortName": "hpn", + "chainId": 400, + "networkId": 400, + "explorers": [ + { + "name": "blockscout", + "url": "https://testnet.hyperonchain.com", + "icon": "hyperonchain", + "standard": "EIP3091" + } + ] + }, + { + "name": "SX Network Mainnet", + "chain": "SX", + "icon": "SX", + "rpc": ["https://rpc.sx.technology"], + "faucets": [], + "nativeCurrency": { "name": "SX Network", "symbol": "SX", "decimals": 18 }, + "infoURL": "https://www.sx.technology", + "shortName": "SX", + "chainId": 416, + "networkId": 416, + "explorers": [ + { + "name": "SX Network Explorer", + "url": "https://explorer.sx.technology", + "standard": "EIP3091" + } + ] + }, + { + "name": "LA Testnet", + "chain": "LATestnet", + "rpc": ["https://rpc.testnet.lachain.network"], + "faucets": [], + "nativeCurrency": { "name": "Test La Coin", "symbol": "TLA", "decimals": 18 }, + "features": [{ "name": "EIP155" }], + "infoURL": "", + "shortName": "latestnet", + "chainId": 418, + "networkId": 418, + "explorers": [] + }, + { + "name": "Optimism Goerli Testnet", + "chain": "ETH", + "rpc": ["https://goerli.optimism.io/"], + "faucets": [], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://optimism.io", + "shortName": "ogor", + "chainId": 420, + "networkId": 420 + }, + { + "name": "Zeeth Chain", + "chain": "ZeethChain", + "rpc": ["https://rpc.zeeth.io"], + "faucets": [], + "nativeCurrency": { "name": "Zeeth Token", "symbol": "ZTH", "decimals": 18 }, + "infoURL": "", + "shortName": "zeeth", + "chainId": 427, + "networkId": 427, + "explorers": [ + { "name": "Zeeth Explorer", "url": "https://explorer.zeeth.io", "standard": "none" } + ] + }, + { + "name": "Frenchain Testnet", + "chain": "tfren", + "rpc": ["https://rpc-01tn.frenchain.app"], + "faucets": [], + "nativeCurrency": { "name": "tFREN", "symbol": "FtREN", "decimals": 18 }, + "infoURL": "https://frenchain.app", + "shortName": "tFREN", + "chainId": 444, + "networkId": 444, + "icon": "fren", + "explorers": [ + { + "name": "blockscout", + "url": "https://testnet.frenscan.io", + "icon": "fren", + "standard": "EIP3091" + } + ] + }, + { + "name": "Rupaya", + "chain": "RUPX", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Rupaya", "symbol": "RUPX", "decimals": 18 }, + "infoURL": "https://www.rupx.io", + "shortName": "rupx", + "chainId": 499, + "networkId": 499, + "slip44": 499 + }, + { + "name": "Camino C-Chain", + "chain": "CAM", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Camino", "symbol": "CAM", "decimals": 18 }, + "infoURL": "https://camino.network/", + "shortName": "Camino", + "chainId": 500, + "networkId": 1000, + "icon": "camino", + "explorers": [ + { + "name": "blockexplorer", + "url": "https://explorer.camino.foundation/mainnet", + "standard": "none" + } + ] + }, + { + "name": "Columbus Test Network", + "chain": "CAM", + "rpc": ["https://columbus.camino.network/ext/bc/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Camino", "symbol": "CAM", "decimals": 18 }, + "infoURL": "https://camino.network/", + "shortName": "Columbus", + "chainId": 501, + "networkId": 1001, + "icon": "camino", + "explorers": [ + { "name": "blockexplorer", "url": "https://explorer.camino.foundation", "standard": "none" } + ] + }, + { + "name": "Double-A Chain Mainnet", + "chain": "AAC", + "rpc": ["https://rpc.acuteangle.com"], + "faucets": [], + "nativeCurrency": { "name": "Acuteangle Native Token", "symbol": "AAC", "decimals": 18 }, + "infoURL": "https://www.acuteangle.com/", + "shortName": "aac", + "chainId": 512, + "networkId": 512, + "slip44": 1512, + "explorers": [ + { "name": "aacscan", "url": "https://scan.acuteangle.com", "standard": "EIP3091" } + ], + "icon": "aac" + }, + { + "name": "Double-A Chain Testnet", + "chain": "AAC", + "icon": "aac", + "rpc": ["https://rpc-testnet.acuteangle.com"], + "faucets": ["https://scan-testnet.acuteangle.com/faucet"], + "nativeCurrency": { "name": "Acuteangle Native Token", "symbol": "AAC", "decimals": 18 }, + "infoURL": "https://www.acuteangle.com/", + "shortName": "aact", + "chainId": 513, + "networkId": 513, + "explorers": [ + { + "name": "aacscan-testnet", + "url": "https://scan-testnet.acuteangle.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Gear Zero Network Mainnet", + "chain": "GearZero", + "rpc": ["https://gzn.linksme.info"], + "faucets": [], + "nativeCurrency": { "name": "Gear Zero Network Native Token", "symbol": "GZN", "decimals": 18 }, + "infoURL": "https://token.gearzero.ca/mainnet", + "shortName": "gz-mainnet", + "chainId": 516, + "networkId": 516, + "slip44": 516, + "explorers": [] + }, + { + "name": "XT Smart Chain Mainnet", + "chain": "XSC", + "icon": "xsc", + "rpc": ["https://datarpc1.xsc.pub", "https://datarpc2.xsc.pub", "https://datarpc3.xsc.pub"], + "faucets": ["https://xsc.pub/faucet"], + "nativeCurrency": { "name": "XT Smart Chain Native Token", "symbol": "XT", "decimals": 18 }, + "infoURL": "https://xsc.pub/", + "shortName": "xt", + "chainId": 520, + "networkId": 1024, + "explorers": [{ "name": "xscscan", "url": "https://xscscan.pub", "standard": "EIP3091" }] + }, + { + "name": "Firechain Mainnet", + "chain": "FIRE", + "icon": "firechain", + "rpc": ["https://mainnet.rpc1.thefirechain.com"], + "faucets": [], + "nativeCurrency": { "name": "Firechain", "symbol": "FIRE", "decimals": 18 }, + "infoURL": "https://thefirechain.com", + "shortName": "fire", + "chainId": 529, + "networkId": 529, + "explorers": [], + "status": "incubating" + }, + { + "name": "F(x)Core Mainnet Network", + "chain": "Fxcore", + "rpc": ["https://fx-json-web3.functionx.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "Function X", "symbol": "FX", "decimals": 18 }, + "infoURL": "https://functionx.io/", + "shortName": "FxCore", + "chainId": 530, + "networkId": 530, + "icon": "fxcore", + "explorers": [ + { "name": "FunctionX Explorer", "url": "https://fx-evm.functionx.io", "standard": "EIP3091" } + ] + }, + { + "name": "Candle", + "chain": "Candle", + "rpc": ["https://candle-rpc.com/", "https://rpc.cndlchain.com"], + "faucets": [], + "nativeCurrency": { "name": "CANDLE", "symbol": "CNDL", "decimals": 18 }, + "infoURL": "https://candlelabs.org/", + "shortName": "CNDL", + "chainId": 534, + "networkId": 534, + "slip44": 674, + "explorers": [ + { "name": "candleexplorer", "url": "https://candleexplorer.com", "standard": "EIP3091" } + ] + }, + { + "name": "Vela1 Chain Mainnet", + "chain": "VELA1", + "rpc": ["https://rpc.velaverse.io"], + "faucets": [], + "nativeCurrency": { "name": "CLASS COIN", "symbol": "CLASS", "decimals": 18 }, + "infoURL": "https://velaverse.io", + "shortName": "CLASS", + "chainId": 555, + "networkId": 555, + "explorers": [ + { + "name": "Vela1 Chain Mainnet Explorer", + "url": "https://exp.velaverse.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Tao Network", + "chain": "TAO", + "rpc": [ + "https://rpc.testnet.tao.network", + "http://rpc.testnet.tao.network:8545", + "https://rpc.tao.network", + "wss://rpc.tao.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Tao", "symbol": "TAO", "decimals": 18 }, + "infoURL": "https://tao.network", + "shortName": "tao", + "chainId": 558, + "networkId": 558 + }, + { + "name": "Dogechain Testnet", + "chain": "DC", + "icon": "dogechain", + "rpc": ["https://rpc-testnet.dogechain.dog"], + "faucets": ["https://faucet.dogechain.dog"], + "nativeCurrency": { "name": "Dogecoin", "symbol": "DOGE", "decimals": 18 }, + "infoURL": "https://dogechain.dog", + "shortName": "dct", + "chainId": 568, + "networkId": 568, + "explorers": [ + { + "name": "dogechain testnet explorer", + "url": "https://explorer-testnet.dogechain.dog", + "standard": "EIP3091" + } + ] + }, + { + "name": "Metis Stardust Testnet", + "chain": "ETH", + "rpc": ["https://stardust.metis.io/?owner=588"], + "faucets": [], + "nativeCurrency": { "name": "tMetis", "symbol": "METIS", "decimals": 18 }, + "infoURL": "https://www.metis.io", + "shortName": "metis-stardust", + "chainId": 588, + "networkId": 588, + "explorers": [ + { "name": "blockscout", "url": "https://stardust-explorer.metis.io", "standard": "EIP3091" } + ], + "parent": { + "type": "L2", + "chain": "eip155-4", + "bridges": [{ "url": "https://bridge.metis.io" }] + }, + "status": "deprecated" + }, + { + "name": "Astar", + "chain": "ASTR", + "rpc": ["https://rpc.astar.network:8545"], + "faucets": [], + "nativeCurrency": { "name": "Astar", "symbol": "ASTR", "decimals": 18 }, + "infoURL": "https://astar.network/", + "shortName": "astr", + "chainId": 592, + "networkId": 592, + "icon": "astar", + "explorers": [ + { + "name": "subscan", + "url": "https://astar.subscan.io", + "standard": "none", + "icon": "subscan" + } + ] + }, + { + "name": "Acala Mandala Testnet", + "chain": "mACA", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Acala Mandala Token", "symbol": "mACA", "decimals": 18 }, + "infoURL": "https://acala.network", + "shortName": "maca", + "chainId": 595, + "networkId": 595 + }, + { + "name": "Karura Network Testnet", + "chain": "KAR", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Karura Token", "symbol": "KAR", "decimals": 18 }, + "infoURL": "https://karura.network", + "shortName": "tkar", + "chainId": 596, + "networkId": 596, + "slip44": 596 + }, + { + "name": "Acala Network Testnet", + "chain": "ACA", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Acala Token", "symbol": "ACA", "decimals": 18 }, + "infoURL": "https://acala.network", + "shortName": "taca", + "chainId": 597, + "networkId": 597, + "slip44": 597 + }, + { + "name": "Metis Goerli Testnet", + "chain": "ETH", + "rpc": ["https://goerli.gateway.metisdevops.link"], + "faucets": ["https://goerli.faucet.metisdevops.link"], + "nativeCurrency": { "name": "Goerli Metis", "symbol": "METIS", "decimals": 18 }, + "infoURL": "https://www.metis.io", + "shortName": "metis-goerli", + "chainId": 599, + "networkId": 599, + "explorers": [ + { + "name": "blockscout", + "url": "https://goerli.explorer.metisdevops.link", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-4", + "bridges": [{ "url": "https://testnet-bridge.metis.io" }] + } + }, + { + "name": "Meshnyan testnet", + "chain": "MeshTestChain", + "rpc": [], + "faucets": [], + "nativeCurrency": { + "name": "Meshnyan Testnet Native Token", + "symbol": "MESHT", + "decimals": 18 + }, + "infoURL": "", + "shortName": "mesh-chain-testnet", + "chainId": 600, + "networkId": 600 + }, + { + "name": "Graphlinq Blockchain Mainnet", + "chain": "GLQ Blockchain", + "rpc": ["https://glq-dataseed.graphlinq.io"], + "faucets": [], + "nativeCurrency": { "name": "GLQ", "symbol": "GLQ", "decimals": 18 }, + "infoURL": "https://graphlinq.io", + "shortName": "glq", + "chainId": 614, + "networkId": 614, + "explorers": [ + { "name": "GLQ Explorer", "url": "https://explorer.graphlinq.io", "standard": "none" } + ] + }, + { + "name": "SX Network Testnet", + "chain": "SX", + "icon": "SX", + "rpc": ["https://rpc.toronto.sx.technology"], + "faucets": ["https://faucet.toronto.sx.technology"], + "nativeCurrency": { "name": "SX Network", "symbol": "SX", "decimals": 18 }, + "infoURL": "https://www.sx.technology", + "shortName": "SX-Testnet", + "chainId": 647, + "networkId": 647, + "explorers": [ + { + "name": "SX Network Toronto Explorer", + "url": "https://explorer.toronto.sx.technology", + "standard": "EIP3091" + } + ] + }, + { + "name": "Endurance Smart Chain Mainnet", + "chain": "ACE", + "rpc": ["https://rpc-endurance.fusionist.io/"], + "faucets": [], + "nativeCurrency": { "name": "Endurance Chain Native Token", "symbol": "ACE", "decimals": 18 }, + "infoURL": "https://ace.fusionist.io/", + "shortName": "ace", + "chainId": 648, + "networkId": 648, + "explorers": [ + { + "name": "Endurance Scan", + "url": "https://explorer.endurance.fusionist.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Pixie Chain Testnet", + "chain": "PixieChain", + "rpc": ["https://http-testnet.chain.pixie.xyz", "wss://ws-testnet.chain.pixie.xyz"], + "faucets": ["https://chain.pixie.xyz/faucet"], + "nativeCurrency": { + "name": "Pixie Chain Testnet Native Token", + "symbol": "PCTT", + "decimals": 18 + }, + "infoURL": "https://scan-testnet.chain.pixie.xyz", + "shortName": "pixie-chain-testnet", + "chainId": 666, + "networkId": 666 + }, + { + "name": "Karura Network", + "chain": "KAR", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Karura Token", "symbol": "KAR", "decimals": 18 }, + "infoURL": "https://karura.network", + "shortName": "kar", + "chainId": 686, + "networkId": 686, + "slip44": 686 + }, + { + "name": "Star Social Testnet", + "chain": "SNS", + "rpc": ["https://avastar.cc/ext/bc/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Social", "symbol": "SNS", "decimals": 18 }, + "infoURL": "https://info.avastar.cc", + "shortName": "SNS", + "chainId": 700, + "networkId": 700, + "explorers": [{ "name": "starscan", "url": "https://avastar.info", "standard": "EIP3091" }] + }, + { + "name": "BlockChain Station Mainnet", + "chain": "BCS", + "rpc": ["https://rpc-mainnet.bcsdev.io", "wss://rpc-ws-mainnet.bcsdev.io"], + "faucets": [], + "nativeCurrency": { "name": "BCS Token", "symbol": "BCS", "decimals": 18 }, + "infoURL": "https://blockchainstation.io", + "shortName": "bcs", + "chainId": 707, + "networkId": 707, + "explorers": [ + { + "name": "BlockChain Station Explorer", + "url": "https://explorer.bcsdev.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "BlockChain Station Testnet", + "chain": "BCS", + "rpc": ["https://rpc-testnet.bcsdev.io", "wss://rpc-ws-testnet.bcsdev.io"], + "faucets": ["https://faucet.bcsdev.io"], + "nativeCurrency": { "name": "BCS Testnet Token", "symbol": "tBCS", "decimals": 18 }, + "infoURL": "https://blockchainstation.io", + "shortName": "tbcs", + "chainId": 708, + "networkId": 708, + "explorers": [ + { + "name": "BlockChain Station Explorer", + "url": "https://testnet.bcsdev.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Lycan Chain", + "chain": "LYC", + "rpc": ["https://rpc.lycanchain.com/"], + "faucets": [], + "nativeCurrency": { "name": "Lycan", "symbol": "LYC", "decimals": 18 }, + "infoURL": "https://lycanchain.com", + "shortName": "LYC", + "chainId": 721, + "networkId": 721, + "icon": "lycanchain", + "explorers": [ + { "name": "blockscout", "url": "https://explorer.lycanchain.com", "standard": "EIP3091" } + ] + }, + { + "name": "Canto Testnet", + "chain": "Canto Tesnet", + "rpc": ["https://eth.plexnode.wtf/"], + "faucets": [], + "nativeCurrency": { "name": "Canto", "symbol": "CANTO", "decimals": 18 }, + "infoURL": "https://canto.io", + "shortName": "tcanto", + "chainId": 740, + "networkId": 740, + "explorers": [ + { + "name": "Canto Tesnet Explorer (Neobase)", + "url": "https://testnet-explorer.canto.neobase.one", + "standard": "none" + } + ], + "status": "deprecated" + }, + { + "name": "Vention Smart Chain Testnet", + "chain": "VSCT", + "icon": "ventionTestnet", + "rpc": ["https://node-testnet.vention.network"], + "faucets": ["https://faucet.vention.network"], + "nativeCurrency": { "name": "VNT", "symbol": "VNT", "decimals": 18 }, + "infoURL": "https://testnet.ventionscan.io", + "shortName": "vsct", + "chainId": 741, + "networkId": 741, + "explorers": [ + { "name": "ventionscan", "url": "https://testnet.ventionscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "QL1", + "chain": "QOM", + "status": "incubating", + "rpc": ["https://rpc.qom.one"], + "faucets": [], + "nativeCurrency": { "name": "Shiba Predator", "symbol": "QOM", "decimals": 18 }, + "infoURL": "https://qom.one", + "shortName": "qom", + "chainId": 766, + "networkId": 766, + "icon": "qom", + "explorers": [ + { + "name": "QL1 Mainnet Explorer", + "url": "https://mainnet.qom.one", + "icon": "qom", + "standard": "EIP3091" + } + ] + }, + { + "name": "OpenChain Testnet", + "chain": "OpenChain Testnet", + "rpc": [], + "faucets": ["https://faucet.openchain.info/"], + "nativeCurrency": { "name": "Openchain Testnet", "symbol": "TOPC", "decimals": 18 }, + "infoURL": "https://testnet.openchain.info/", + "shortName": "opc", + "chainId": 776, + "networkId": 776, + "explorers": [ + { "name": "OPEN CHAIN TESTNET", "url": "https://testnet.openchain.info", "standard": "none" } + ] + }, + { + "name": "cheapETH", + "chain": "cheapETH", + "rpc": ["https://node.cheapeth.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "cTH", "symbol": "cTH", "decimals": 18 }, + "infoURL": "https://cheapeth.org/", + "shortName": "cth", + "chainId": 777, + "networkId": 777 + }, + { + "name": "Acala Network", + "chain": "ACA", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Acala Token", "symbol": "ACA", "decimals": 18 }, + "infoURL": "https://acala.network", + "shortName": "aca", + "chainId": 787, + "networkId": 787, + "slip44": 787 + }, + { + "name": "Aerochain Testnet", + "chain": "Aerochain", + "rpc": ["https://testnet-rpc.aerochain.id/"], + "faucets": ["https://faucet.aerochain.id/"], + "nativeCurrency": { "name": "Aerochain Testnet", "symbol": "TAero", "decimals": 18 }, + "infoURL": "https://aerochaincoin.org/", + "shortName": "taero", + "chainId": 788, + "networkId": 788, + "explorers": [ + { "name": "aeroscan", "url": "https://testnet.aeroscan.id", "standard": "EIP3091" } + ] + }, + { + "name": "Lucid Blockchain", + "chain": "Lucid", + "icon": "lucid", + "rpc": ["https://rpc.lucidcoin.io"], + "faucets": ["https://faucet.lucidcoin.io"], + "nativeCurrency": { "name": "LUCID", "symbol": "LUCID", "decimals": 18 }, + "infoURL": "https://lucidcoin.io", + "shortName": "LUCID", + "chainId": 800, + "networkId": 800, + "explorers": [ + { "name": "Lucid Explorer", "url": "https://explorer.lucidcoin.io", "standard": "none" } + ] + }, + { + "name": "Haic", + "chain": "Haic", + "rpc": ["https://orig.haichain.io/"], + "faucets": [], + "nativeCurrency": { "name": "Haicoin", "symbol": "HAIC", "decimals": 18 }, + "infoURL": "https://www.haichain.io/", + "shortName": "haic", + "chainId": 803, + "networkId": 803 + }, + { + "name": "Portal Fantasy Chain Test", + "chain": "PF", + "icon": "pf", + "rpc": ["https://subnets.avax.network/portal-fantasy/testnet/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Portal Fantasy Token", "symbol": "PFT", "decimals": 18 }, + "infoURL": "https://portalfantasy.io", + "shortName": "PFTEST", + "chainId": 808, + "networkId": 808, + "explorers": [] + }, + { + "name": "Qitmeer", + "chain": "MEER", + "rpc": [ + "https://evm-dataseed1.meerscan.io", + "https://evm-dataseed2.meerscan.io", + "https://evm-dataseed3.meerscan.io", + "https://evm-dataseed.meerscan.com", + "https://evm-dataseed1.meerscan.com", + "https://evm-dataseed2.meerscan.com" + ], + "faucets": [], + "nativeCurrency": { "name": "Qitmeer", "symbol": "MEER", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "meer", + "chainId": 813, + "networkId": 813, + "slip44": 813, + "icon": "meer", + "explorers": [ + { "name": "meerscan", "icon": "meer", "url": "https://evm.meerscan.com", "standard": "none" } + ] + }, + { + "name": "BeOne Chain Mainnet", + "chain": "BOC", + "icon": "beonechain", + "rpc": [ + "https://dataseed1.beonechain.com", + "https://dataseed2.beonechain.com", + "https://dataseed-us1.beonechain.com", + "https://dataseed-us2.beonechain.com", + "https://dataseed-uk1.beonechain.com", + "https://dataseed-uk2.beonechain.com" + ], + "faucets": [], + "nativeCurrency": { "name": "BeOne Chain Mainnet", "symbol": "BOC", "decimals": 18 }, + "infoURL": "https://beonechain.com", + "shortName": "BOC", + "chainId": 818, + "networkId": 818, + "slip44": 8181, + "explorers": [ + { "name": "BeOne Chain Mainnet", "url": "https://beonescan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Callisto Mainnet", + "chain": "CLO", + "rpc": ["https://rpc.callisto.network/"], + "faucets": [], + "nativeCurrency": { "name": "Callisto", "symbol": "CLO", "decimals": 18 }, + "infoURL": "https://callisto.network", + "shortName": "clo", + "chainId": 820, + "networkId": 1, + "slip44": 820 + }, + { + "name": "Callisto Testnet Deprecated", + "chain": "CLO", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Callisto Testnet Ether", "symbol": "TCLO", "decimals": 18 }, + "infoURL": "https://callisto.network", + "shortName": "tclo", + "chainId": 821, + "networkId": 2, + "status": "deprecated" + }, + { + "name": "Taraxa Mainnet", + "chain": "Tara", + "icon": "taraxa", + "rpc": ["https://rpc.mainnet.taraxa.io/"], + "faucets": [], + "nativeCurrency": { "name": "Tara", "symbol": "TARA", "decimals": 18 }, + "infoURL": "https://taraxa.io", + "shortName": "tara", + "chainId": 841, + "networkId": 841, + "explorers": [ + { "name": "Taraxa Explorer", "url": "https://explorer.mainnet.taraxa.io", "standard": "none" } + ] + }, + { + "name": "Taraxa Testnet", + "chain": "Tara", + "icon": "taraxa", + "rpc": ["https://rpc.testnet.taraxa.io/"], + "faucets": [], + "nativeCurrency": { "name": "Tara", "symbol": "TARA", "decimals": 18 }, + "infoURL": "https://taraxa.io", + "shortName": "taratest", + "chainId": 842, + "networkId": 842, + "explorers": [ + { "name": "Taraxa Explorer", "url": "https://explorer.testnet.taraxa.io", "standard": "none" } + ] + }, + { + "name": "Zeeth Chain Dev", + "chain": "ZeethChainDev", + "rpc": ["https://rpc.dev.zeeth.io"], + "faucets": [], + "nativeCurrency": { "name": "Zeeth Token", "symbol": "ZTH", "decimals": 18 }, + "infoURL": "", + "shortName": "zeethdev", + "chainId": 859, + "networkId": 859, + "explorers": [ + { "name": "Zeeth Explorer Dev", "url": "https://explorer.dev.zeeth.io", "standard": "none" } + ] + }, + { + "name": "Fantasia Chain Mainnet", + "chain": "FSC", + "rpc": [ + "https://mainnet-data1.fantasiachain.com/", + "https://mainnet-data2.fantasiachain.com/", + "https://mainnet-data3.fantasiachain.com/" + ], + "faucets": [], + "nativeCurrency": { "name": "FST", "symbol": "FST", "decimals": 18 }, + "infoURL": "https://fantasia.technology/", + "shortName": "FSCMainnet", + "chainId": 868, + "networkId": 868, + "explorers": [ + { "name": "FSCScan", "url": "https://explorer.fantasiachain.com", "standard": "EIP3091" } + ] + }, + { + "name": "Bandai Namco Research Verse Mainnet", + "chain": "Bandai Namco Research Verse", + "icon": "bnken", + "rpc": ["https://rpc.main.oasvrs.bnken.net"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://www.bandainamco-mirai.com/en/", + "shortName": "BNKEN", + "chainId": 876, + "networkId": 876, + "explorers": [ + { + "name": "Bandai Namco Research Verse Explorer", + "url": "https://explorer.main.oasvrs.bnken.net", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-248" } + }, + { + "name": "Dexit Network", + "chain": "DXT", + "rpc": ["https://dxt.dexit.network"], + "faucets": ["https://faucet.dexit.network"], + "nativeCurrency": { "name": "Dexit network", "symbol": "DXT", "decimals": 18 }, + "infoURL": "https://dexit.network", + "shortName": "DXT", + "chainId": 877, + "networkId": 877, + "explorers": [{ "name": "dxtscan", "url": "https://dxtscan.com", "standard": "EIP3091" }] + }, + { + "name": "Ambros Chain Mainnet", + "chain": "ambroschain", + "rpc": ["https://api.ambros.network"], + "faucets": [], + "nativeCurrency": { "name": "AMBROS", "symbol": "AMBROS", "decimals": 18 }, + "infoURL": "https://ambros.network", + "shortName": "ambros", + "chainId": 880, + "networkId": 880, + "explorers": [ + { "name": "Ambros Chain Explorer", "url": "https://ambrosscan.com", "standard": "none" } + ] + }, + { + "name": "Wanchain", + "chain": "WAN", + "rpc": ["https://gwan-ssl.wandevs.org:56891/"], + "faucets": [], + "nativeCurrency": { "name": "Wancoin", "symbol": "WAN", "decimals": 18 }, + "infoURL": "https://www.wanscan.org", + "shortName": "wan", + "chainId": 888, + "networkId": 888, + "slip44": 5718350 + }, + { + "name": "Garizon Testnet Stage0", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s0-testnet.garizon.net/rpc"], + "faucets": ["https://faucet-testnet.garizon.com"], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-test-s0", + "chainId": 900, + "networkId": 900, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer-testnet.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ] + }, + { + "name": "Garizon Testnet Stage1", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s1-testnet.garizon.net/rpc"], + "faucets": ["https://faucet-testnet.garizon.com"], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-test-s1", + "chainId": 901, + "networkId": 901, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer-testnet.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-900", "type": "shard" } + }, + { + "name": "Garizon Testnet Stage2", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s2-testnet.garizon.net/rpc"], + "faucets": ["https://faucet-testnet.garizon.com"], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-test-s2", + "chainId": 902, + "networkId": 902, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer-testnet.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-900", "type": "shard" } + }, + { + "name": "Garizon Testnet Stage3", + "chain": "GAR", + "icon": "garizon", + "rpc": ["https://s3-testnet.garizon.net/rpc"], + "faucets": ["https://faucet-testnet.garizon.com"], + "nativeCurrency": { "name": "Garizon", "symbol": "GAR", "decimals": 18 }, + "infoURL": "https://garizon.com", + "shortName": "gar-test-s3", + "chainId": 903, + "networkId": 903, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer-testnet.garizon.com", + "icon": "garizon", + "standard": "EIP3091" + } + ], + "parent": { "chain": "eip155-900", "type": "shard" } + }, + { + "name": "Portal Fantasy Chain", + "chain": "PF", + "icon": "pf", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Portal Fantasy Token", "symbol": "PFT", "decimals": 18 }, + "infoURL": "https://portalfantasy.io", + "shortName": "PF", + "chainId": 909, + "networkId": 909, + "explorers": [], + "status": "incubating" + }, + { + "name": "DecentraBone Layer1 Testnet", + "chain": "DBONE", + "rpc": ["https://layer1test.decentrabone.com"], + "faucets": [], + "nativeCurrency": { "name": "DecentraBone", "symbol": "DBONE", "decimals": 18 }, + "infoURL": "https://decentrabone.com", + "shortName": "DBONE", + "chainId": 910, + "networkId": 910 + }, + { + "name": "Rinia Testnet", + "chain": "FIRE", + "icon": "rinia", + "rpc": ["https://rinia.rpc1.thefirechain.com"], + "faucets": ["https://faucet.thefirechain.com"], + "nativeCurrency": { "name": "Firechain", "symbol": "FIRE", "decimals": 18 }, + "infoURL": "https://thefirechain.com", + "shortName": "tfire", + "chainId": 917, + "networkId": 917, + "explorers": [], + "status": "incubating" + }, + { + "name": "PulseChain Testnet", + "shortName": "tpls", + "chain": "tPLS", + "chainId": 940, + "networkId": 940, + "infoURL": "https://pulsechain.com/", + "rpc": ["https://rpc.v2.testnet.pulsechain.com/", "wss://rpc.v2.testnet.pulsechain.com/"], + "faucets": ["https://faucet.v2.testnet.pulsechain.com/"], + "nativeCurrency": { "name": "Test Pulse", "symbol": "tPLS", "decimals": 18 } + }, + { + "name": "PulseChain Testnet v2b", + "shortName": "t2bpls", + "chain": "t2bPLS", + "chainId": 941, + "networkId": 941, + "infoURL": "https://pulsechain.com/", + "rpc": ["https://rpc.v2b.testnet.pulsechain.com/", "wss://rpc.v2b.testnet.pulsechain.com/"], + "faucets": ["https://faucet.v2b.testnet.pulsechain.com/"], + "nativeCurrency": { "name": "Test Pulse", "symbol": "tPLS", "decimals": 18 } + }, + { + "name": "PulseChain Testnet v3", + "shortName": "t3pls", + "chain": "t3PLS", + "chainId": 942, + "networkId": 942, + "infoURL": "https://pulsechain.com/", + "rpc": ["https://rpc.v3.testnet.pulsechain.com/", "wss://rpc.v3.testnet.pulsechain.com/"], + "faucets": ["https://faucet.v3.testnet.pulsechain.com/"], + "nativeCurrency": { "name": "Test Pulse", "symbol": "tPLS", "decimals": 18 } + }, + { + "name": "muNode Testnet", + "chain": "munode", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://munode.dev/", + "shortName": "munode", + "chainId": 956, + "networkId": 956 + }, + { + "name": "Oort Mainnet", + "chain": "Oort Mainnet", + "rpc": ["https://rpc.oortech.com"], + "faucets": [], + "nativeCurrency": { "name": "Oort", "symbol": "CCN", "decimals": 18 }, + "infoURL": "https://oortech.com", + "shortName": "ccn", + "chainId": 970, + "networkId": 970, + "icon": "ccn" + }, + { + "name": "Oort Huygens", + "chain": "Huygens", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Oort", "symbol": "CCN", "decimals": 18 }, + "infoURL": "https://oortech.com", + "shortName": "Huygens", + "chainId": 971, + "networkId": 971, + "icon": "ccn" + }, + { + "name": "Oort Ascraeus", + "title": "Oort Ascraeus", + "chain": "Ascraeus", + "rpc": ["https://ascraeus-rpc.oortech.com"], + "faucets": [], + "nativeCurrency": { "name": "Oort", "symbol": "CCNA", "decimals": 18 }, + "infoURL": "https://oortech.com", + "shortName": "Ascraeus", + "chainId": 972, + "networkId": 972, + "icon": "ccn" + }, + { + "name": "Nepal Blockchain Network", + "chain": "YETI", + "rpc": ["https://api.nepalblockchain.dev", "https://api.nepalblockchain.network"], + "faucets": ["https://faucet.nepalblockchain.network"], + "nativeCurrency": { + "name": "Nepal Blockchain Network Ether", + "symbol": "YETI", + "decimals": 18 + }, + "infoURL": "https://nepalblockchain.network", + "shortName": "yeti", + "chainId": 977, + "networkId": 977 + }, + { + "name": "TOP Mainnet EVM", + "chain": "TOP", + "icon": "top", + "rpc": ["https://ethapi.topnetwork.org"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://www.topnetwork.org/", + "shortName": "top_evm", + "chainId": 980, + "networkId": 0, + "explorers": [{ "name": "topscan.dev", "url": "https://www.topscan.io", "standard": "none" }] + }, + { + "name": "Memo Smart Chain Mainnet", + "chain": "MEMO", + "rpc": ["https://chain.metamemo.one:8501", "wss://chain.metamemo.one:16801"], + "faucets": ["https://faucet.metamemo.one/"], + "nativeCurrency": { "name": "Memo", "symbol": "CMEMO", "decimals": 18 }, + "infoURL": "www.memolabs.org", + "shortName": "memochain", + "chainId": 985, + "networkId": 985, + "icon": "memo", + "explorers": [ + { + "name": "Memo Mainnet Explorer", + "url": "https://scan.metamemo.one:8080", + "icon": "memo", + "standard": "EIP3091" + } + ] + }, + { + "name": "TOP Mainnet", + "chain": "TOP", + "icon": "top", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "TOP", "symbol": "TOP", "decimals": 6 }, + "infoURL": "https://www.topnetwork.org/", + "shortName": "top", + "chainId": 989, + "networkId": 0, + "explorers": [{ "name": "topscan.dev", "url": "https://www.topscan.io", "standard": "none" }] + }, + { + "name": "Lucky Network", + "chain": "LN", + "rpc": ["https://rpc.luckynetwork.org", "wss://ws.lnscan.org", "https://rpc.lnscan.org"], + "faucets": [], + "nativeCurrency": { "name": "Lucky", "symbol": "L99", "decimals": 18 }, + "infoURL": "https://luckynetwork.org", + "shortName": "ln", + "chainId": 998, + "networkId": 998, + "icon": "lucky", + "explorers": [ + { "name": "blockscout", "url": "https://explorer.luckynetwork.org", "standard": "none" }, + { "name": "expedition", "url": "https://lnscan.org", "standard": "none" } + ] + }, + { + "name": "Wanchain Testnet", + "chain": "WAN", + "rpc": ["https://gwan-ssl.wandevs.org:46891/"], + "faucets": [], + "nativeCurrency": { "name": "Wancoin", "symbol": "WAN", "decimals": 18 }, + "infoURL": "https://testnet.wanscan.org", + "shortName": "twan", + "chainId": 999, + "networkId": 999 + }, + { + "name": "GTON Mainnet", + "chain": "GTON", + "rpc": ["https://rpc.gton.network/"], + "faucets": [], + "nativeCurrency": { "name": "GCD", "symbol": "GCD", "decimals": 18 }, + "infoURL": "https://gton.capital", + "shortName": "gton", + "chainId": 1000, + "networkId": 1000, + "explorers": [ + { + "name": "GTON Network Explorer", + "url": "https://explorer.gton.network", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-1" } + }, + { + "name": "Klaytn Testnet Baobab", + "chain": "KLAY", + "rpc": ["https://api.baobab.klaytn.net:8651"], + "faucets": ["https://baobab.wallet.klaytn.com/access?next=faucet"], + "nativeCurrency": { "name": "KLAY", "symbol": "KLAY", "decimals": 18 }, + "infoURL": "https://www.klaytn.com/", + "shortName": "Baobab", + "chainId": 1001, + "networkId": 1001 + }, + { + "name": "T-EKTA", + "title": "EKTA Testnet T-EKTA", + "chain": "T-EKTA", + "rpc": ["https://test.ekta.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "T-EKTA", "symbol": "T-EKTA", "decimals": 18 }, + "infoURL": "https://www.ekta.io", + "shortName": "t-ekta", + "chainId": 1004, + "networkId": 1004, + "icon": "ekta", + "explorers": [ + { + "name": "test-ektascan", + "url": "https://test.ektascan.io", + "icon": "ekta", + "standard": "EIP3091" + } + ] + }, + { + "name": "Newton Testnet", + "chain": "NEW", + "rpc": ["https://rpc1.newchain.newtonproject.org"], + "faucets": [], + "nativeCurrency": { "name": "Newton", "symbol": "NEW", "decimals": 18 }, + "infoURL": "https://www.newtonproject.org/", + "shortName": "tnew", + "chainId": 1007, + "networkId": 1007 + }, + { + "name": "Eurus Mainnet", + "chain": "EUN", + "rpc": ["https://mainnet.eurus.network/"], + "faucets": [], + "nativeCurrency": { "name": "Eurus", "symbol": "EUN", "decimals": 18 }, + "infoURL": "https://eurus.network", + "shortName": "eun", + "chainId": 1008, + "networkId": 1008, + "icon": "eurus", + "explorers": [ + { + "name": "eurusexplorer", + "url": "https://explorer.eurus.network", + "icon": "eurus", + "standard": "none" + } + ] + }, + { + "name": "Evrice Network", + "chain": "EVC", + "rpc": ["https://meta.evrice.com"], + "faucets": [], + "nativeCurrency": { "name": "Evrice", "symbol": "EVC", "decimals": 18 }, + "infoURL": "https://evrice.com", + "shortName": "EVC", + "chainId": 1010, + "networkId": 1010, + "slip44": 1020 + }, + { + "name": "Newton", + "chain": "NEW", + "rpc": ["https://global.rpc.mainnet.newtonproject.org"], + "faucets": [], + "nativeCurrency": { "name": "Newton", "symbol": "NEW", "decimals": 18 }, + "infoURL": "https://www.newtonproject.org/", + "shortName": "new", + "chainId": 1012, + "networkId": 1012 + }, + { + "name": "Sakura", + "chain": "Sakura", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Sakura", "symbol": "SKU", "decimals": 18 }, + "infoURL": "https://clover.finance/sakura", + "shortName": "sku", + "chainId": 1022, + "networkId": 1022 + }, + { + "name": "Clover Testnet", + "chain": "Clover", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Clover", "symbol": "CLV", "decimals": 18 }, + "infoURL": "https://clover.finance", + "shortName": "tclv", + "chainId": 1023, + "networkId": 1023 + }, + { + "name": "CLV Parachain", + "chain": "CLV", + "rpc": ["https://api-para.clover.finance"], + "faucets": [], + "nativeCurrency": { "name": "CLV", "symbol": "CLV", "decimals": 18 }, + "infoURL": "https://clv.org", + "shortName": "clv", + "chainId": 1024, + "networkId": 1024 + }, + { + "name": "BitTorrent Chain Testnet", + "chain": "BTTC", + "rpc": ["https://testrpc.bittorrentchain.io/"], + "faucets": [], + "nativeCurrency": { "name": "BitTorrent", "symbol": "BTT", "decimals": 18 }, + "infoURL": "https://bittorrentchain.io/", + "shortName": "tbtt", + "chainId": 1028, + "networkId": 1028, + "explorers": [ + { "name": "testbttcscan", "url": "https://testscan.bittorrentchain.io", "standard": "none" } + ] + }, + { + "name": "Conflux eSpace", + "chain": "Conflux", + "rpc": ["https://evm.confluxrpc.com"], + "faucets": [], + "nativeCurrency": { "name": "CFX", "symbol": "CFX", "decimals": 18 }, + "infoURL": "https://confluxnetwork.org", + "shortName": "cfx", + "chainId": 1030, + "networkId": 1030, + "icon": "conflux", + "explorers": [ + { "name": "Conflux Scan", "url": "https://evm.confluxscan.net", "standard": "none" } + ] + }, + { + "name": "Proxy Network Testnet", + "chain": "Proxy Network", + "rpc": ["http://128.199.94.183:8041"], + "faucets": [], + "nativeCurrency": { "name": "PRX", "symbol": "PRX", "decimals": 18 }, + "infoURL": "https://theproxy.network", + "shortName": "prx", + "chainId": 1031, + "networkId": 1031, + "explorers": [ + { + "name": "proxy network testnet", + "url": "http://testnet-explorer.theproxy.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bronos Testnet", + "chain": "Bronos", + "rpc": ["https://evm-testnet.bronos.org"], + "faucets": ["https://faucet.bronos.org"], + "nativeCurrency": { "name": "tBRO", "symbol": "tBRO", "decimals": 18 }, + "infoURL": "https://bronos.org", + "shortName": "bronos-testnet", + "chainId": 1038, + "networkId": 1038, + "icon": "bronos", + "explorers": [ + { + "name": "Bronos Testnet Explorer", + "url": "https://tbroscan.bronos.org", + "standard": "none", + "icon": "bronos" + } + ] + }, + { + "name": "Bronos Mainnet", + "chain": "Bronos", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "BRO", "symbol": "BRO", "decimals": 18 }, + "infoURL": "https://bronos.org", + "shortName": "bronos-mainnet", + "chainId": 1039, + "networkId": 1039, + "icon": "bronos", + "explorers": [ + { + "name": "Bronos Explorer", + "url": "https://broscan.bronos.org", + "standard": "none", + "icon": "bronos" + } + ] + }, + { + "name": "ShimmerEVM Testnet", + "title": "ShimmerEVM Testnet", + "chain": "ShimmerEVM", + "icon": "shimmerevm", + "rpc": ["https://json-rpc.evm.testnet.shimmer.network"], + "faucets": [ + "https://evm-toolkit.evm.testnet.shimmer.network", + "https://evm-faucet.testnet.shimmer.network" + ], + "nativeCurrency": { "name": "SMR", "symbol": "SMR", "decimals": 18 }, + "infoURL": "https://shimmer.network", + "shortName": "shimmerevm-testnet", + "chainId": 1071, + "networkId": 1071, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.evm.testnet.shimmer.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Metis Andromeda Mainnet", + "chain": "ETH", + "rpc": ["https://andromeda.metis.io/?owner=1088"], + "faucets": [], + "nativeCurrency": { "name": "Metis", "symbol": "METIS", "decimals": 18 }, + "infoURL": "https://www.metis.io", + "shortName": "metis-andromeda", + "chainId": 1088, + "networkId": 1088, + "explorers": [ + { "name": "blockscout", "url": "https://andromeda-explorer.metis.io", "standard": "EIP3091" } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.metis.io" }] + } + }, + { + "name": "MOAC mainnet", + "chain": "MOAC", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "MOAC", "symbol": "mc", "decimals": 18 }, + "infoURL": "https://moac.io", + "shortName": "moac", + "chainId": 1099, + "networkId": 1099, + "slip44": 314, + "explorers": [ + { "name": "moac explorer", "url": "https://explorer.moac.io", "standard": "none" } + ] + }, + { + "name": "Polygon zkEVM", + "title": "Polygon zkEVM", + "chain": "Polygon", + "rpc": ["https://zkevm-rpc.com"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://polygon.technology/polygon-zkevm", + "shortName": "zkevm", + "chainId": 1101, + "networkId": 1101, + "icon": "zkevm", + "explorers": [ + { + "name": "blockscout", + "url": "https://zkevm.polygonscan.com", + "icon": "zkevm", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.zkevm-rpc.com" }] + } + }, + { + "name": "WEMIX3.0 Mainnet", + "chain": "WEMIX", + "rpc": ["https://api.wemix.com", "wss://ws.wemix.com"], + "faucets": [], + "nativeCurrency": { "name": "WEMIX", "symbol": "WEMIX", "decimals": 18 }, + "infoURL": "https://wemix.com", + "shortName": "wemix", + "chainId": 1111, + "networkId": 1111, + "explorers": [ + { "name": "WEMIX Block Explorer", "url": "https://explorer.wemix.com", "standard": "EIP3091" } + ] + }, + { + "name": "WEMIX3.0 Testnet", + "chain": "TWEMIX", + "rpc": ["https://api.test.wemix.com", "wss://ws.test.wemix.com"], + "faucets": ["https://wallet.test.wemix.com/faucet"], + "nativeCurrency": { "name": "TestnetWEMIX", "symbol": "tWEMIX", "decimals": 18 }, + "infoURL": "https://wemix.com", + "shortName": "twemix", + "chainId": 1112, + "networkId": 1112, + "explorers": [ + { + "name": "WEMIX Testnet Microscope", + "url": "https://microscope.test.wemix.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Core Blockchain Testnet", + "chain": "Core", + "icon": "core", + "rpc": ["https://rpc.test.btcs.network/"], + "faucets": ["https://scan.test.btcs.network/faucet"], + "nativeCurrency": { + "name": "Core Blockchain Testnet Native Token", + "symbol": "tCORE", + "decimals": 18 + }, + "infoURL": "https://www.coredao.org", + "shortName": "tcore", + "chainId": 1115, + "networkId": 1115, + "explorers": [ + { + "name": "Core Scan Testnet", + "url": "https://scan.test.btcs.network", + "icon": "core", + "standard": "EIP3091" + } + ] + }, + { + "name": "Core Blockchain Mainnet", + "chain": "Core", + "icon": "core", + "rpc": ["https://rpc.coredao.org/", "https://rpc-core.icecreamswap.com"], + "faucets": [], + "nativeCurrency": { "name": "Core Blockchain Native Token", "symbol": "CORE", "decimals": 18 }, + "infoURL": "https://www.coredao.org", + "shortName": "core", + "chainId": 1116, + "networkId": 1116, + "explorers": [ + { + "name": "Core Scan", + "url": "https://scan.coredao.org", + "icon": "core", + "standard": "EIP3091" + } + ] + }, + { + "name": "Dogcoin Mainnet", + "chain": "DOGS", + "icon": "dogs", + "rpc": ["https://mainnet-rpc.dogcoin.me"], + "faucets": ["https://faucet.dogcoin.network"], + "nativeCurrency": { "name": "Dogcoin", "symbol": "DOGS", "decimals": 18 }, + "infoURL": "https://dogcoin.network", + "shortName": "DOGSm", + "chainId": 1117, + "networkId": 1117, + "explorers": [ + { "name": "Dogcoin", "url": "https://explorer.dogcoin.network", "standard": "EIP3091" } + ] + }, + { + "name": "DeFiChain EVM Network Mainnet", + "chain": "defichain-evm", + "status": "incubating", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "DeFiChain", "symbol": "DFI", "decimals": 18 }, + "infoURL": "https://meta.defichain.com/", + "shortName": "DFI", + "chainId": 1130, + "networkId": 1130, + "slip44": 1130, + "icon": "defichain-network", + "explorers": [] + }, + { + "name": "DeFiChain EVM Network Testnet", + "chain": "defichain-evm-testnet", + "status": "incubating", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "DeFiChain", "symbol": "DFI", "decimals": 18 }, + "infoURL": "https://meta.defichain.com/", + "shortName": "DFI-T", + "chainId": 1131, + "networkId": 1131, + "icon": "defichain-network", + "explorers": [] + }, + { + "name": "AmStar Testnet", + "chain": "AmStar", + "icon": "amstar", + "rpc": ["https://testnet-rpc.amstarscan.com"], + "faucets": [], + "nativeCurrency": { "name": "SINSO", "symbol": "SINSO", "decimals": 18 }, + "infoURL": "https://sinso.io", + "shortName": "ASARt", + "chainId": 1138, + "networkId": 1138, + "explorers": [ + { + "name": "amstarscan-testnet", + "url": "https://testnet.amstarscan.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "MathChain", + "chain": "MATH", + "rpc": [ + "https://mathchain-asia.maiziqianbao.net/rpc", + "https://mathchain-us.maiziqianbao.net/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "MathChain", "symbol": "MATH", "decimals": 18 }, + "infoURL": "https://mathchain.org", + "shortName": "MATH", + "chainId": 1139, + "networkId": 1139 + }, + { + "name": "MathChain Testnet", + "chain": "MATH", + "rpc": ["https://galois-hk.maiziqianbao.net/rpc"], + "faucets": ["https://scan.boka.network/#/Galois/faucet"], + "nativeCurrency": { "name": "MathChain", "symbol": "MATH", "decimals": 18 }, + "infoURL": "https://mathchain.org", + "shortName": "tMATH", + "chainId": 1140, + "networkId": 1140 + }, + { + "name": "Smart Host Teknoloji TESTNET", + "chain": "SHT", + "rpc": ["https://s2.tl.web.tr:4041"], + "faucets": [], + "nativeCurrency": { "name": "Smart Host Teknoloji TESTNET", "symbol": "tSHT", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://smart-host.com.tr", + "shortName": "sht", + "chainId": 1177, + "networkId": 1177, + "icon": "smarthost", + "explorers": [ + { + "name": "Smart Host Teknoloji TESTNET Explorer", + "url": "https://s2.tl.web.tr:4000", + "icon": "smarthost", + "standard": "EIP3091" + } + ] + }, + { + "name": "Iora Chain", + "chain": "IORA", + "icon": "iorachain", + "rpc": ["https://dataseed.iorachain.com"], + "faucets": [], + "nativeCurrency": { "name": "Iora", "symbol": "IORA", "decimals": 18 }, + "infoURL": "https://iorachain.com", + "shortName": "iora", + "chainId": 1197, + "networkId": 1197, + "explorers": [ + { "name": "ioraexplorer", "url": "https://explorer.iorachain.com", "standard": "EIP3091" } + ] + }, + { + "name": "Evanesco Testnet", + "chain": "Evanesco Testnet", + "rpc": ["https://seed5.evanesco.org:8547"], + "faucets": [], + "nativeCurrency": { "name": "AVIS", "symbol": "AVIS", "decimals": 18 }, + "infoURL": "https://evanesco.org/", + "shortName": "avis", + "chainId": 1201, + "networkId": 1201 + }, + { + "name": "World Trade Technical Chain Mainnet", + "chain": "WTT", + "rpc": ["https://rpc.cadaut.com", "wss://rpc.cadaut.com/ws"], + "faucets": [], + "nativeCurrency": { "name": "World Trade Token", "symbol": "WTT", "decimals": 18 }, + "infoURL": "http://www.cadaut.com", + "shortName": "wtt", + "chainId": 1202, + "networkId": 2048, + "explorers": [ + { "name": "WTTScout", "url": "https://explorer.cadaut.com", "standard": "EIP3091" } + ] + }, + { + "name": "Popcateum Mainnet", + "chain": "POPCATEUM", + "rpc": ["https://dataseed.popcateum.org"], + "faucets": [], + "nativeCurrency": { "name": "Popcat", "symbol": "POP", "decimals": 18 }, + "infoURL": "https://popcateum.org", + "shortName": "popcat", + "chainId": 1213, + "networkId": 1213, + "explorers": [ + { "name": "popcateum explorer", "url": "https://explorer.popcateum.org", "standard": "none" } + ] + }, + { + "name": "EnterChain Mainnet", + "chain": "ENTER", + "rpc": ["https://tapi.entercoin.net/"], + "faucets": [], + "nativeCurrency": { "name": "EnterCoin", "symbol": "ENTER", "decimals": 18 }, + "infoURL": "https://entercoin.net", + "shortName": "enter", + "chainId": 1214, + "networkId": 1214, + "icon": "enter", + "explorers": [ + { + "name": "Enter Explorer - Expenter", + "url": "https://explorer.entercoin.net", + "icon": "enter", + "standard": "EIP3091" + } + ] + }, + { + "name": "Exzo Network Mainnet", + "chain": "EXZO", + "icon": "exzo", + "rpc": ["https://mainnet.exzo.technology"], + "faucets": [], + "nativeCurrency": { "name": "Exzo", "symbol": "XZO", "decimals": 18 }, + "infoURL": "https://exzo.network", + "shortName": "xzo", + "chainId": 1229, + "networkId": 1229, + "explorers": [{ "name": "blockscout", "url": "https://exzoscan.io", "standard": "EIP3091" }] + }, + { + "name": "Ultron Testnet", + "chain": "Ultron", + "icon": "ultron", + "rpc": ["https://ultron-dev.io"], + "faucets": [], + "nativeCurrency": { "name": "Ultron", "symbol": "ULX", "decimals": 18 }, + "infoURL": "https://ultron.foundation", + "shortName": "UltronTestnet", + "chainId": 1230, + "networkId": 1230, + "explorers": [ + { + "name": "Ultron Testnet Explorer", + "url": "https://explorer.ultron-dev.io", + "icon": "ultron", + "standard": "none" + } + ] + }, + { + "name": "Ultron Mainnet", + "chain": "Ultron", + "icon": "ultron", + "rpc": ["https://ultron-rpc.net"], + "faucets": [], + "nativeCurrency": { "name": "Ultron", "symbol": "ULX", "decimals": 18 }, + "infoURL": "https://ultron.foundation", + "shortName": "UtronMainnet", + "chainId": 1231, + "networkId": 1231, + "explorers": [ + { + "name": "Ultron Explorer", + "url": "https://ulxscan.com", + "icon": "ultron", + "standard": "none" + } + ] + }, + { + "name": "Step Network", + "title": "Step Main Network", + "chain": "STEP", + "icon": "step", + "rpc": ["https://rpc.step.network"], + "faucets": [], + "nativeCurrency": { "name": "FITFI", "symbol": "FITFI", "decimals": 18 }, + "infoURL": "https://step.network", + "shortName": "step", + "chainId": 1234, + "networkId": 1234, + "explorers": [ + { "name": "StepScan", "url": "https://stepscan.io", "icon": "step", "standard": "EIP3091" } + ], + "parent": { + "type": "L2", + "chain": "eip155-43114", + "bridges": [{ "url": "https://bridge.step.network" }] + } + }, + { + "name": "OM Platform Mainnet", + "chain": "omplatform", + "rpc": ["https://rpc-cnx.omplatform.com/"], + "faucets": [], + "nativeCurrency": { "name": "OMCOIN", "symbol": "OM", "decimals": 18 }, + "infoURL": "https://omplatform.com/", + "shortName": "om", + "chainId": 1246, + "networkId": 1246, + "explorers": [ + { "name": "OMSCAN - Expenter", "url": "https://omscan.omplatform.com", "standard": "none" } + ] + }, + { + "name": "CIC Chain Testnet", + "chain": "CICT", + "rpc": ["https://testapi.cicscan.com"], + "faucets": ["https://cicfaucet.com"], + "nativeCurrency": { "name": "Crazy Internet Coin", "symbol": "CICT", "decimals": 18 }, + "infoURL": "https://www.cicchain.net", + "shortName": "CICT", + "chainId": 1252, + "networkId": 1252, + "icon": "cicchain", + "explorers": [ + { + "name": "CICscan", + "url": "https://testnet.cicscan.com", + "icon": "cicchain", + "standard": "EIP3091" + } + ] + }, + { + "name": "HALO Mainnet", + "chain": "HALO", + "rpc": ["https://nodes.halo.land"], + "faucets": [], + "nativeCurrency": { "name": "HALO", "symbol": "HO", "decimals": 18 }, + "infoURL": "https://halo.land/#/", + "shortName": "HO", + "chainId": 1280, + "networkId": 1280, + "explorers": [ + { "name": "HALOexplorer", "url": "https://browser.halo.land", "standard": "none" } + ] + }, + { + "name": "Moonbeam", + "chain": "MOON", + "rpc": ["https://rpc.api.moonbeam.network", "wss://wss.api.moonbeam.network"], + "faucets": [], + "nativeCurrency": { "name": "Glimmer", "symbol": "GLMR", "decimals": 18 }, + "infoURL": "https://moonbeam.network/networks/moonbeam/", + "shortName": "mbeam", + "chainId": 1284, + "networkId": 1284, + "explorers": [{ "name": "moonscan", "url": "https://moonbeam.moonscan.io", "standard": "none" }] + }, + { + "name": "Moonriver", + "chain": "MOON", + "rpc": [ + "https://rpc.api.moonriver.moonbeam.network", + "wss://wss.api.moonriver.moonbeam.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Moonriver", "symbol": "MOVR", "decimals": 18 }, + "infoURL": "https://moonbeam.network/networks/moonriver/", + "shortName": "mriver", + "chainId": 1285, + "networkId": 1285, + "explorers": [ + { "name": "moonscan", "url": "https://moonriver.moonscan.io", "standard": "none" } + ] + }, + { + "name": "Moonrock old", + "chain": "MOON", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Rocs", "symbol": "ROC", "decimals": 18 }, + "infoURL": "", + "shortName": "mrock-old", + "chainId": 1286, + "networkId": 1286, + "status": "deprecated" + }, + { + "name": "Moonbase Alpha", + "chain": "MOON", + "rpc": ["https://rpc.api.moonbase.moonbeam.network", "wss://wss.api.moonbase.moonbeam.network"], + "faucets": [], + "nativeCurrency": { "name": "Dev", "symbol": "DEV", "decimals": 18 }, + "infoURL": "https://docs.moonbeam.network/networks/testnet/", + "shortName": "mbase", + "chainId": 1287, + "networkId": 1287, + "explorers": [{ "name": "moonscan", "url": "https://moonbase.moonscan.io", "standard": "none" }] + }, + { + "name": "Moonrock", + "chain": "MOON", + "rpc": ["https://rpc.api.moonrock.moonbeam.network", "wss://wss.api.moonrock.moonbeam.network"], + "faucets": [], + "nativeCurrency": { "name": "Rocs", "symbol": "ROC", "decimals": 18 }, + "infoURL": "https://docs.moonbeam.network/learn/platform/networks/overview/", + "shortName": "mrock", + "chainId": 1288, + "networkId": 1288 + }, + { + "name": "Bobabeam", + "chain": "Bobabeam", + "rpc": [ + "https://bobabeam.boba.network", + "wss://wss.bobabeam.boba.network", + "https://replica.bobabeam.boba.network", + "wss://replica-wss.bobabeam.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "Bobabeam", + "chainId": 1294, + "networkId": 1294, + "explorers": [ + { + "name": "Bobabeam block explorer", + "url": "https://blockexplorer.bobabeam.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Bobabase Testnet", + "chain": "Bobabase Testnet", + "rpc": [ + "https://bobabase.boba.network", + "wss://wss.bobabase.boba.network", + "https://replica.bobabase.boba.network", + "wss://replica-wss.bobabase.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "Bobabase", + "chainId": 1297, + "networkId": 1297, + "explorers": [ + { + "name": "Bobabase block explorer", + "url": "https://blockexplorer.bobabase.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Dos Fuji Subnet", + "chain": "DOS", + "rpc": ["https://test.doschain.com/jsonrpc"], + "faucets": [], + "nativeCurrency": { "name": "Dos Native Token", "symbol": "DOS", "decimals": 18 }, + "infoURL": "http://doschain.io/", + "shortName": "TDOS", + "chainId": 1311, + "networkId": 1311, + "explorers": [{ "name": "dos-testnet", "url": "https://test.doscan.io", "standard": "EIP3091" }] + }, + { + "name": "Alyx Mainnet", + "chain": "ALYX", + "rpc": ["https://rpc.alyxchain.com"], + "faucets": [], + "nativeCurrency": { "name": "Alyx Chain Native Token", "symbol": "ALYX", "decimals": 18 }, + "infoURL": "https://www.alyxchain.com", + "shortName": "alyx", + "chainId": 1314, + "networkId": 1314, + "explorers": [{ "name": "alyxscan", "url": "https://www.alyxscan.com", "standard": "EIP3091" }], + "icon": "alyx" + }, + { + "name": "Aitd Mainnet", + "chain": "AITD", + "icon": "aitd", + "rpc": ["https://walletrpc.aitd.io", "https://node.aitd.io"], + "faucets": [], + "nativeCurrency": { "name": "AITD Mainnet", "symbol": "AITD", "decimals": 18 }, + "infoURL": "https://www.aitd.io/", + "shortName": "aitd", + "chainId": 1319, + "networkId": 1319, + "explorers": [ + { + "name": "AITD Chain Explorer Mainnet", + "url": "https://aitd-explorer-new.aitd.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Aitd Testnet", + "chain": "AITD", + "icon": "aitd", + "rpc": ["http://http-testnet.aitd.io"], + "faucets": ["https://aitd-faucet-pre.aitdcoin.com/"], + "nativeCurrency": { "name": "AITD Testnet", "symbol": "AITD", "decimals": 18 }, + "infoURL": "https://www.aitd.io/", + "shortName": "aitdtestnet", + "chainId": 1320, + "networkId": 1320, + "explorers": [ + { + "name": "AITD Chain Explorer Testnet", + "url": "https://block-explorer-testnet.aitd.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Geth Testnet", + "title": "Go Ethereum (Geth) Private Testnet", + "chain": "ETH", + "rpc": ["http://127.0.0.1:8545"], + "faucets": [], + "nativeCurrency": { "name": "Geth Testnet Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://geth.ethereum.org", + "shortName": "geth", + "chainId": 1337, + "networkId": 1337 + }, + { + "name": "Elysium Testnet", + "title": "An L1, carbon-neutral, tree-planting, metaverse dedicated blockchain created by VulcanForged", + "chain": "Elysium", + "rpc": ["https://elysium-test-rpc.vulcanforged.com"], + "faucets": [], + "nativeCurrency": { "name": "LAVA", "symbol": "LAVA", "decimals": 18 }, + "infoURL": "https://elysiumscan.vulcanforged.com", + "shortName": "ELST", + "chainId": 1338, + "networkId": 1338, + "explorers": [ + { + "name": "Elysium testnet explorer", + "url": "https://elysium-explorer.vulcanforged.com", + "standard": "none" + } + ] + }, + { + "name": "Elysium Mainnet", + "title": "An L1, carbon-neutral, tree-planting, metaverse dedicated blockchain created by VulcanForged", + "chain": "Elysium", + "rpc": ["https://rpc.elysiumchain.tech/"], + "faucets": [], + "nativeCurrency": { "name": "LAVA", "symbol": "LAVA", "decimals": 18 }, + "infoURL": "https://elysiumscan.vulcanforged.com", + "shortName": "ELSM", + "chainId": 1339, + "networkId": 1339, + "explorers": [ + { + "name": "Elysium mainnet explorer", + "url": "https://explorer.elysiumchain.tech", + "standard": "none" + } + ] + }, + { + "name": "CIC Chain Mainnet", + "chain": "CIC", + "rpc": ["https://xapi.cicscan.com"], + "faucets": [], + "nativeCurrency": { "name": "Crazy Internet Coin", "symbol": "CIC", "decimals": 18 }, + "infoURL": "https://www.cicchain.net", + "shortName": "CIC", + "chainId": 1353, + "networkId": 1353, + "icon": "cicchain", + "explorers": [ + { "name": "CICscan", "url": "https://cicscan.com", "icon": "cicchain", "standard": "EIP3091" } + ] + }, + { + "name": "AmStar Mainnet", + "chain": "AmStar", + "icon": "amstar", + "rpc": ["https://mainnet-rpc.amstarscan.com"], + "faucets": [], + "nativeCurrency": { "name": "SINSO", "symbol": "SINSO", "decimals": 18 }, + "infoURL": "https://sinso.io", + "shortName": "ASAR", + "chainId": 1388, + "networkId": 1388, + "explorers": [ + { "name": "amstarscan", "url": "https://mainnet.amstarscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Polygon zkEVM Testnet old", + "title": "Polygon zkEVM Testnet", + "chain": "Polygon", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://polygon.technology/solutions/polygon-zkevm/", + "shortName": "zkevmtest", + "chainId": 1402, + "networkId": 1402, + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer.public.zkevm-test.net", + "standard": "EIP3091" + } + ], + "status": "deprecated" + }, + { + "name": "Polygon zkEVM Testnet Pre Audit-Upgraded", + "title": "Polygon zkEVM Testnet Pre Audit-Upgraded", + "chain": "Polygon", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://polygon.technology/solutions/polygon-zkevm/", + "shortName": "testnet-zkEVM-mango-pre-audit-upgraded", + "chainId": 1422, + "networkId": 1422, + "explorers": [ + { + "name": "Polygon zkEVM explorer", + "url": "https://explorer.public.zkevm-test.net", + "standard": "EIP3091" + } + ], + "status": "deprecated" + }, + { + "name": "Rikeza Network Mainnet", + "title": "Rikeza Network Mainnet", + "chain": "Rikeza", + "rpc": ["https://rpc.rikscan.com"], + "faucets": [], + "nativeCurrency": { "name": "Rikeza", "symbol": "RIK", "decimals": 18 }, + "infoURL": "https://rikeza.io", + "shortName": "RIK", + "chainId": 1433, + "networkId": 1433, + "explorers": [ + { "name": "Rikeza Blockchain explorer", "url": "https://rikscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Polygon zkEVM Testnet", + "title": "Polygon zkEVM Testnet", + "chain": "Polygon", + "rpc": ["https://rpc.public.zkevm-test.net"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://polygon.technology/solutions/polygon-zkevm/", + "shortName": "testnet-zkEVM-mango", + "chainId": 1442, + "networkId": 1442, + "explorers": [ + { + "name": "Polygon zkEVM explorer", + "url": "https://explorer.public.zkevm-test.net", + "standard": "EIP3091" + } + ] + }, + { + "name": "Ctex Scan Blockchain", + "chain": "Ctex Scan Blockchain", + "icon": "ctex", + "rpc": ["https://mainnet-rpc.ctexscan.com/"], + "faucets": ["https://faucet.ctexscan.com"], + "nativeCurrency": { "name": "CTEX", "symbol": "CTEX", "decimals": 18 }, + "infoURL": "https://ctextoken.io", + "shortName": "CTEX", + "chainId": 1455, + "networkId": 1455, + "explorers": [ + { "name": "Ctex Scan Explorer", "url": "https://ctexscan.com", "standard": "none" } + ] + }, + { + "name": "Sherpax Mainnet", + "chain": "Sherpax Mainnet", + "rpc": ["https://mainnet.sherpax.io/rpc"], + "faucets": [], + "nativeCurrency": { "name": "KSX", "symbol": "KSX", "decimals": 18 }, + "infoURL": "https://sherpax.io/", + "shortName": "Sherpax", + "chainId": 1506, + "networkId": 1506, + "explorers": [ + { "name": "Sherpax Mainnet Explorer", "url": "https://evm.sherpax.io", "standard": "none" } + ] + }, + { + "name": "Sherpax Testnet", + "chain": "Sherpax Testnet", + "rpc": ["https://sherpax-testnet.chainx.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "KSX", "symbol": "KSX", "decimals": 18 }, + "infoURL": "https://sherpax.io/", + "shortName": "SherpaxTestnet", + "chainId": 1507, + "networkId": 1507, + "explorers": [ + { + "name": "Sherpax Testnet Explorer", + "url": "https://evm-pre.sherpax.io", + "standard": "none" + } + ] + }, + { + "name": "Beagle Messaging Chain", + "chain": "BMC", + "rpc": ["https://beagle.chat/eth"], + "faucets": ["https://faucet.beagle.chat/"], + "nativeCurrency": { "name": "Beagle", "symbol": "BG", "decimals": 18 }, + "infoURL": "https://beagle.chat/", + "shortName": "beagle", + "chainId": 1515, + "networkId": 1515, + "explorers": [ + { + "name": "Beagle Messaging Chain Explorer", + "url": "https://eth.beagle.chat", + "standard": "EIP3091" + } + ] + }, + { + "name": "Catecoin Chain Mainnet", + "chain": "Catechain", + "rpc": ["https://send.catechain.com"], + "faucets": [], + "nativeCurrency": { "name": "Catecoin", "symbol": "CATE", "decimals": 18 }, + "infoURL": "https://catechain.com", + "shortName": "cate", + "chainId": 1618, + "networkId": 1618 + }, + { + "name": "Atheios", + "chain": "ATH", + "rpc": ["https://wallet.atheios.com:8797"], + "faucets": [], + "nativeCurrency": { "name": "Atheios Ether", "symbol": "ATH", "decimals": 18 }, + "infoURL": "https://atheios.com", + "shortName": "ath", + "chainId": 1620, + "networkId": 11235813, + "slip44": 1620 + }, + { + "name": "Btachain", + "chain": "btachain", + "rpc": ["https://dataseed1.btachain.com/"], + "faucets": [], + "nativeCurrency": { "name": "Bitcoin Asset", "symbol": "BTA", "decimals": 18 }, + "infoURL": "https://bitcoinasset.io/", + "shortName": "bta", + "chainId": 1657, + "networkId": 1657 + }, + { + "name": "Horizen Yuma Testnet", + "shortName": "Yuma", + "chain": "Yuma", + "icon": "eon", + "rpc": ["https://yuma-testnet.horizenlabs.io/ethv1"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": ["https://yuma-testnet-faucet.horizen.io"], + "nativeCurrency": { "name": "Testnet Zen", "symbol": "tZEN", "decimals": 18 }, + "infoURL": "https://horizen.io/", + "chainId": 1662, + "networkId": 1662, + "slip44": 121, + "explorers": [ + { + "name": "Yuma Testnet Block Explorer", + "url": "https://yuma-explorer.horizen.io", + "icon": "eon", + "standard": "EIP3091" + } + ] + }, + { + "name": "LUDAN Mainnet", + "chain": "LUDAN", + "rpc": ["https://rpc.ludan.org/"], + "faucets": [], + "nativeCurrency": { "name": "LUDAN", "symbol": "LUDAN", "decimals": 18 }, + "infoURL": "https://www.ludan.org/", + "shortName": "LUDAN", + "icon": "ludan", + "chainId": 1688, + "networkId": 1688 + }, + { + "name": "Anytype EVM Chain", + "chain": "ETH", + "icon": "any", + "rpc": ["https://geth.anytype.io"], + "faucets": ["https://evm.anytype.io/faucet"], + "nativeCurrency": { "name": "ANY", "symbol": "ANY", "decimals": 18 }, + "infoURL": "https://evm.anytype.io", + "shortName": "AnytypeChain", + "chainId": 1701, + "networkId": 1701, + "explorers": [ + { + "name": "Anytype Explorer", + "url": "https://explorer.anytype.io", + "icon": "any", + "standard": "EIP3091" + } + ] + }, + { + "name": "TBSI Mainnet", + "title": "Thai Blockchain Service Infrastructure Mainnet", + "chain": "TBSI", + "rpc": ["https://rpc.blockchain.or.th"], + "faucets": [], + "nativeCurrency": { "name": "Jinda", "symbol": "JINDA", "decimals": 18 }, + "infoURL": "https://blockchain.or.th", + "shortName": "TBSI", + "chainId": 1707, + "networkId": 1707 + }, + { + "name": "TBSI Testnet", + "title": "Thai Blockchain Service Infrastructure Testnet", + "chain": "TBSI", + "rpc": ["https://rpc.testnet.blockchain.or.th"], + "faucets": ["https://faucet.blockchain.or.th"], + "nativeCurrency": { "name": "Jinda", "symbol": "JINDA", "decimals": 18 }, + "infoURL": "https://blockchain.or.th", + "shortName": "tTBSI", + "chainId": 1708, + "networkId": 1708 + }, + { + "name": "Palette Chain Mainnet", + "chain": "PLT", + "rpc": ["https://palette-rpc.com:22000"], + "faucets": [], + "nativeCurrency": { "name": "Palette Token", "symbol": "PLT", "decimals": 18 }, + "features": [], + "infoURL": "https://hashpalette.com/", + "shortName": "PaletteChain", + "chainId": 1718, + "networkId": 1718, + "icon": "PLT", + "explorers": [ + { "name": "Palettescan", "url": "https://palettescan.com", "icon": "PLT", "standard": "none" } + ] + }, + { + "name": "Kerleano", + "title": "Proof of Carbon Reduction testnet", + "chain": "CRC", + "status": "active", + "rpc": [ + "https://cacib-saturn-test.francecentral.cloudapp.azure.com", + "wss://cacib-saturn-test.francecentral.cloudapp.azure.com:9443" + ], + "faucets": ["https://github.com/ethereum-pocr/kerleano/blob/main/docs/faucet.md"], + "nativeCurrency": { "name": "Carbon Reduction Coin", "symbol": "CRC", "decimals": 18 }, + "infoURL": "https://github.com/ethereum-pocr/kerleano", + "shortName": "kerleano", + "chainId": 1804, + "networkId": 1804, + "explorers": [ + { + "name": "Lite Explorer", + "url": "https://ethereum-pocr.github.io/explorer/kerleano", + "standard": "EIP3091" + } + ] + }, + { + "name": "Rabbit Analog Testnet Chain", + "chain": "rAna", + "icon": "rabbit", + "rpc": ["https://rabbit.analog-rpc.com"], + "faucets": ["https://analogfaucet.com"], + "nativeCurrency": { + "name": "Rabbit Analog Test Chain Native Token ", + "symbol": "rAna", + "decimals": 18 + }, + "infoURL": "https://rabbit.analogscan.com", + "shortName": "rAna", + "chainId": 1807, + "networkId": 1807, + "explorers": [ + { "name": "blockscout", "url": "https://rabbit.analogscan.com", "standard": "none" } + ] + }, + { + "name": "Cube Chain Mainnet", + "chain": "Cube", + "icon": "cube", + "rpc": [ + "https://http-mainnet.cube.network", + "wss://ws-mainnet.cube.network", + "https://http-mainnet-sg.cube.network", + "wss://ws-mainnet-sg.cube.network", + "https://http-mainnet-us.cube.network", + "wss://ws-mainnet-us.cube.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Cube Chain Native Token", "symbol": "CUBE", "decimals": 18 }, + "infoURL": "https://www.cube.network", + "shortName": "cube", + "chainId": 1818, + "networkId": 1818, + "slip44": 1818, + "explorers": [{ "name": "cube-scan", "url": "https://cubescan.network", "standard": "EIP3091" }] + }, + { + "name": "Cube Chain Testnet", + "chain": "Cube", + "icon": "cube", + "rpc": [ + "https://http-testnet.cube.network", + "wss://ws-testnet.cube.network", + "https://http-testnet-sg.cube.network", + "wss://ws-testnet-sg.cube.network", + "https://http-testnet-jp.cube.network", + "wss://ws-testnet-jp.cube.network", + "https://http-testnet-us.cube.network", + "wss://ws-testnet-us.cube.network" + ], + "faucets": ["https://faucet.cube.network"], + "nativeCurrency": { "name": "Cube Chain Test Native Token", "symbol": "CUBET", "decimals": 18 }, + "infoURL": "https://www.cube.network", + "shortName": "cubet", + "chainId": 1819, + "networkId": 1819, + "slip44": 1819, + "explorers": [ + { "name": "cubetest-scan", "url": "https://testnet.cubescan.network", "standard": "EIP3091" } + ] + }, + { + "name": "Teslafunds", + "chain": "TSF", + "rpc": ["https://tsfapi.europool.me"], + "faucets": [], + "nativeCurrency": { "name": "Teslafunds Ether", "symbol": "TSF", "decimals": 18 }, + "infoURL": "https://teslafunds.io", + "shortName": "tsf", + "chainId": 1856, + "networkId": 1 + }, + { + "name": "Gitshock Cartenz Testnet", + "chain": "Gitshock Cartenz", + "icon": "gitshockchain", + "rpc": ["https://rpc.cartenz.works"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Gitshock Cartenz", "symbol": "tGTFX", "decimals": 18 }, + "infoURL": "https://gitshock.com", + "shortName": "gitshockchain", + "chainId": 1881, + "networkId": 1881, + "explorers": [ + { "name": "blockscout", "url": "https://scan.cartenz.works", "standard": "EIP3091" } + ] + }, + { + "name": "Lightlink Phoenix Mainnet", + "chain": "Lightlink Phoenix Mainnet", + "icon": "lightlink", + "rpc": [ + "https://replicator-01.phoenix.lightlink.io/rpc/v1", + "https://replicator-02.phoenix.lightlink.io/rpc/v1" + ], + "features": [{ "name": "EIP155" }], + "faucets": [], + "nativeCurrency": { "name": "Ethereum", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://lightlink.io", + "shortName": "lightlink_phoenix", + "chainId": 1890, + "networkId": 1890, + "explorers": [ + { + "name": "phoenix", + "url": "https://phoenix.lightlink.io", + "icon": "lightlink", + "standard": "EIP3091" + } + ] + }, + { + "name": "Lightlink Pegasus Testnet", + "chain": "Lightlink Pegasus Testnet", + "icon": "lightlink", + "rpc": [ + "https://replicator-01.pegasus.lightlink.io/rpc/v1", + "https://replicator-02.pegasus.lightlink.io/rpc/v1" + ], + "features": [{ "name": "EIP155" }], + "faucets": ["https://pegasus-faucet-react.vercel.app"], + "nativeCurrency": { "name": "Ethereum", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://lightlink.io", + "shortName": "lightlink_pegasus", + "chainId": 1891, + "networkId": 1891, + "explorers": [ + { + "name": "pegasus", + "url": "https://pegasus.lightlink.io", + "icon": "lightlink", + "standard": "EIP3091" + } + ] + }, + { + "name": "BON Network", + "chain": "BON", + "rpc": ["http://rpc.boyanet.org:8545", "ws://rpc.boyanet.org:8546"], + "faucets": [], + "nativeCurrency": { "name": "BOYACoin", "symbol": "BOY", "decimals": 18 }, + "infoURL": "https://boyanet.org", + "shortName": "boya", + "chainId": 1898, + "networkId": 1, + "explorers": [ + { "name": "explorer", "url": "https://explorer.boyanet.org:4001", "standard": "EIP3091" } + ] + }, + { + "name": "Bitcichain Mainnet", + "chain": "BITCI", + "icon": "bitci", + "rpc": ["https://rpc.bitci.com"], + "faucets": [], + "nativeCurrency": { "name": "Bitci", "symbol": "BITCI", "decimals": 18 }, + "infoURL": "https://www.bitcichain.com", + "shortName": "bitci", + "chainId": 1907, + "networkId": 1907, + "explorers": [ + { "name": "Bitci Explorer", "url": "https://bitciexplorer.com", "standard": "EIP3091" } + ] + }, + { + "name": "Bitcichain Testnet", + "chain": "TBITCI", + "icon": "bitci", + "rpc": ["https://testnet.bitcichain.com"], + "faucets": ["https://faucet.bitcichain.com"], + "nativeCurrency": { "name": "Test Bitci", "symbol": "TBITCI", "decimals": 18 }, + "infoURL": "https://www.bitcichain.com", + "shortName": "tbitci", + "chainId": 1908, + "networkId": 1908, + "explorers": [ + { + "name": "Bitci Explorer Testnet", + "url": "https://testnet.bitciexplorer.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "ONUS Chain Testnet", + "title": "ONUS Chain Testnet", + "chain": "onus", + "rpc": ["https://rpc-testnet.onuschain.io"], + "faucets": [], + "nativeCurrency": { "name": "ONUS", "symbol": "ONUS", "decimals": 18 }, + "infoURL": "https://onuschain.io", + "shortName": "onus-testnet", + "chainId": 1945, + "networkId": 1945, + "explorers": [ + { + "name": "Onus explorer testnet", + "url": "https://explorer-testnet.onuschain.io", + "icon": "onus", + "standard": "EIP3091" + } + ] + }, + { + "name": "D-Chain Mainnet", + "chain": "D-Chain", + "rpc": [ + "https://mainnet.d-chain.network/ext/bc/2ZiR1Bro5E59siVuwdNuRFzqL95NkvkbzyLBdrsYR9BLSHV7H4/rpc" + ], + "nativeCurrency": { "name": "DOINX", "symbol": "DOINX", "decimals": 18 }, + "shortName": "dchain-mainnet", + "chainId": 1951, + "networkId": 1951, + "icon": "dchain", + "faucets": [], + "infoURL": "" + }, + { + "name": "Dexilla Testnet", + "chain": "Dexilla", + "rpc": ["https://rpc.dexilla.com"], + "faucets": [], + "icon": "dxz", + "nativeCurrency": { "name": "Dexilla Native Token", "symbol": "DXZ", "decimals": 18 }, + "infoURL": "https://dexilla.com", + "shortName": "Dexilla", + "chainId": 1954, + "networkId": 1954, + "explorers": [ + { "name": "dos-mainnet", "url": "https://exp.dexilla.com", "standard": "EIP3091" } + ], + "parent": { + "type": "L2", + "chain": "eip155-11155111", + "bridges": [{ "url": "https://bridge.dexilla.com" }] + } + }, + { + "name": "Eleanor", + "title": "Metatime Testnet Eleanor", + "chain": "MTC", + "rpc": ["https://rpc.metatime.com/eleanor", "wss://ws.metatime.com/eleanor"], + "faucets": ["https://faucet.metatime.com/eleanor"], + "nativeCurrency": { "name": "Eleanor Metacoin", "symbol": "MTC", "decimals": 18 }, + "infoURL": "https://eleanor.metatime.com", + "shortName": "mtc", + "chainId": 1967, + "networkId": 1967, + "explorers": [ + { + "name": "metaexplorer-eleanor", + "url": "https://explorer.metatime.com/eleanor", + "standard": "EIP3091" + } + ] + }, + { + "name": "Super Smart Chain Testnet", + "chain": "TSCS", + "rpc": ["https://testnet.superexchain.com:8544"], + "faucets": ["https://testnet.superexchain.com"], + "nativeCurrency": { "name": "Super Chain Native Token", "symbol": "TSCS", "decimals": 18 }, + "infoURL": "https://testnet.superexchain.com", + "shortName": "tscs", + "chainId": 1969, + "networkId": 1969, + "icon": "super", + "explorers": [ + { "name": "blockscout", "url": "https://testnetscan.superexchain.com", "standard": "EIP3091" } + ] + }, + { + "name": "Super Smart Chain Mainnet", + "chain": "SCS", + "rpc": ["https://superexchain.com:8545"], + "faucets": [], + "nativeCurrency": { "name": "Super Chain Native Token", "symbol": "SCS", "decimals": 18 }, + "infoURL": "https://superexchain.com", + "shortName": "scs", + "chainId": 1970, + "networkId": 1970, + "icon": "super", + "explorers": [ + { "name": "blockscout", "url": "https://scan.superexchain.com", "standard": "EIP3091" } + ] + }, + { + "name": "Atelier", + "title": "Atelier Test Network", + "chain": "ALTR", + "rpc": ["https://1971.network/atlr", "wss://1971.network/atlr"], + "faucets": [], + "nativeCurrency": { "name": "ATLR", "symbol": "ATLR", "decimals": 18 }, + "infoURL": "https://1971.network/", + "shortName": "atlr", + "chainId": 1971, + "networkId": 1971, + "icon": "atlr" + }, + { + "name": "ONUS Chain Mainnet", + "title": "ONUS Chain Mainnet", + "chain": "onus", + "rpc": ["https://rpc.onuschain.io", "wss://ws.onuschain.io"], + "faucets": [], + "nativeCurrency": { "name": "ONUS", "symbol": "ONUS", "decimals": 18 }, + "infoURL": "https://onuschain.io", + "shortName": "onus-mainnet", + "chainId": 1975, + "networkId": 1975, + "explorers": [ + { + "name": "Onus explorer mainnet", + "url": "https://explorer.onuschain.io", + "icon": "onus", + "standard": "EIP3091" + } + ] + }, + { + "name": "Eurus Testnet", + "chain": "EUN", + "rpc": ["https://testnet.eurus.network"], + "faucets": [], + "nativeCurrency": { "name": "Eurus", "symbol": "EUN", "decimals": 18 }, + "infoURL": "https://eurus.network", + "shortName": "euntest", + "chainId": 1984, + "networkId": 1984, + "icon": "eurus", + "explorers": [ + { + "name": "testnetexplorer", + "url": "https://testnetexplorer.eurus.network", + "icon": "eurus", + "standard": "none" + } + ] + }, + { + "name": "EtherGem", + "chain": "EGEM", + "rpc": ["https://jsonrpc.egem.io/custom"], + "faucets": [], + "nativeCurrency": { "name": "EtherGem Ether", "symbol": "EGEM", "decimals": 18 }, + "infoURL": "https://egem.io", + "shortName": "egem", + "chainId": 1987, + "networkId": 1987, + "slip44": 1987 + }, + { + "name": "Ekta", + "chain": "EKTA", + "rpc": ["https://main.ekta.io"], + "faucets": [], + "nativeCurrency": { "name": "EKTA", "symbol": "EKTA", "decimals": 18 }, + "infoURL": "https://www.ekta.io", + "shortName": "ekta", + "chainId": 1994, + "networkId": 1994, + "icon": "ekta", + "explorers": [ + { "name": "ektascan", "url": "https://ektascan.io", "icon": "ekta", "standard": "EIP3091" } + ] + }, + { + "name": "edeXa Testnet", + "chain": "edeXa TestNetwork", + "rpc": [ + "https://testnet.edexa.com/rpc", + "https://io-dataseed1.testnet.edexa.io-market.com/rpc" + ], + "faucets": ["https://faucet.edexa.com/"], + "nativeCurrency": { "name": "EDEXA", "symbol": "EDX", "decimals": 18 }, + "infoURL": "https://edexa.com/", + "shortName": "edx", + "chainId": 1995, + "networkId": 1995, + "icon": "edexa", + "explorers": [ + { "name": "edexa-testnet", "url": "https://explorer.edexa.com", "standard": "EIP3091" } + ] + }, + { + "name": "Dogechain Mainnet", + "chain": "DC", + "icon": "dogechain", + "rpc": [ + "https://rpc.dogechain.dog", + "https://rpc-us.dogechain.dog", + "https://rpc01.dogechain.dog" + ], + "faucets": [], + "nativeCurrency": { "name": "Dogecoin", "symbol": "DOGE", "decimals": 18 }, + "infoURL": "https://dogechain.dog", + "shortName": "dc", + "chainId": 2000, + "networkId": 2000, + "explorers": [ + { + "name": "dogechain explorer", + "url": "https://explorer.dogechain.dog", + "standard": "EIP3091" + } + ] + }, + { + "name": "Milkomeda C1 Mainnet", + "chain": "milkAda", + "icon": "milkomeda", + "rpc": [ + "https://rpc-mainnet-cardano-evm.c1.milkomeda.com", + "wss://rpc-mainnet-cardano-evm.c1.milkomeda.com" + ], + "faucets": [], + "nativeCurrency": { "name": "milkAda", "symbol": "mADA", "decimals": 18 }, + "infoURL": "https://milkomeda.com", + "shortName": "milkAda", + "chainId": 2001, + "networkId": 2001, + "explorers": [ + { + "name": "Blockscout", + "url": "https://explorer-mainnet-cardano-evm.c1.milkomeda.com", + "standard": "none" + } + ] + }, + { + "name": "Milkomeda A1 Mainnet", + "chain": "milkALGO", + "icon": "milkomeda", + "rpc": [ + "https://rpc-mainnet-algorand-rollup.a1.milkomeda.com", + "wss://rpc-mainnet-algorand-rollup.a1.milkomeda.com/ws" + ], + "faucets": [], + "nativeCurrency": { "name": "milkALGO", "symbol": "mALGO", "decimals": 18 }, + "infoURL": "https://milkomeda.com", + "shortName": "milkALGO", + "chainId": 2002, + "networkId": 2002, + "explorers": [ + { + "name": "Blockscout", + "url": "https://explorer-mainnet-algorand-rollup.a1.milkomeda.com", + "standard": "none" + } + ] + }, + { + "name": "CloudWalk Testnet", + "chain": "CloudWalk Testnet", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "CloudWalk Native Token", "symbol": "CWN", "decimals": 18 }, + "infoURL": "https://cloudwalk.io", + "shortName": "cloudwalk_testnet", + "chainId": 2008, + "networkId": 2008, + "explorers": [ + { + "name": "CloudWalk Testnet Explorer", + "url": "https://explorer.testnet.cloudwalk.io", + "standard": "none" + } + ] + }, + { + "name": "CloudWalk Mainnet", + "chain": "CloudWalk Mainnet", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "CloudWalk Native Token", "symbol": "CWN", "decimals": 18 }, + "infoURL": "https://cloudwalk.io", + "shortName": "cloudwalk_mainnet", + "chainId": 2009, + "networkId": 2009, + "explorers": [ + { + "name": "CloudWalk Mainnet Explorer", + "url": "https://explorer.mainnet.cloudwalk.io", + "standard": "none" + } + ] + }, + { + "name": "MainnetZ Mainnet", + "chain": "NetZ", + "icon": "mainnetz", + "rpc": ["https://mainnet-rpc.mainnetz.io"], + "faucets": ["https://faucet.mainnetz.io"], + "nativeCurrency": { "name": "MainnetZ", "symbol": "NetZ", "decimals": 18 }, + "infoURL": "https://mainnetz.io", + "shortName": "NetZm", + "chainId": 2016, + "networkId": 2016, + "explorers": [ + { "name": "MainnetZ", "url": "https://explorer.mainnetz.io", "standard": "EIP3091" } + ] + }, + { + "name": "PublicMint Devnet", + "title": "Public Mint Devnet", + "chain": "PublicMint", + "rpc": ["https://rpc.dev.publicmint.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "USD", "symbol": "USD", "decimals": 18 }, + "infoURL": "https://publicmint.com", + "shortName": "pmint_dev", + "chainId": 2018, + "networkId": 2018, + "slip44": 60, + "explorers": [ + { + "name": "PublicMint Explorer", + "url": "https://explorer.dev.publicmint.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "PublicMint Testnet", + "title": "Public Mint Testnet", + "chain": "PublicMint", + "rpc": ["https://rpc.tst.publicmint.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "USD", "symbol": "USD", "decimals": 18 }, + "infoURL": "https://publicmint.com", + "shortName": "pmint_test", + "chainId": 2019, + "networkId": 2019, + "slip44": 60, + "explorers": [ + { + "name": "PublicMint Explorer", + "url": "https://explorer.tst.publicmint.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "PublicMint Mainnet", + "title": "Public Mint Mainnet", + "chain": "PublicMint", + "rpc": ["https://rpc.publicmint.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "USD", "symbol": "USD", "decimals": 18 }, + "infoURL": "https://publicmint.com", + "shortName": "pmint", + "chainId": 2020, + "networkId": 2020, + "slip44": 60, + "explorers": [ + { + "name": "PublicMint Explorer", + "url": "https://explorer.publicmint.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Edgeware EdgeEVM Mainnet", + "chain": "EDG", + "icon": "edgeware", + "rpc": [ + "https://edgeware-evm.jelliedowl.net", + "https://mainnet2.edgewa.re/evm", + "https://mainnet3.edgewa.re/evm", + "https://mainnet4.edgewa.re/evm", + "https://mainnet5.edgewa.re/evm", + "wss://edgeware.jelliedowl.net", + "wss://mainnet2.edgewa.re", + "wss://mainnet3.edgewa.re", + "wss://mainnet4.edgewa.re", + "wss://mainnet5.edgewa.re" + ], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Edgeware", "symbol": "EDG", "decimals": 18 }, + "infoURL": "https://edgeware.io", + "shortName": "edg", + "chainId": 2021, + "networkId": 2021, + "slip44": 523, + "explorers": [ + { "name": "Edgscan by Bharathcoorg", "url": "https://edgscan.live", "standard": "EIP3091" }, + { + "name": "Subscan", + "url": "https://edgeware.subscan.io", + "standard": "none", + "icon": "subscan" + } + ] + }, + { + "name": "Beresheet BereEVM Testnet", + "chain": "EDG", + "rpc": ["https://beresheet-evm.jelliedowl.net", "wss://beresheet.jelliedowl.net"], + "faucets": [], + "nativeCurrency": { "name": "Testnet EDG", "symbol": "tEDG", "decimals": 18 }, + "infoURL": "https://edgeware.io/build", + "shortName": "edgt", + "chainId": 2022, + "networkId": 2022, + "explorers": [ + { + "name": "Edgscan by Bharathcoorg", + "url": "https://testnet.edgscan.live", + "standard": "EIP3091" + } + ] + }, + { + "name": "Taycan Testnet", + "chain": "Taycan", + "rpc": ["https://test-taycan.hupayx.io"], + "faucets": ["https://ttaycan-faucet.hupayx.io/"], + "nativeCurrency": { "name": "test-Shuffle", "symbol": "tSFL", "decimals": 18 }, + "infoURL": "https://hupayx.io", + "shortName": "taycan-testnet", + "chainId": 2023, + "networkId": 2023, + "icon": "shuffle", + "explorers": [ + { + "name": "Taycan Explorer(Blockscout)", + "url": "https://evmscan-test.hupayx.io", + "standard": "none", + "icon": "shuffle" + }, + { + "name": "Taycan Cosmos Explorer", + "url": "https://cosmoscan-test.hupayx.io", + "standard": "none", + "icon": "shuffle" + } + ] + }, + { + "name": "Rangers Protocol Mainnet", + "chain": "Rangers", + "icon": "rangers", + "rpc": ["https://mainnet.rangersprotocol.com/api/jsonrpc"], + "faucets": [], + "nativeCurrency": { "name": "Rangers Protocol Gas", "symbol": "RPG", "decimals": 18 }, + "infoURL": "https://rangersprotocol.com", + "shortName": "rpg", + "chainId": 2025, + "networkId": 2025, + "slip44": 1008, + "explorers": [ + { "name": "rangersscan", "url": "https://scan.rangersprotocol.com", "standard": "none" } + ] + }, + { + "name": "OriginTrail Parachain", + "chain": "OTP", + "rpc": ["https://astrosat.origintrail.network", "wss://parachain-rpc.origin-trail.network"], + "faucets": [], + "nativeCurrency": { "name": "OriginTrail Parachain Token", "symbol": "OTP", "decimals": 12 }, + "infoURL": "https://parachain.origintrail.io", + "shortName": "otp", + "chainId": 2043, + "networkId": 2043 + }, + { + "name": "Stratos Testnet", + "chain": "STOS", + "rpc": ["https://web3-testnet-rpc.thestratos.org"], + "faucets": [], + "nativeCurrency": { "name": "STOS", "symbol": "STOS", "decimals": 18 }, + "infoURL": "https://www.thestratos.org", + "shortName": "stos-testnet", + "chainId": 2047, + "networkId": 2047, + "explorers": [ + { + "name": "Stratos EVM Explorer (Blockscout)", + "url": "https://web3-testnet-explorer.thestratos.org", + "standard": "none" + }, + { + "name": "Stratos Cosmos Explorer (BigDipper)", + "url": "https://big-dipper-dev.thestratos.org", + "standard": "none" + } + ] + }, + { + "name": "Stratos Mainnet", + "chain": "STOS", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "STOS", "symbol": "STOS", "decimals": 18 }, + "infoURL": "https://www.thestratos.org", + "shortName": "stos-mainnet", + "chainId": 2048, + "networkId": 2048, + "status": "incubating" + }, + { + "name": "Quokkacoin Mainnet", + "chain": "Qkacoin", + "rpc": ["https://rpc.qkacoin.org"], + "faucets": [], + "nativeCurrency": { "name": "Qkacoin", "symbol": "QKA", "decimals": 18 }, + "infoURL": "https://qkacoin.org", + "shortName": "QKA", + "chainId": 2077, + "networkId": 2077, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.qkacoin.org", "standard": "EIP3091" } + ] + }, + { + "name": "Ecoball Mainnet", + "chain": "ECO", + "rpc": ["https://api.ecoball.org/ecoball/"], + "faucets": [], + "nativeCurrency": { "name": "Ecoball Coin", "symbol": "ECO", "decimals": 18 }, + "infoURL": "https://ecoball.org", + "shortName": "eco", + "chainId": 2100, + "networkId": 2100, + "explorers": [ + { "name": "Ecoball Explorer", "url": "https://scan.ecoball.org", "standard": "EIP3091" } + ] + }, + { + "name": "Ecoball Testnet Espuma", + "chain": "ECO", + "rpc": ["https://api.ecoball.org/espuma/"], + "faucets": [], + "nativeCurrency": { "name": "Espuma Coin", "symbol": "ECO", "decimals": 18 }, + "infoURL": "https://ecoball.org", + "shortName": "esp", + "chainId": 2101, + "networkId": 2101, + "explorers": [ + { + "name": "Ecoball Testnet Explorer", + "url": "https://espuma-scan.ecoball.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Exosama Network", + "chain": "EXN", + "rpc": ["https://rpc.exosama.com", "wss://rpc.exosama.com"], + "faucets": [], + "nativeCurrency": { "name": "Sama Token", "symbol": "SAMA", "decimals": 18 }, + "infoURL": "https://moonsama.com", + "shortName": "exn", + "chainId": 2109, + "networkId": 2109, + "slip44": 2109, + "icon": "exn", + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer.exosama.com", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "Metaplayerone Mainnet", + "chain": "METAD", + "icon": "metad", + "rpc": ["https://rpc.metaplayer.one/"], + "faucets": [], + "nativeCurrency": { "name": "METAD", "symbol": "METAD", "decimals": 18 }, + "infoURL": "https://docs.metaplayer.one/", + "shortName": "Metad", + "chainId": 2122, + "networkId": 2122, + "explorers": [ + { + "name": "Metad Scan", + "url": "https://scan.metaplayer.one", + "icon": "metad", + "standard": "EIP3091" + } + ] + }, + { + "name": "Defi Oracle Meta Testnet", + "chain": "dfiometatest", + "icon": "defioraclemeta", + "rpc": ["https://rpc.public-2138.defi-oracle.io", "wss://rpc.public-2138.defi-oracle.io"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "testEther", "symbol": "tETH", "decimals": 18 }, + "infoURL": "https://defi-oracle.io/", + "shortName": "dfio-meta-test", + "chainId": 2138, + "networkId": 21, + "slip44": 60, + "ens": { "registry": "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85" }, + "explorers": [ + { "name": "Quorum Explorer", "url": "https://public-2138.defi-oracle.io", "standard": "none" } + ] + }, + { + "name": "BOSagora Mainnet", + "chain": "ETH", + "rpc": ["https://mainnet.bosagora.org", "https://rpc.bosagora.org"], + "faucets": [], + "nativeCurrency": { "name": "BOSAGORA", "symbol": "BOA", "decimals": 18 }, + "infoURL": "https://docs.bosagora.org", + "shortName": "boa", + "chainId": 2151, + "networkId": 2151, + "icon": "agora", + "explorers": [ + { "name": "BOASCAN", "url": "https://boascan.io", "icon": "agora", "standard": "EIP3091" } + ] + }, + { + "name": "Findora Mainnet", + "chain": "Findora", + "rpc": ["https://rpc-mainnet.findora.org"], + "faucets": [], + "nativeCurrency": { "name": "FRA", "symbol": "FRA", "decimals": 18 }, + "infoURL": "https://findora.org/", + "shortName": "fra", + "chainId": 2152, + "networkId": 2152, + "explorers": [ + { "name": "findorascan", "url": "https://evm.findorascan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Findora Testnet", + "chain": "Testnet-anvil", + "rpc": ["https://prod-testnet.prod.findora.org:8545/"], + "faucets": [], + "nativeCurrency": { "name": "FRA", "symbol": "FRA", "decimals": 18 }, + "infoURL": "https://findora.org/", + "shortName": "findora-testnet", + "chainId": 2153, + "networkId": 2153, + "explorers": [ + { + "name": "findorascan", + "url": "https://testnet-anvil.evm.findorascan.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Findora Forge", + "chain": "Testnet-forge", + "rpc": ["https://prod-forge.prod.findora.org:8545/"], + "faucets": [], + "nativeCurrency": { "name": "FRA", "symbol": "FRA", "decimals": 18 }, + "infoURL": "https://findora.org/", + "shortName": "findora-forge", + "chainId": 2154, + "networkId": 2154, + "explorers": [ + { + "name": "findorascan", + "url": "https://testnet-forge.evm.findorascan.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bitcoin EVM", + "chain": "Bitcoin EVM", + "rpc": ["https://connect.bitcoinevm.com"], + "faucets": [], + "nativeCurrency": { "name": "Bitcoin", "symbol": "eBTC", "decimals": 18 }, + "infoURL": "https://bitcoinevm.com", + "shortName": "eBTC", + "chainId": 2203, + "networkId": 2203, + "icon": "ebtc", + "explorers": [ + { + "name": "Explorer", + "url": "https://explorer.bitcoinevm.com", + "icon": "ebtc", + "standard": "none" + } + ] + }, + { + "name": "Evanesco Mainnet", + "chain": "EVA", + "rpc": ["https://seed4.evanesco.org:8546"], + "faucets": [], + "nativeCurrency": { "name": "EVA", "symbol": "EVA", "decimals": 18 }, + "infoURL": "https://evanesco.org/", + "shortName": "evanesco", + "chainId": 2213, + "networkId": 2213, + "icon": "evanesco", + "explorers": [ + { "name": "Evanesco Explorer", "url": "https://explorer.evanesco.org", "standard": "none" } + ] + }, + { + "name": "Kava EVM Testnet", + "chain": "KAVA", + "rpc": ["https://evm.testnet.kava.io", "wss://wevm.testnet.kava.io"], + "faucets": ["https://faucet.kava.io"], + "nativeCurrency": { "name": "TKava", "symbol": "TKAVA", "decimals": 18 }, + "infoURL": "https://www.kava.io", + "shortName": "tkava", + "chainId": 2221, + "networkId": 2221, + "icon": "kava", + "explorers": [ + { + "name": "Kava Testnet Explorer", + "url": "https://explorer.testnet.kava.io", + "standard": "EIP3091", + "icon": "kava" + } + ] + }, + { + "name": "Kava EVM", + "chain": "KAVA", + "rpc": [ + "https://evm.kava.io", + "https://evm2.kava.io", + "wss://wevm.kava.io", + "wss://wevm2.kava.io" + ], + "faucets": [], + "nativeCurrency": { "name": "Kava", "symbol": "KAVA", "decimals": 18 }, + "infoURL": "https://www.kava.io", + "shortName": "kava", + "chainId": 2222, + "networkId": 2222, + "icon": "kava", + "explorers": [ + { + "name": "Kava EVM Explorer", + "url": "https://explorer.kava.io", + "standard": "EIP3091", + "icon": "kava" + } + ] + }, + { + "name": "VChain Mainnet", + "chain": "VChain", + "rpc": ["https://bc.vcex.xyz"], + "faucets": [], + "nativeCurrency": { "name": "VNDT", "symbol": "VNDT", "decimals": 18 }, + "infoURL": "https://bo.vcex.xyz/", + "shortName": "VChain", + "chainId": 2223, + "networkId": 2223, + "explorers": [{ "name": "VChain Scan", "url": "https://scan.vcex.xyz", "standard": "EIP3091" }] + }, + { + "name": "BOMB Chain", + "chain": "BOMB", + "rpc": ["https://rpc.bombchain.com"], + "faucets": [], + "nativeCurrency": { "name": "BOMB Token", "symbol": "BOMB", "decimals": 18 }, + "infoURL": "https://www.bombchain.com", + "shortName": "bomb", + "chainId": 2300, + "networkId": 2300, + "icon": "bomb", + "explorers": [ + { "name": "bombscan", "icon": "bomb", "url": "https://bombscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Arevia", + "chain": "Arevia", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Arev", "symbol": "ARÉV", "decimals": 18 }, + "infoURL": "", + "shortName": "arevia", + "chainId": 2309, + "networkId": 2309, + "explorers": [], + "status": "incubating" + }, + { + "name": "Altcoinchain", + "chain": "mainnet", + "rpc": ["https://rpc0.altcoinchain.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Altcoin", "symbol": "ALT", "decimals": 18 }, + "infoURL": "https://altcoinchain.org", + "shortName": "alt", + "chainId": 2330, + "networkId": 2330, + "icon": "altcoinchain", + "status": "active", + "explorers": [ + { + "name": "expedition", + "url": "http://expedition.altcoinchain.org", + "icon": "altcoinchain", + "standard": "none" + } + ] + }, + { + "name": "WEMIX Kanvas Sepolia", + "title": "WEMIX Kanvas Testnet Sepolia", + "chainId": 2357, + "shortName": "kanvas-aqua", + "chain": "ETH", + "networkId": 2357, + "nativeCurrency": { "name": "Sepolia Ether", "symbol": "ETH", "decimals": 18 }, + "rpc": ["https://api.aqua.wemixkanvas.io"], + "faucets": [], + "infoURL": "https://wemixkanvas.io", + "icon": "wemixkanvas", + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout.aqua.wemixkanvas.io", + "icon": "wemixkanvas", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-11155111", + "bridges": [{ "url": "https://wemixkanvas.io/bridge" }] + } + }, + { + "name": "BOMB Chain Testnet", + "chain": "BOMB", + "rpc": ["https://bombchain-testnet.ankr.com/bas_full_rpc_1"], + "faucets": ["https://faucet.bombchain-testnet.ankr.com/"], + "nativeCurrency": { "name": "BOMB Token", "symbol": "tBOMB", "decimals": 18 }, + "infoURL": "https://www.bombmoney.com", + "shortName": "bombt", + "chainId": 2399, + "networkId": 2399, + "icon": "bomb", + "explorers": [ + { + "name": "bombscan-testnet", + "icon": "bomb", + "url": "https://explorer.bombchain-testnet.ankr.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "TCG Verse Mainnet", + "chain": "TCG Verse", + "icon": "tcg_verse", + "rpc": ["https://rpc.tcgverse.xyz"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://tcgverse.xyz/", + "shortName": "TCGV", + "chainId": 2400, + "networkId": 2400, + "explorers": [ + { + "name": "TCG Verse Explorer", + "url": "https://explorer.tcgverse.xyz", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-248" } + }, + { + "name": "XODEX", + "chain": "XODEX", + "rpc": ["https://mainnet.xo-dex.com/rpc", "https://xo-dex.io"], + "faucets": [], + "nativeCurrency": { "name": "XODEX Native Token", "symbol": "XODEX", "decimals": 18 }, + "infoURL": "https://xo-dex.com", + "shortName": "xodex", + "chainId": 2415, + "networkId": 10, + "icon": "xodex", + "explorers": [ + { + "name": "XODEX Explorer", + "url": "https://explorer.xo-dex.com", + "standard": "EIP3091", + "icon": "xodex" + } + ] + }, + { + "name": "Kortho Mainnet", + "chain": "Kortho Chain", + "rpc": ["https://www.kortho-chain.com"], + "faucets": [], + "nativeCurrency": { "name": "KorthoChain", "symbol": "KTO", "decimals": 11 }, + "infoURL": "https://www.kortho.io/", + "shortName": "ktoc", + "chainId": 2559, + "networkId": 2559 + }, + { + "name": "TechPay Mainnet", + "chain": "TPC", + "rpc": ["https://api.techpay.io/"], + "faucets": [], + "nativeCurrency": { "name": "TechPay", "symbol": "TPC", "decimals": 18 }, + "infoURL": "https://techpay.io/", + "shortName": "tpc", + "chainId": 2569, + "networkId": 2569, + "icon": "techpay", + "explorers": [ + { "name": "tpcscan", "url": "https://tpcscan.com", "icon": "techpay", "standard": "EIP3091" } + ] + }, + { + "name": "PoCRNet", + "title": "Proof of Carbon Reduction mainnet", + "chain": "CRC", + "status": "active", + "rpc": [ + "https://pocrnet.westeurope.cloudapp.azure.com/http", + "wss://pocrnet.westeurope.cloudapp.azure.com/ws" + ], + "faucets": [], + "nativeCurrency": { "name": "Carbon Reduction Coin", "symbol": "CRC", "decimals": 18 }, + "infoURL": "https://github.com/ethereum-pocr/pocrnet", + "shortName": "pocrnet", + "chainId": 2606, + "networkId": 2606, + "explorers": [ + { + "name": "Lite Explorer", + "url": "https://ethereum-pocr.github.io/explorer/pocrnet", + "standard": "EIP3091" + } + ] + }, + { + "name": "Redlight Chain Mainnet", + "chain": "REDLC", + "rpc": ["https://dataseed2.redlightscan.finance"], + "faucets": [], + "nativeCurrency": { "name": "Redlight Coin", "symbol": "REDLC", "decimals": 18 }, + "infoURL": "https://redlight.finance/", + "shortName": "REDLC", + "chainId": 2611, + "networkId": 2611, + "explorers": [ + { "name": "REDLC Explorer", "url": "https://redlightscan.finance", "standard": "EIP3091" } + ] + }, + { + "name": "EZChain C-Chain Mainnet", + "chain": "EZC", + "rpc": ["https://api.ezchain.com/ext/bc/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "EZChain", "symbol": "EZC", "decimals": 18 }, + "infoURL": "https://ezchain.com", + "shortName": "EZChain", + "chainId": 2612, + "networkId": 2612, + "icon": "ezchain", + "explorers": [ + { "name": "ezchain", "url": "https://cchain-explorer.ezchain.com", "standard": "EIP3091" } + ] + }, + { + "name": "EZChain C-Chain Testnet", + "chain": "EZC", + "rpc": ["https://testnet-api.ezchain.com/ext/bc/C/rpc"], + "faucets": ["https://testnet-faucet.ezchain.com"], + "nativeCurrency": { "name": "EZChain", "symbol": "EZC", "decimals": 18 }, + "infoURL": "https://ezchain.com", + "shortName": "Fuji-EZChain", + "chainId": 2613, + "networkId": 2613, + "icon": "ezchain", + "explorers": [ + { + "name": "ezchain", + "url": "https://testnet-cchain-explorer.ezchain.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Boba Network Goerli Testnet", + "chain": "ETH", + "rpc": ["https://goerli.boba.network/"], + "faucets": [], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "Bobagoerli", + "chainId": 2888, + "networkId": 2888, + "explorers": [ + { "name": "Blockscout", "url": "https://testnet.bobascan.com", "standard": "none" } + ], + "parent": { + "type": "L2", + "chain": "eip155-5", + "bridges": [{ "url": "https://gateway.goerli.boba.network" }] + } + }, + { + "name": "BitYuan Mainnet", + "chain": "BTY", + "rpc": ["https://mainnet.bityuan.com/eth"], + "faucets": [], + "nativeCurrency": { "name": "BTY", "symbol": "BTY", "decimals": 18 }, + "infoURL": "https://www.bityuan.com", + "shortName": "bty", + "chainId": 2999, + "networkId": 2999, + "icon": "bty", + "explorers": [ + { + "name": "BitYuan Block Chain Explorer", + "url": "https://mainnet.bityuan.com", + "standard": "none" + } + ] + }, + { + "name": "CENNZnet Rata", + "chain": "CENNZnet", + "rpc": [], + "faucets": ["https://app-faucet.centrality.me"], + "nativeCurrency": { "name": "CPAY", "symbol": "CPAY", "decimals": 18 }, + "infoURL": "https://cennz.net", + "shortName": "cennz-r", + "chainId": 3000, + "networkId": 3000, + "icon": "cennz" + }, + { + "name": "CENNZnet Nikau", + "chain": "CENNZnet", + "rpc": ["https://nikau.centrality.me/public"], + "faucets": ["https://app-faucet.centrality.me"], + "nativeCurrency": { "name": "CPAY", "symbol": "CPAY", "decimals": 18 }, + "infoURL": "https://cennz.net", + "shortName": "cennz-n", + "chainId": 3001, + "networkId": 3001, + "icon": "cennz", + "explorers": [ + { + "name": "UNcover", + "url": "https://www.uncoverexplorer.com/?network=Nikau", + "standard": "none" + } + ] + }, + { + "name": "Orlando Chain", + "chain": "ORL", + "rpc": ["https://rpc-testnet.orlchain.com"], + "faucets": [], + "nativeCurrency": { "name": "Orlando", "symbol": "ORL", "decimals": 18 }, + "infoURL": "https://orlchain.com", + "shortName": "ORL", + "chainId": 3031, + "networkId": 3031, + "icon": "orl", + "explorers": [ + { + "name": "Orlando (ORL) Explorer", + "url": "https://orlscan.com", + "icon": "orl", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bifrost Mainnet", + "title": "The Bifrost Mainnet network", + "chain": "BFC", + "rpc": [ + "https://public-01.mainnet.thebifrost.io/rpc", + "https://public-02.mainnet.thebifrost.io/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "Bifrost", "symbol": "BFC", "decimals": 18 }, + "infoURL": "https://thebifrost.io", + "shortName": "bfc", + "chainId": 3068, + "networkId": 3068, + "icon": "bifrost", + "explorers": [ + { + "name": "explorer-thebifrost", + "url": "https://explorer.mainnet.thebifrost.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Filecoin - Hyperspace testnet", + "chain": "FIL", + "icon": "filecoin", + "rpc": [ + "https://api.hyperspace.node.glif.io/rpc/v1", + "https://rpc.ankr.com/filecoin_testnet", + "https://filecoin-hyperspace.chainstacklabs.com/rpc/v1" + ], + "faucets": ["https://hyperspace.yoga/#faucet"], + "nativeCurrency": { "name": "testnet filecoin", "symbol": "tFIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin-hyperspace", + "chainId": 3141, + "networkId": 3141, + "slip44": 1, + "explorers": [ + { + "name": "Filfox - Hyperspace", + "url": "https://hyperspace.filfox.info/en", + "standard": "none" + }, + { + "name": "Glif Explorer - Hyperspace", + "url": "https://explorer.glif.io/?network=hyperspace", + "standard": "none" + }, + { "name": "Beryx", "url": "https://beryx.zondax.ch", "standard": "none" }, + { "name": "Dev.storage", "url": "https://dev.storage", "standard": "none" }, + { "name": "Filscan - Hyperspace", "url": "https://hyperspace.filscan.io", "standard": "none" } + ] + }, + { + "name": "Debounce Subnet Testnet", + "chain": "Debounce Network", + "icon": "debounce", + "rpc": ["https://dev-rpc.debounce.network"], + "faucets": [], + "nativeCurrency": { "name": "Debounce Network", "symbol": "DB", "decimals": 18 }, + "infoURL": "https://debounce.network", + "shortName": "debounce-devnet", + "chainId": 3306, + "networkId": 3306, + "explorers": [ + { + "name": "Debounce Devnet Explorer", + "url": "https://explorer.debounce.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "ZCore Testnet", + "chain": "Beach", + "icon": "zcore", + "rpc": ["https://rpc-testnet.zcore.cash"], + "faucets": ["https://faucet.zcore.cash"], + "nativeCurrency": { "name": "ZCore", "symbol": "ZCR", "decimals": 18 }, + "infoURL": "https://zcore.cash", + "shortName": "zcrbeach", + "chainId": 3331, + "networkId": 3331 + }, + { + "name": "Web3Q Testnet", + "chain": "Web3Q", + "rpc": ["https://testnet.web3q.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "Web3Q", "symbol": "W3Q", "decimals": 18 }, + "infoURL": "https://testnet.web3q.io/home.w3q/", + "shortName": "w3q-t", + "chainId": 3333, + "networkId": 3333, + "explorers": [ + { "name": "w3q-testnet", "url": "https://explorer.testnet.web3q.io", "standard": "EIP3091" } + ] + }, + { + "name": "Web3Q Galileo", + "chain": "Web3Q", + "rpc": ["https://galileo.web3q.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "Web3Q", "symbol": "W3Q", "decimals": 18 }, + "infoURL": "https://galileo.web3q.io/home.w3q/", + "shortName": "w3q-g", + "chainId": 3334, + "networkId": 3334, + "explorers": [ + { "name": "w3q-galileo", "url": "https://explorer.galileo.web3q.io", "standard": "EIP3091" } + ] + }, + { + "name": "Paribu Net Mainnet", + "chain": "PRB", + "rpc": ["https://rpc.paribu.network"], + "faucets": [], + "nativeCurrency": { "name": "PRB", "symbol": "PRB", "decimals": 18 }, + "infoURL": "https://net.paribu.com", + "shortName": "prb", + "chainId": 3400, + "networkId": 3400, + "icon": "prb", + "explorers": [ + { + "name": "Paribu Net Explorer", + "url": "https://explorer.paribu.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Paribu Net Testnet", + "chain": "PRB", + "rpc": ["https://rpc.testnet.paribuscan.com"], + "faucets": ["https://faucet.paribuscan.com"], + "nativeCurrency": { "name": "PRB", "symbol": "PRB", "decimals": 18 }, + "infoURL": "https://net.paribu.com", + "shortName": "prbtestnet", + "chainId": 3500, + "networkId": 3500, + "icon": "prb", + "explorers": [ + { + "name": "Paribu Net Testnet Explorer", + "url": "https://testnet.paribuscan.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "JFIN Chain", + "chain": "JFIN", + "rpc": ["https://rpc.jfinchain.com"], + "faucets": [], + "nativeCurrency": { "name": "JFIN Coin", "symbol": "jfin", "decimals": 18 }, + "infoURL": "https://jfinchain.com", + "shortName": "jfin", + "chainId": 3501, + "networkId": 3501, + "explorers": [ + { "name": "JFIN Chain Explorer", "url": "https://exp.jfinchain.com", "standard": "EIP3091" } + ] + }, + { + "name": "PandoProject Mainnet", + "chain": "PandoProject", + "icon": "pando", + "rpc": ["https://eth-rpc-api.pandoproject.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "pando-token", "symbol": "PTX", "decimals": 18 }, + "infoURL": "https://www.pandoproject.org/", + "shortName": "pando-mainnet", + "chainId": 3601, + "networkId": 3601, + "explorers": [ + { + "name": "Pando Mainnet Explorer", + "url": "https://explorer.pandoproject.org", + "standard": "none" + } + ] + }, + { + "name": "PandoProject Testnet", + "chain": "PandoProject", + "icon": "pando", + "rpc": ["https://testnet.ethrpc.pandoproject.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "pando-token", "symbol": "PTX", "decimals": 18 }, + "infoURL": "https://www.pandoproject.org/", + "shortName": "pando-testnet", + "chainId": 3602, + "networkId": 3602, + "explorers": [ + { + "name": "Pando Testnet Explorer", + "url": "https://testnet.explorer.pandoproject.org", + "standard": "none" + } + ] + }, + { + "name": "Metacodechain", + "chain": "metacode", + "rpc": ["https://j.blockcoach.com:8503"], + "faucets": [], + "nativeCurrency": { "name": "J", "symbol": "J", "decimals": 18 }, + "infoURL": "https://j.blockcoach.com:8089", + "shortName": "metacode", + "chainId": 3666, + "networkId": 3666, + "explorers": [{ "name": "meta", "url": "https://j.blockcoach.com:8089", "standard": "EIP3091" }] + }, + { + "name": "Bittex Mainnet", + "chain": "BTX", + "rpc": ["https://rpc1.bittexscan.info", "https://rpc2.bittexscan.info"], + "faucets": [], + "nativeCurrency": { "name": "Bittex", "symbol": "BTX", "decimals": 18 }, + "infoURL": "https://bittexscan.com", + "shortName": "btx", + "chainId": 3690, + "networkId": 3690, + "explorers": [{ "name": "bittexscan", "url": "https://bittexscan.com", "standard": "EIP3091" }] + }, + { + "name": "Empire Network", + "chain": "EMPIRE", + "rpc": ["https://rpc.empirenetwork.io"], + "faucets": [], + "nativeCurrency": { "name": "Empire", "symbol": "EMPIRE", "decimals": 18 }, + "infoURL": "https://www.empirenetwork.io/", + "shortName": "empire", + "chainId": 3693, + "networkId": 3693, + "explorers": [ + { "name": "Empire Explorer", "url": "https://explorer.empirenetwork.io", "standard": "none" } + ] + }, + { + "name": "Crossbell", + "chain": "Crossbell", + "rpc": ["https://rpc.crossbell.io"], + "faucets": ["https://faucet.crossbell.io"], + "nativeCurrency": { "name": "Crossbell Token", "symbol": "CSB", "decimals": 18 }, + "infoURL": "https://crossbell.io", + "shortName": "csb", + "chainId": 3737, + "networkId": 3737, + "icon": "crossbell", + "explorers": [ + { "name": "Crossbell Explorer", "url": "https://scan.crossbell.io", "standard": "EIP3091" } + ] + }, + { + "name": "AlveyChain Mainnet", + "chain": "ALV", + "icon": "alveychain", + "rpc": ["https://rpc.alveychain.com/rpc", "https://rpc2.alvey.io/rpc"], + "faucets": [], + "nativeCurrency": { "name": "AlveyCoin", "symbol": "ALV", "decimals": 18 }, + "infoURL": "https://alveyscan.com/rpc", + "shortName": "alv", + "chainId": 3797, + "networkId": 3797, + "explorers": [{ "name": "AlveyScan", "url": "https://alveyscan.com", "standard": "EIP3091" }] + }, + { + "name": "DRAC Network", + "chain": "DRAC", + "rpc": ["https://www.dracscan.com/rpc"], + "faucets": ["https://www.dracscan.io/faucet"], + "nativeCurrency": { "name": "DRAC", "symbol": "DRAC", "decimals": 18 }, + "infoURL": "https://drac.io/", + "shortName": "drac", + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "chainId": 3912, + "networkId": 3912, + "icon": "drac", + "explorers": [ + { "name": "DRAC_Network Scan", "url": "https://www.dracscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "DOS Tesnet", + "chain": "DOS", + "rpc": ["https://test.doschain.com"], + "faucets": [], + "nativeCurrency": { "name": "DOS", "symbol": "DOS", "decimals": 18 }, + "infoURL": "http://doschain.io/", + "shortName": "dost", + "chainId": 3939, + "networkId": 3939, + "icon": "doschain", + "explorers": [ + { + "name": "DOScan-Test", + "url": "https://test.doscan.io", + "icon": "doschain", + "standard": "EIP3091" + } + ] + }, + { + "name": "DYNO Mainnet", + "chain": "DYNO", + "rpc": ["https://api.dynoprotocol.com"], + "faucets": ["https://faucet.dynoscan.io"], + "nativeCurrency": { "name": "DYNO Token", "symbol": "DYNO", "decimals": 18 }, + "infoURL": "https://dynoprotocol.com", + "shortName": "dyno", + "chainId": 3966, + "networkId": 3966, + "explorers": [{ "name": "DYNO Explorer", "url": "https://dynoscan.io", "standard": "EIP3091" }] + }, + { + "name": "DYNO Testnet", + "chain": "DYNO", + "rpc": ["https://tapi.dynoprotocol.com"], + "faucets": ["https://faucet.dynoscan.io"], + "nativeCurrency": { "name": "DYNO Token", "symbol": "tDYNO", "decimals": 18 }, + "infoURL": "https://dynoprotocol.com", + "shortName": "tdyno", + "chainId": 3967, + "networkId": 3967, + "explorers": [ + { "name": "DYNO Explorer", "url": "https://testnet.dynoscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "YuanChain Mainnet", + "chain": "YCC", + "rpc": ["https://mainnet.yuan.org/eth"], + "faucets": [], + "nativeCurrency": { "name": "YCC", "symbol": "YCC", "decimals": 18 }, + "infoURL": "https://www.yuan.org", + "shortName": "ycc", + "chainId": 3999, + "networkId": 3999, + "icon": "ycc", + "explorers": [ + { "name": "YuanChain Explorer", "url": "https://mainnet.yuan.org", "standard": "none" } + ] + }, + { + "name": "Fantom Testnet", + "chain": "FTM", + "rpc": ["https://rpc.testnet.fantom.network"], + "faucets": ["https://faucet.fantom.network"], + "nativeCurrency": { "name": "Fantom", "symbol": "FTM", "decimals": 18 }, + "infoURL": "https://docs.fantom.foundation/quick-start/short-guide#fantom-testnet", + "shortName": "tftm", + "chainId": 4002, + "networkId": 4002, + "icon": "fantom", + "explorers": [ + { + "name": "ftmscan", + "url": "https://testnet.ftmscan.com", + "icon": "ftmscan", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bobaopera Testnet", + "chain": "Bobaopera Testnet", + "rpc": [ + "https://testnet.bobaopera.boba.network", + "wss://wss.testnet.bobaopera.boba.network", + "https://replica.testnet.bobaopera.boba.network", + "wss://replica-wss.testnet.bobaopera.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaoperaTestnet", + "chainId": 4051, + "networkId": 4051, + "explorers": [ + { + "name": "Bobaopera Testnet block explorer", + "url": "https://blockexplorer.testnet.bobaopera.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Nahmii 3 Mainnet", + "chain": "Nahmii", + "rpc": [], + "status": "incubating", + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://nahmii.io", + "shortName": "Nahmii3Mainnet", + "chainId": 4061, + "networkId": 4061, + "icon": "nahmii", + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.nahmii.io" }] + } + }, + { + "name": "Nahmii 3 Testnet", + "chain": "Nahmii", + "rpc": ["https://ngeth.testnet.n3.nahmii.io"], + "faucets": [], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://nahmii.io", + "shortName": "Nahmii3Testnet", + "chainId": 4062, + "networkId": 4062, + "icon": "nahmii", + "explorers": [ + { + "name": "Nahmii 3 Testnet Explorer", + "url": "https://explorer.testnet.n3.nahmii.io", + "icon": "nahmii", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-3", + "bridges": [{ "url": "https://bridge.testnet.n3.nahmii.io" }] + } + }, + { + "name": "Bitindi Testnet", + "chain": "BNI", + "icon": "bitindiTestnet", + "rpc": ["https://testnet-rpc.bitindi.org"], + "faucets": ["https://faucet.bitindi.org"], + "nativeCurrency": { "name": "BNI", "symbol": "$BNI", "decimals": 18 }, + "infoURL": "https://bitindi.org", + "shortName": "BNIt", + "chainId": 4096, + "networkId": 4096, + "explorers": [ + { "name": "Bitindi", "url": "https://testnet.bitindiscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Bitindi Mainnet", + "chain": "BNI", + "icon": "bitindi", + "rpc": ["https://mainnet-rpc.bitindi.org"], + "faucets": ["https://faucet.bitindi.org"], + "nativeCurrency": { "name": "BNI", "symbol": "$BNI", "decimals": 18 }, + "infoURL": "https://bitindi.org", + "shortName": "BNIm", + "chainId": 4099, + "networkId": 4099, + "explorers": [{ "name": "Bitindi", "url": "https://bitindiscan.com", "standard": "EIP3091" }] + }, + { + "name": "AIOZ Network Testnet", + "chain": "AIOZ", + "icon": "aioz", + "rpc": ["https://eth-ds.testnet.aioz.network"], + "faucets": [], + "nativeCurrency": { "name": "testAIOZ", "symbol": "AIOZ", "decimals": 18 }, + "infoURL": "https://aioz.network", + "shortName": "aioz-testnet", + "chainId": 4102, + "networkId": 4102, + "slip44": 60, + "explorers": [ + { + "name": "AIOZ Network Testnet Explorer", + "url": "https://testnet.explorer.aioz.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Tipboxcoin Testnet", + "chain": "TPBX", + "icon": "tipboxcoinIcon", + "rpc": ["https://testnet-rpc.tipboxcoin.net"], + "faucets": ["https://faucet.tipboxcoin.net"], + "nativeCurrency": { "name": "Tipboxcoin", "symbol": "TPBX", "decimals": 18 }, + "infoURL": "https://tipboxcoin.net", + "shortName": "TPBXt", + "chainId": 4141, + "networkId": 4141, + "explorers": [ + { "name": "Tipboxcoin", "url": "https://testnet.tipboxcoin.net", "standard": "EIP3091" } + ] + }, + { + "name": "PHI Network V1", + "chain": "PHI V1", + "rpc": ["https://rpc1.phi.network", "https://rpc2.phi.network"], + "faucets": [], + "nativeCurrency": { "name": "PHI", "symbol": "Φ", "decimals": 18 }, + "infoURL": "https://phi.network", + "shortName": "PHIv1", + "chainId": 4181, + "networkId": 4181, + "icon": "phi", + "explorers": [ + { + "name": "PHI Explorer", + "url": "https://explorer.phi.network", + "icon": "phi", + "standard": "none" + } + ] + }, + { + "name": "Nexi Mainnet", + "chain": "Nexi", + "icon": "nexi", + "rpc": [ + "https://rpc.chain.nexi.technology/", + "https://chain.nexilix.com", + "https://chain.nexi.evmnode.online" + ], + "faucets": [], + "nativeCurrency": { "name": "Nexi", "symbol": "NEXI", "decimals": 18 }, + "infoURL": "https://www.nexi.technology/", + "shortName": "nexi", + "chainId": 4242, + "networkId": 4242, + "slip44": 2500, + "explorers": [{ "name": "nexiscan", "url": "https://www.nexiscan.com", "standard": "EIP3091" }] + }, + { + "name": "Bobafuji Testnet", + "chain": "Bobafuji Testnet", + "rpc": [ + "https://testnet.avax.boba.network", + "wss://wss.testnet.avax.boba.network", + "https://replica.testnet.avax.boba.network", + "wss://replica-wss.testnet.avax.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaFujiTestnet", + "chainId": 4328, + "networkId": 4328, + "explorers": [ + { + "name": "Bobafuji Testnet block explorer", + "url": "https://blockexplorer.testnet.avax.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Htmlcoin Mainnet", + "chain": "mainnet", + "rpc": ["https://janus.htmlcoin.com/api/"], + "faucets": ["https://gruvin.me/htmlcoin"], + "nativeCurrency": { "name": "Htmlcoin", "symbol": "HTML", "decimals": 8 }, + "infoURL": "https://htmlcoin.com", + "shortName": "html", + "chainId": 4444, + "networkId": 4444, + "icon": "htmlcoin", + "status": "active", + "explorers": [ + { + "name": "htmlcoin", + "url": "https://explorer.htmlcoin.com", + "icon": "htmlcoin", + "standard": "none" + } + ] + }, + { + "name": "IoTeX Network Mainnet", + "chain": "iotex.io", + "rpc": ["https://babel-api.mainnet.iotex.io"], + "faucets": [], + "nativeCurrency": { "name": "IoTeX", "symbol": "IOTX", "decimals": 18 }, + "infoURL": "https://iotex.io", + "shortName": "iotex-mainnet", + "chainId": 4689, + "networkId": 4689, + "explorers": [{ "name": "iotexscan", "url": "https://iotexscan.io", "standard": "EIP3091" }] + }, + { + "name": "IoTeX Network Testnet", + "chain": "iotex.io", + "rpc": ["https://babel-api.testnet.iotex.io"], + "faucets": ["https://faucet.iotex.io/"], + "nativeCurrency": { "name": "IoTeX", "symbol": "IOTX", "decimals": 18 }, + "infoURL": "https://iotex.io", + "shortName": "iotex-testnet", + "chainId": 4690, + "networkId": 4690, + "explorers": [ + { "name": "testnet iotexscan", "url": "https://testnet.iotexscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "BlackFort Exchange Network Testnet", + "chain": "TBXN", + "rpc": ["https://testnet.blackfort.network/rpc"], + "faucets": [], + "nativeCurrency": { "name": "BlackFort Testnet Token", "symbol": "TBXN", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://blackfort.exchange", + "shortName": "TBXN", + "chainId": 4777, + "networkId": 4777, + "icon": "bxn", + "explorers": [ + { + "name": "blockscout", + "url": "https://testnet-explorer.blackfort.network", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "Venidium Testnet", + "chain": "XVM", + "rpc": ["https://rpc-evm-testnet.venidium.io"], + "faucets": [], + "nativeCurrency": { "name": "Venidium", "symbol": "XVM", "decimals": 18 }, + "infoURL": "https://venidium.io", + "shortName": "txvm", + "chainId": 4918, + "networkId": 4918, + "explorers": [ + { + "name": "Venidium EVM Testnet Explorer", + "url": "https://evm-testnet.venidiumexplorer.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Venidium Mainnet", + "chain": "XVM", + "icon": "venidium", + "rpc": ["https://rpc.venidium.io"], + "faucets": [], + "nativeCurrency": { "name": "Venidium", "symbol": "XVM", "decimals": 18 }, + "infoURL": "https://venidium.io", + "shortName": "xvm", + "chainId": 4919, + "networkId": 4919, + "explorers": [ + { + "name": "Venidium Explorer", + "url": "https://evm.venidiumexplorer.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "BlackFort Exchange Network", + "chain": "BXN", + "rpc": [ + "https://mainnet.blackfort.network/rpc", + "https://mainnet-1.blackfort.network/rpc", + "https://mainnet-2.blackfort.network/rpc", + "https://mainnet-3.blackfort.network/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "BlackFort Token", "symbol": "BXN", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://blackfort.exchange", + "shortName": "BXN", + "chainId": 4999, + "networkId": 4999, + "icon": "bxn", + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer.blackfort.network", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "Mantle", + "chain": "ETH", + "rpc": ["https://rpc.mantle.xyz"], + "faucets": [], + "nativeCurrency": { "name": "BitDAO", "symbol": "BIT", "decimals": 18 }, + "infoURL": "https://mantle.xyz", + "shortName": "mantle", + "chainId": 5000, + "networkId": 5000, + "explorers": [ + { "name": "Mantle Explorer", "url": "https://explorer.mantle.xyz", "standard": "EIP3091" } + ] + }, + { + "name": "TLChain Network Mainnet", + "chain": "TLC", + "icon": "tlc", + "rpc": ["https://mainnet-rpc.tlxscan.com/"], + "faucets": [], + "nativeCurrency": { "name": "TLChain Network", "symbol": "TLC", "decimals": 18 }, + "infoURL": "https://tlchain.network/", + "shortName": "tlc", + "chainId": 5177, + "networkId": 5177, + "explorers": [ + { "name": "TLChain Explorer", "url": "https://explorer.tlchain.network", "standard": "none" } + ] + }, + { + "name": "EraSwap Mainnet", + "chain": "ESN", + "icon": "eraswap", + "rpc": ["https://mainnet.eraswap.network", "https://rpc-mumbai.mainnet.eraswap.network"], + "faucets": [], + "nativeCurrency": { "name": "EraSwap", "symbol": "ES", "decimals": 18 }, + "infoURL": "https://eraswap.info/", + "shortName": "es", + "chainId": 5197, + "networkId": 5197 + }, + { + "name": "Humanode Mainnet", + "chain": "HMND", + "rpc": ["https://explorer-rpc-http.mainnet.stages.humanode.io"], + "faucets": [], + "nativeCurrency": { "name": "HMND", "symbol": "HMND", "decimals": 18 }, + "infoURL": "https://humanode.io", + "shortName": "hmnd", + "chainId": 5234, + "networkId": 5234, + "explorers": [] + }, + { + "name": "Firechain Mainnet Old", + "chain": "FIRE", + "icon": "firechain", + "rpc": ["https://mainnet.rpc1.thefirechain.com"], + "faucets": [], + "nativeCurrency": { "name": "Firechain", "symbol": "FIRE", "decimals": 18 }, + "infoURL": "https://thefirechain.com", + "shortName": "_old_fire", + "chainId": 5290, + "networkId": 5290, + "explorers": [], + "status": "deprecated" + }, + { + "name": "Uzmi Network Mainnet", + "chain": "UZMI", + "rpc": ["https://network.uzmigames.com.br/"], + "faucets": [], + "nativeCurrency": { "name": "UZMI", "symbol": "UZMI", "decimals": 18 }, + "infoURL": "https://uzmigames.com.br/", + "shortName": "UZMI", + "chainId": 5315, + "networkId": 5315 + }, + { + "name": "Nahmii Mainnet", + "chain": "Nahmii", + "rpc": ["https://l2.nahmii.io"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://nahmii.io", + "shortName": "Nahmii", + "chainId": 5551, + "networkId": 5551, + "icon": "nahmii", + "explorers": [ + { + "name": "Nahmii mainnet explorer", + "url": "https://explorer.nahmii.io", + "icon": "nahmii", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.nahmii.io" }] + } + }, + { + "name": "Nahmii Testnet", + "chain": "Nahmii", + "rpc": ["https://l2.testnet.nahmii.io"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://nahmii.io", + "shortName": "NahmiiTestnet", + "chainId": 5553, + "networkId": 5553, + "icon": "nahmii", + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer.testnet.nahmii.io", + "icon": "nahmii", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-3", + "bridges": [{ "url": "https://bridge.nahmii.io" }] + } + }, + { + "name": "Chain Verse Mainnet", + "chain": "CVERSE", + "icon": "chain_verse", + "rpc": ["https://rpc.chainverse.info"], + "faucets": [], + "nativeCurrency": { "name": "Oasys", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://chainverse.info", + "shortName": "cverse", + "chainId": 5555, + "networkId": 5555, + "explorers": [ + { + "name": "Chain Verse Explorer", + "url": "https://explorer.chainverse.info", + "standard": "EIP3091" + } + ] + }, + { + "name": "Syscoin Tanenbaum Testnet", + "chain": "SYS", + "rpc": ["https://rpc.tanenbaum.io", "wss://rpc.tanenbaum.io/wss"], + "faucets": ["https://faucet.tanenbaum.io"], + "nativeCurrency": { "name": "Testnet Syscoin", "symbol": "tSYS", "decimals": 18 }, + "infoURL": "https://syscoin.org", + "shortName": "tsys", + "chainId": 5700, + "networkId": 5700, + "explorers": [ + { + "name": "Syscoin Testnet Block Explorer", + "url": "https://tanenbaum.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Hika Network Testnet", + "title": "Hika Network Testnet", + "chain": "HIK", + "icon": "hik", + "rpc": ["https://rpc-testnet.hika.network/"], + "faucets": [], + "nativeCurrency": { "name": "Hik Token", "symbol": "HIK", "decimals": 18 }, + "infoURL": "https://hika.network/", + "shortName": "hik", + "chainId": 5729, + "networkId": 5729, + "explorers": [ + { + "name": "Hika Network Testnet Explorer", + "url": "https://scan-testnet.hika.network", + "standard": "none" + } + ] + }, + { + "name": "Ganache", + "title": "Ganache GUI Ethereum Testnet", + "chain": "ETH", + "icon": "ganache", + "rpc": ["https://127.0.0.1:7545"], + "faucets": [], + "nativeCurrency": { "name": "Ganache Test Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://trufflesuite.com/ganache/", + "shortName": "ggui", + "chainId": 5777, + "networkId": 5777, + "explorers": [] + }, + { + "name": "Ontology Testnet", + "chain": "Ontology", + "icon": "ontology", + "rpc": [ + "http://polaris1.ont.io:20339", + "http://polaris2.ont.io:20339", + "http://polaris3.ont.io:20339", + "http://polaris4.ont.io:20339", + "https://polaris1.ont.io:10339", + "https://polaris2.ont.io:10339", + "https://polaris3.ont.io:10339", + "https://polaris4.ont.io:10339" + ], + "faucets": ["https://developer.ont.io/"], + "nativeCurrency": { "name": "ONG", "symbol": "ONG", "decimals": 18 }, + "infoURL": "https://ont.io/", + "shortName": "OntologyTestnet", + "chainId": 5851, + "networkId": 5851, + "explorers": [ + { "name": "explorer", "url": "https://explorer.ont.io/testnet", "standard": "EIP3091" } + ] + }, + { + "name": "Wegochain Rubidium Mainnet", + "chain": "RBD", + "rpc": ["https://proxy.wegochain.io", "http://wallet.wegochain.io:7764"], + "faucets": [], + "nativeCurrency": { "name": "Rubid", "symbol": "RBD", "decimals": 18 }, + "infoURL": "https://www.wegochain.io", + "shortName": "rbd", + "chainId": 5869, + "networkId": 5869, + "explorers": [ + { "name": "wegoscan2", "url": "https://scan2.wegochain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Tres Testnet", + "chain": "TresLeches", + "rpc": ["https://rpc-test.tresleches.finance/"], + "faucets": ["http://faucet.tresleches.finance:8080"], + "nativeCurrency": { "name": "TRES", "symbol": "TRES", "decimals": 18 }, + "infoURL": "https://treschain.com", + "shortName": "TRESTEST", + "chainId": 6065, + "networkId": 6065, + "icon": "tresleches", + "explorers": [ + { + "name": "treslechesexplorer", + "url": "https://explorer-test.tresleches.finance", + "icon": "treslechesexplorer", + "standard": "EIP3091" + } + ] + }, + { + "name": "Tres Mainnet", + "chain": "TresLeches", + "rpc": ["https://rpc.tresleches.finance/", "https://rpc.treschain.io/"], + "faucets": [], + "nativeCurrency": { "name": "TRES", "symbol": "TRES", "decimals": 18 }, + "infoURL": "https://treschain.com", + "shortName": "TRESMAIN", + "chainId": 6066, + "networkId": 6066, + "icon": "tresleches", + "explorers": [ + { + "name": "treslechesexplorer", + "url": "https://explorer.tresleches.finance", + "icon": "treslechesexplorer", + "standard": "EIP3091" + } + ] + }, + { + "name": "Scolcoin WeiChain Testnet", + "chain": "SCOLWEI-testnet", + "rpc": ["https://testnet-rpc.scolcoin.com"], + "faucets": ["https://faucet.scolcoin.com"], + "nativeCurrency": { "name": "Scolcoin", "symbol": "SCOL", "decimals": 18 }, + "infoURL": "https://scolcoin.com", + "shortName": "SRC-test", + "chainId": 6552, + "networkId": 6552, + "icon": "scolcoin", + "explorers": [ + { + "name": "Scolscan Testnet Explorer", + "url": "https://testnet-explorer.scolcoin.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Pixie Chain Mainnet", + "chain": "PixieChain", + "rpc": ["https://http-mainnet.chain.pixie.xyz", "wss://ws-mainnet.chain.pixie.xyz"], + "faucets": [], + "nativeCurrency": { "name": "Pixie Chain Native Token", "symbol": "PIX", "decimals": 18 }, + "infoURL": "https://chain.pixie.xyz", + "shortName": "pixie-chain", + "chainId": 6626, + "networkId": 6626, + "explorers": [ + { "name": "blockscout", "url": "https://scan.chain.pixie.xyz", "standard": "none" } + ] + }, + { + "name": "Gold Smart Chain Mainnet", + "chain": "STAND", + "icon": "stand", + "rpc": ["https://rpc-mainnet.goldsmartchain.com"], + "faucets": ["https://faucet.goldsmartchain.com"], + "nativeCurrency": { "name": "Standard in Gold", "symbol": "STAND", "decimals": 18 }, + "infoURL": "https://goldsmartchain.com", + "shortName": "STANDm", + "chainId": 6789, + "networkId": 6789, + "explorers": [ + { + "name": "Gold Smart Chain", + "url": "https://mainnet.goldsmartchain.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Tomb Chain Mainnet", + "chain": "Tomb Chain", + "rpc": ["https://rpc.tombchain.com/"], + "faucets": [], + "nativeCurrency": { "name": "Tomb", "symbol": "TOMB", "decimals": 18 }, + "infoURL": "https://tombchain.com/", + "shortName": "tombchain", + "chainId": 6969, + "networkId": 6969, + "explorers": [{ "name": "tombscout", "url": "https://tombscout.com", "standard": "none" }], + "parent": { + "type": "L2", + "chain": "eip155-250", + "bridges": [{ "url": "https://lif3.com/bridge" }] + } + }, + { + "name": "PolySmartChain", + "chain": "PSC", + "rpc": [ + "https://seed0.polysmartchain.com/", + "https://seed1.polysmartchain.com/", + "https://seed2.polysmartchain.com/" + ], + "faucets": [], + "nativeCurrency": { "name": "PSC", "symbol": "PSC", "decimals": 18 }, + "infoURL": "https://www.polysmartchain.com/", + "shortName": "psc", + "chainId": 6999, + "networkId": 6999 + }, + { + "name": "ZetaChain Mainnet", + "chain": "ZetaChain", + "icon": "zetachain", + "rpc": ["https://api.mainnet.zetachain.com/evm"], + "faucets": [], + "nativeCurrency": { "name": "Zeta", "symbol": "ZETA", "decimals": 18 }, + "infoURL": "https://docs.zetachain.com/", + "shortName": "zetachain-mainnet", + "chainId": 7000, + "networkId": 7000, + "status": "incubating", + "explorers": [ + { + "name": "ZetaChain Mainnet Explorer", + "url": "https://explorer.mainnet.zetachain.com", + "standard": "none" + } + ] + }, + { + "name": "ZetaChain Athens Testnet", + "chain": "ZetaChain", + "icon": "zetachain", + "rpc": ["https://api.athens2.zetachain.com/evm"], + "faucets": ["https://labs.zetachain.com/get-zeta"], + "nativeCurrency": { "name": "Zeta", "symbol": "aZETA", "decimals": 18 }, + "infoURL": "https://docs.zetachain.com/", + "shortName": "zetachain-athens", + "chainId": 7001, + "networkId": 7001, + "status": "active", + "explorers": [ + { + "name": "ZetaChain Athens Testnet Explorer", + "url": "https://explorer.athens.zetachain.com", + "standard": "none" + } + ] + }, + { + "name": "Ella the heart", + "chain": "ella", + "icon": "ella", + "rpc": ["https://rpc.ella.network"], + "faucets": [], + "nativeCurrency": { "name": "Ella", "symbol": "ELLA", "decimals": 18 }, + "infoURL": "https://ella.network", + "shortName": "ELLA", + "chainId": 7027, + "networkId": 7027, + "explorers": [{ "name": "Ella", "url": "https://ella.network", "standard": "EIP3091" }] + }, + { + "name": "Planq Mainnet", + "chain": "Planq", + "icon": "planq", + "rpc": ["https://evm-rpc.planq.network"], + "faucets": [], + "nativeCurrency": { "name": "Planq", "symbol": "PLQ", "decimals": 18 }, + "infoURL": "https://planq.network", + "shortName": "planq", + "chainId": 7070, + "networkId": 7070, + "explorers": [ + { + "name": "Planq EVM Explorer (Blockscout)", + "url": "https://evm.planq.network", + "standard": "none" + }, + { + "name": "Planq Cosmos Explorer (BigDipper)", + "url": "https://explorer.planq.network", + "standard": "none" + } + ] + }, + { + "name": "KLYNTAR", + "chain": "KLY", + "rpc": ["https://evm.klyntar.org/kly_evm_rpc", "https://evm.klyntarscan.org/kly_evm_rpc"], + "faucets": [], + "nativeCurrency": { "name": "KLYNTAR", "symbol": "KLY", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://klyntar.org", + "shortName": "kly", + "chainId": 7331, + "networkId": 7331, + "icon": "klyntar", + "explorers": [], + "status": "incubating" + }, + { + "name": "Shyft Mainnet", + "chain": "SHYFT", + "icon": "shyft", + "rpc": ["https://rpc.shyft.network/"], + "faucets": [], + "nativeCurrency": { "name": "Shyft", "symbol": "SHYFT", "decimals": 18 }, + "infoURL": "https://shyft.network", + "shortName": "shyft", + "chainId": 7341, + "networkId": 7341, + "slip44": 2147490989, + "explorers": [{ "name": "Shyft BX", "url": "https://bx.shyft.network", "standard": "EIP3091" }] + }, + { + "name": "ADIL Testnet", + "chain": "ADIL", + "icon": "adil", + "rpc": ["https://testnet.adilchain-rpc.io"], + "faucets": ["https://testnet-faucet.adil-scan.io"], + "nativeCurrency": { "name": "Testnet ADIL", "symbol": "ADIL", "decimals": 18 }, + "infoURL": "https://adilchain.io", + "shortName": "tadil", + "chainId": 7575, + "networkId": 7575, + "explorers": [ + { + "name": "ADIL Testnet Explorer", + "url": "https://testnet.adilchain-scan.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "The Root Network - Mainnet", + "chain": "TRN", + "rpc": ["https://root.rootnet.live/archive", "wss://root.rootnet.live/archive/ws"], + "faucets": [], + "nativeCurrency": { "name": "XRP", "symbol": "XRP", "decimals": 6 }, + "infoURL": "https://www.futureverse.com/technology/root", + "shortName": "trn-mainnet", + "chainId": 7668, + "networkId": 7668, + "explorers": [{ "name": "rootnet", "url": "https://explorer.rootnet.live", "standard": "none" }] + }, + { + "name": "The Root Network - Porcini Testnet", + "chain": "TRN", + "rpc": ["https://porcini.rootnet.app/archive", "wss://porcini.rootnet.app/archive/ws"], + "faucets": [], + "nativeCurrency": { "name": "XRP", "symbol": "XRP", "decimals": 6 }, + "infoURL": "https://www.futureverse.com/technology/root", + "shortName": "trn-porcini", + "chainId": 7672, + "networkId": 7672, + "explorers": [ + { "name": "rootnet", "url": "https://explorer.rootnet.cloud", "standard": "none" } + ] + }, + { + "name": "Canto", + "chain": "Canto", + "rpc": [ + "https://canto.slingshot.finance", + "https://canto.neobase.one", + "https://mainnode.plexnode.org:8545" + ], + "faucets": [], + "nativeCurrency": { "name": "Canto", "symbol": "CANTO", "decimals": 18 }, + "infoURL": "https://canto.io", + "shortName": "canto", + "chainId": 7700, + "networkId": 7700, + "explorers": [ + { + "name": "Canto EVM Explorer (Blockscout)", + "url": "https://evm.explorer.canto.io", + "standard": "none" + }, + { + "name": "Canto Cosmos Explorer", + "url": "https://cosmos-explorers.neobase.one", + "standard": "none" + }, + { + "name": "Canto EVM Explorer (Blockscout)", + "url": "https://tuber.build", + "standard": "none" + } + ] + }, + { + "name": "Canto Tesnet", + "chain": "Canto", + "rpc": ["https://testnet-archive.plexnode.wtf"], + "faucets": [], + "nativeCurrency": { "name": "Testnet Canto", "symbol": "CANTO", "decimals": 18 }, + "infoURL": "https://canto.io", + "shortName": "TestnetCanto", + "chainId": 7701, + "networkId": 7701, + "explorers": [ + { + "name": "Canto Testnet EVM Explorer (Blockscout)", + "url": "https://testnet.tuber.build", + "standard": "none" + } + ] + }, + { + "name": "Rise of the Warbots Testnet", + "chain": "nmactest", + "rpc": [ + "https://testnet1.riseofthewarbots.com", + "https://testnet2.riseofthewarbots.com", + "https://testnet3.riseofthewarbots.com", + "https://testnet4.riseofthewarbots.com", + "https://testnet5.riseofthewarbots.com" + ], + "faucets": [], + "nativeCurrency": { "name": "Nano Machines", "symbol": "NMAC", "decimals": 18 }, + "infoURL": "https://riseofthewarbots.com/", + "shortName": "RiseOfTheWarbotsTestnet", + "chainId": 7777, + "networkId": 7777, + "explorers": [ + { + "name": "avascan", + "url": "https://testnet.avascan.info/blockchain/2mZ9doojfwHzXN3VXDQELKnKyZYxv7833U8Yq5eTfFx3hxJtiy", + "standard": "none" + } + ] + }, + { + "name": "Hazlor Testnet", + "chain": "SCAS", + "rpc": ["https://hatlas.rpc.hazlor.com:8545", "wss://hatlas.rpc.hazlor.com:8546"], + "faucets": ["https://faucet.hazlor.com"], + "nativeCurrency": { "name": "Hazlor Test Coin", "symbol": "TSCAS", "decimals": 18 }, + "infoURL": "https://hazlor.com", + "shortName": "tscas", + "chainId": 7878, + "networkId": 7878, + "explorers": [ + { + "name": "Hazlor Testnet Explorer", + "url": "https://explorer.hazlor.com", + "standard": "none" + } + ] + }, + { + "name": "DOS Chain", + "chain": "DOS", + "rpc": ["https://main.doschain.com"], + "faucets": [], + "nativeCurrency": { "name": "DOS", "symbol": "DOS", "decimals": 18 }, + "infoURL": "https://doschain.io", + "shortName": "dos", + "chainId": 7979, + "networkId": 7979, + "icon": "doschain", + "explorers": [ + { "name": "DOScan", "url": "https://doscan.io", "icon": "doschain", "standard": "EIP3091" } + ] + }, + { + "name": "Teleport", + "chain": "Teleport", + "rpc": ["https://evm-rpc.teleport.network"], + "faucets": [], + "nativeCurrency": { "name": "Tele", "symbol": "TELE", "decimals": 18 }, + "infoURL": "https://teleport.network", + "shortName": "teleport", + "chainId": 8000, + "networkId": 8000, + "icon": "teleport", + "explorers": [ + { + "name": "Teleport EVM Explorer (Blockscout)", + "url": "https://evm-explorer.teleport.network", + "standard": "none", + "icon": "teleport" + }, + { + "name": "Teleport Cosmos Explorer (Big Dipper)", + "url": "https://explorer.teleport.network", + "standard": "none", + "icon": "teleport" + } + ] + }, + { + "name": "Teleport Testnet", + "chain": "Teleport", + "rpc": ["https://evm-rpc.testnet.teleport.network"], + "faucets": ["https://chain-docs.teleport.network/testnet/faucet.html"], + "nativeCurrency": { "name": "Tele", "symbol": "TELE", "decimals": 18 }, + "infoURL": "https://teleport.network", + "shortName": "teleport-testnet", + "chainId": 8001, + "networkId": 8001, + "icon": "teleport", + "explorers": [ + { + "name": "Teleport EVM Explorer (Blockscout)", + "url": "https://evm-explorer.testnet.teleport.network", + "standard": "none", + "icon": "teleport" + }, + { + "name": "Teleport Cosmos Explorer (Big Dipper)", + "url": "https://explorer.testnet.teleport.network", + "standard": "none", + "icon": "teleport" + } + ] + }, + { + "name": "MDGL Testnet", + "chain": "MDGL", + "rpc": ["https://testnet.mdgl.io"], + "faucets": [], + "nativeCurrency": { "name": "MDGL Token", "symbol": "MDGLT", "decimals": 18 }, + "infoURL": "https://mdgl.io", + "shortName": "mdgl", + "chainId": 8029, + "networkId": 8029 + }, + { + "name": "Shardeum Liberty 1.X", + "chain": "Shardeum", + "icon": "shardeum", + "rpc": ["https://liberty10.shardeum.org/"], + "faucets": ["https://faucet.liberty10.shardeum.org"], + "nativeCurrency": { "name": "Shardeum SHM", "symbol": "SHM", "decimals": 18 }, + "infoURL": "https://docs.shardeum.org/", + "shortName": "Liberty10", + "chainId": 8080, + "networkId": 8080, + "explorers": [ + { + "name": "Shardeum Scan", + "url": "https://explorer-liberty10.shardeum.org", + "standard": "EIP3091" + } + ], + "redFlags": ["reusedChainId"] + }, + { + "name": "Shardeum Liberty 2.X", + "chain": "Shardeum", + "icon": "shardeum", + "rpc": ["https://liberty20.shardeum.org/"], + "faucets": ["https://faucet.liberty20.shardeum.org"], + "nativeCurrency": { "name": "Shardeum SHM", "symbol": "SHM", "decimals": 18 }, + "infoURL": "https://docs.shardeum.org/", + "shortName": "Liberty20", + "chainId": 8081, + "networkId": 8081, + "explorers": [ + { + "name": "Shardeum Scan", + "url": "https://explorer-liberty20.shardeum.org", + "standard": "EIP3091" + } + ], + "redFlags": ["reusedChainId"] + }, + { + "name": "Shardeum Sphinx 1.X", + "chain": "Shardeum", + "icon": "shardeum", + "rpc": ["https://sphinx.shardeum.org/"], + "faucets": ["https://faucet-sphinx.shardeum.org/"], + "nativeCurrency": { "name": "Shardeum SHM", "symbol": "SHM", "decimals": 18 }, + "infoURL": "https://docs.shardeum.org/", + "shortName": "Sphinx10", + "chainId": 8082, + "networkId": 8082, + "explorers": [ + { + "name": "Shardeum Scan", + "url": "https://explorer-sphinx.shardeum.org", + "standard": "EIP3091" + } + ], + "redFlags": ["reusedChainId"] + }, + { + "name": "StreamuX Blockchain", + "chain": "StreamuX", + "rpc": [ + "https://u0ma6t6heb:KDNwOsRDGcyM2Oeui1p431Bteb4rvcWkuPgQNHwB4FM@u0xy4x6x82-u0e2mg517m-rpc.us0-aws.kaleido.io/" + ], + "faucets": [], + "nativeCurrency": { "name": "StreamuX", "symbol": "SmuX", "decimals": 18 }, + "infoURL": "https://www.streamux.cloud", + "shortName": "StreamuX", + "chainId": 8098, + "networkId": 8098 + }, + { + "name": "Qitmeer Network Testnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Qitmeer Testnet", "symbol": "MEER-T", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "meertest", + "chainId": 8131, + "networkId": 8131, + "icon": "meer", + "explorers": [ + { + "name": "meerscan testnet", + "icon": "meer", + "url": "https://testnet.qng.meerscan.io", + "standard": "none" + } + ] + }, + { + "name": "Qitmeer Network Mixnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Qitmeer Mixnet", "symbol": "MEER-M", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "meermix", + "icon": "meer", + "chainId": 8132, + "networkId": 8132, + "status": "incubating" + }, + { + "name": "Qitmeer Network Privnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Qitmeer Privnet", "symbol": "MEER-P", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "meerpriv", + "icon": "meer", + "chainId": 8133, + "networkId": 8133, + "status": "incubating" + }, + { + "name": "Amana", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Amana Mainnet", "symbol": "MEER", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "amana", + "icon": "meer", + "chainId": 8134, + "networkId": 8134, + "status": "incubating" + }, + { + "name": "Flana", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Flana Mainnet", "symbol": "MEER", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "flana", + "icon": "meer", + "chainId": 8135, + "networkId": 8135, + "status": "incubating" + }, + { + "name": "Mizana", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Mizana Mainnet", "symbol": "MEER", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "mizana", + "icon": "meer", + "chainId": 8136, + "networkId": 8136, + "status": "incubating" + }, + { + "name": "BeOne Chain Testnet", + "chain": "BOC", + "rpc": [ + "https://pre-boc1.beonechain.com", + "https://pre-boc2.beonechain.com", + "https://pre-boc3.beonechain.com" + ], + "faucets": ["https://testnet.beonescan.com/faucet"], + "nativeCurrency": { "name": "BeOne Chain Testnet", "symbol": "BOC", "decimals": 18 }, + "infoURL": "https://testnet.beonescan.com", + "shortName": "tBOC", + "chainId": 8181, + "networkId": 8181, + "icon": "beonechain", + "explorers": [ + { + "name": "BeOne Chain Testnet", + "url": "https://testnet.beonescan.com", + "icon": "beonechain", + "standard": "none" + } + ] + }, + { + "name": "Klaytn Mainnet Cypress", + "chain": "KLAY", + "rpc": ["https://public-node-api.klaytnapi.com/v1/cypress"], + "faucets": [], + "nativeCurrency": { "name": "KLAY", "symbol": "KLAY", "decimals": 18 }, + "infoURL": "https://www.klaytn.com/", + "shortName": "Cypress", + "chainId": 8217, + "networkId": 8217, + "slip44": 8217, + "explorers": [{ "name": "Klaytnscope", "url": "https://scope.klaytn.com", "standard": "none" }] + }, + { + "name": "Blockton Blockchain", + "chain": "Blockton Blockchain", + "icon": "bton", + "rpc": ["https://rpc.blocktonscan.com/"], + "faucets": ["https://faucet.blocktonscan.com/"], + "nativeCurrency": { "name": "BLOCKTON", "symbol": "BTON", "decimals": 18 }, + "infoURL": "https://blocktoncoin.com", + "shortName": "BTON", + "chainId": 8272, + "networkId": 8272, + "explorers": [ + { "name": "Blockton Explorer", "url": "https://blocktonscan.com", "standard": "none" } + ] + }, + { + "name": "KorthoTest", + "chain": "Kortho", + "rpc": ["https://www.krotho-test.net"], + "faucets": [], + "nativeCurrency": { "name": "Kortho Test", "symbol": "KTO", "decimals": 11 }, + "infoURL": "https://www.kortho.io/", + "shortName": "Kortho", + "chainId": 8285, + "networkId": 8285 + }, + { + "name": "Dracones Financial Services", + "title": "The Dracones Mainnet", + "chain": "FUCK", + "rpc": ["https://api.dracones.net/"], + "faucets": [], + "nativeCurrency": { + "name": "Functionally Universal Coin Kind", + "symbol": "FUCK", + "decimals": 18 + }, + "infoURL": "https://wolfery.com", + "shortName": "fuck", + "chainId": 8387, + "networkId": 8387, + "icon": "dracones", + "explorers": [] + }, + { + "name": "Base", + "chain": "ETH", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://base.org", + "shortName": "base", + "chainId": 8453, + "networkId": 8453, + "status": "incubating" + }, + { + "name": "Toki Network", + "chain": "TOKI", + "rpc": ["https://mainnet.buildwithtoki.com/v0/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Toki", "symbol": "TOKI", "decimals": 18 }, + "infoURL": "https://www.buildwithtoki.com", + "shortName": "toki", + "chainId": 8654, + "networkId": 8654, + "icon": "toki", + "explorers": [] + }, + { + "name": "Toki Testnet", + "chain": "TOKI", + "rpc": ["https://testnet.buildwithtoki.com/v0/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Toki", "symbol": "TOKI", "decimals": 18 }, + "infoURL": "https://www.buildwithtoki.com", + "shortName": "toki-testnet", + "chainId": 8655, + "networkId": 8655, + "icon": "toki", + "explorers": [] + }, + { + "name": "TOOL Global Mainnet", + "chain": "OLO", + "rpc": ["https://mainnet-web3.wolot.io"], + "faucets": [], + "nativeCurrency": { "name": "TOOL Global", "symbol": "OLO", "decimals": 18 }, + "infoURL": "https://ibdt.io", + "shortName": "olo", + "chainId": 8723, + "networkId": 8723, + "slip44": 479, + "explorers": [ + { "name": "OLO Block Explorer", "url": "https://www.olo.network", "standard": "EIP3091" } + ] + }, + { + "name": "TOOL Global Testnet", + "chain": "OLO", + "rpc": ["https://testnet-web3.wolot.io"], + "faucets": ["https://testnet-explorer.wolot.io"], + "nativeCurrency": { "name": "TOOL Global", "symbol": "OLO", "decimals": 18 }, + "infoURL": "https://testnet-explorer.wolot.io", + "shortName": "tolo", + "chainId": 8724, + "networkId": 8724, + "slip44": 479 + }, + { + "name": "Alph Network", + "chain": "ALPH", + "rpc": ["https://rpc.alph.network", "wss://rpc.alph.network"], + "faucets": [], + "nativeCurrency": { "name": "Alph Network", "symbol": "ALPH", "decimals": 18 }, + "infoURL": "https://alph.network", + "shortName": "alph", + "chainId": 8738, + "networkId": 8738, + "explorers": [ + { "name": "alphscan", "url": "https://explorer.alph.network", "standard": "EIP3091" } + ] + }, + { + "name": "TMY Chain", + "chain": "TMY", + "icon": "tmychain", + "rpc": ["https://node1.tmyblockchain.org/rpc"], + "faucets": ["https://faucet.tmychain.org/"], + "nativeCurrency": { "name": "TMY", "symbol": "TMY", "decimals": 18 }, + "infoURL": "https://tmychain.org/", + "shortName": "tmy", + "chainId": 8768, + "networkId": 8768 + }, + { + "name": "MARO Blockchain Mainnet", + "chain": "MARO Blockchain", + "icon": "MARO", + "rpc": ["https://rpc-mainnet.ma.ro"], + "faucets": [], + "nativeCurrency": { "name": "MARO", "symbol": "MARO", "decimals": 18 }, + "infoURL": "https://ma.ro/", + "shortName": "maro", + "chainId": 8848, + "networkId": 8848, + "explorers": [{ "name": "MARO Scan", "url": "https://scan.ma.ro/#", "standard": "none" }] + }, + { + "name": "Unique", + "icon": "unique", + "chain": "UNQ", + "rpc": [ + "https://rpc.unique.network", + "https://eu-rpc.unique.network", + "https://asia-rpc.unique.network", + "https://us-rpc.unique.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Unique", "symbol": "UNQ", "decimals": 18 }, + "infoURL": "https://unique.network", + "shortName": "unq", + "chainId": 8880, + "networkId": 8880, + "explorers": [ + { "name": "Unique Scan", "url": "https://uniquescan.io/unique", "standard": "none" } + ] + }, + { + "name": "Quartz by Unique", + "icon": "quartz", + "chain": "UNQ", + "rpc": [ + "https://rpc-quartz.unique.network", + "https://quartz.api.onfinality.io/public-ws", + "https://eu-rpc-quartz.unique.network", + "https://asia-rpc-quartz.unique.network", + "https://us-rpc-quartz.unique.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Quartz", "symbol": "QTZ", "decimals": 18 }, + "infoURL": "https://unique.network", + "shortName": "qtz", + "chainId": 8881, + "networkId": 8881, + "explorers": [ + { "name": "Unique Scan / Quartz", "url": "https://uniquescan.io/quartz", "standard": "none" } + ] + }, + { + "name": "Opal testnet by Unique", + "icon": "opal", + "chain": "UNQ", + "rpc": [ + "https://rpc-opal.unique.network", + "https://us-rpc-opal.unique.network", + "https://eu-rpc-opal.unique.network", + "https://asia-rpc-opal.unique.network" + ], + "faucets": ["https://t.me/unique2faucet_opal_bot"], + "nativeCurrency": { "name": "Opal", "symbol": "UNQ", "decimals": 18 }, + "infoURL": "https://unique.network", + "shortName": "opl", + "chainId": 8882, + "networkId": 8882, + "explorers": [ + { "name": "Unique Scan / Opal", "url": "https://uniquescan.io/opal", "standard": "none" } + ] + }, + { + "name": "Sapphire by Unique", + "icon": "sapphire", + "chain": "UNQ", + "rpc": [ + "https://rpc-sapphire.unique.network", + "https://us-rpc-sapphire.unique.network", + "https://eu-rpc-sapphire.unique.network", + "https://asia-rpc-sapphire.unique.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Quartz", "symbol": "QTZ", "decimals": 18 }, + "infoURL": "https://unique.network", + "shortName": "sph", + "chainId": 8883, + "networkId": 8883, + "explorers": [ + { + "name": "Unique Scan / Sapphire", + "url": "https://uniquescan.io/sapphire", + "standard": "none" + } + ] + }, + { + "name": "XANAChain", + "chain": "XANAChain", + "rpc": ["https://mainnet.xana.net/rpc"], + "faucets": [], + "nativeCurrency": { "name": "XETA", "symbol": "XETA", "decimals": 18 }, + "infoURL": "https://xanachain.xana.net/", + "shortName": "XANAChain", + "chainId": 8888, + "networkId": 8888, + "icon": "xeta", + "explorers": [ + { "name": "XANAChain", "url": "https://xanachain.xana.net", "standard": "EIP3091" } + ], + "redFlags": ["reusedChainId"] + }, + { + "name": "Vyvo Smart Chain", + "chain": "VSC", + "rpc": ["https://vsc-dataseed.vyvo.org:8889"], + "faucets": [], + "nativeCurrency": { "name": "VSC", "symbol": "VSC", "decimals": 18 }, + "infoURL": "https://vsc-dataseed.vyvo.org", + "shortName": "vsc", + "chainId": 8889, + "networkId": 8889 + }, + { + "name": "Mammoth Mainnet", + "title": "Mammoth Chain", + "chain": "MMT", + "rpc": [ + "https://dataseed.mmtscan.io", + "https://dataseed1.mmtscan.io", + "https://dataseed2.mmtscan.io" + ], + "faucets": ["https://faucet.mmtscan.io/"], + "nativeCurrency": { "name": "Mammoth Token", "symbol": "MMT", "decimals": 18 }, + "infoURL": "https://mmtchain.io/", + "shortName": "mmt", + "chainId": 8898, + "networkId": 8898, + "icon": "mmt", + "explorers": [ + { "name": "mmtscan", "url": "https://mmtscan.io", "standard": "EIP3091", "icon": "mmt" } + ] + }, + { + "name": "JIBCHAIN L1", + "chain": "JBC", + "rpc": ["https://rpc-l1.jibchain.net"], + "faucets": [], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "nativeCurrency": { "name": "JIBCOIN", "symbol": "JBC", "decimals": 18 }, + "infoURL": "https://jibchain.net", + "shortName": "jbc", + "chainId": 8899, + "networkId": 8899, + "explorers": [ + { "name": "JIBCHAIN Explorer", "url": "https://exp-l1.jibchain.net", "standard": "EIP3091" } + ] + }, + { + "name": "Giant Mammoth Mainnet", + "title": "Giant Mammoth Chain", + "chain": "GMMT", + "rpc": ["https://rpc-asia.gmmtchain.io"], + "faucets": [], + "nativeCurrency": { "name": "Giant Mammoth Coin", "symbol": "GMMT", "decimals": 18 }, + "infoURL": "https://gmmtchain.io/", + "shortName": "gmmt", + "chainId": 8989, + "networkId": 8989, + "icon": "gmmt", + "explorers": [ + { + "name": "gmmtscan", + "url": "https://scan.gmmtchain.io", + "standard": "EIP3091", + "icon": "gmmt" + } + ] + }, + { + "name": "bloxberg", + "chain": "bloxberg", + "rpc": ["https://core.bloxberg.org"], + "faucets": ["https://faucet.bloxberg.org/"], + "nativeCurrency": { "name": "BERG", "symbol": "U+25B3", "decimals": 18 }, + "infoURL": "https://bloxberg.org", + "shortName": "berg", + "chainId": 8995, + "networkId": 8995 + }, + { + "name": "Evmos Testnet", + "chain": "Evmos", + "rpc": ["https://eth.bd.evmos.dev:8545"], + "faucets": ["https://faucet.evmos.dev"], + "nativeCurrency": { "name": "test-Evmos", "symbol": "tEVMOS", "decimals": 18 }, + "infoURL": "https://evmos.org", + "shortName": "evmos-testnet", + "chainId": 9000, + "networkId": 9000, + "icon": "evmos", + "explorers": [ + { + "name": "Evmos EVM Explorer", + "url": "https://evm.evmos.dev", + "standard": "EIP3091", + "icon": "evmos" + }, + { + "name": "Evmos Cosmos Explorer", + "url": "https://explorer.evmos.dev", + "standard": "none", + "icon": "evmos" + } + ] + }, + { + "name": "Evmos", + "chain": "Evmos", + "rpc": ["https://eth.bd.evmos.org:8545", "https://evmos-evm.publicnode.com"], + "faucets": [], + "nativeCurrency": { "name": "Evmos", "symbol": "EVMOS", "decimals": 18 }, + "infoURL": "https://evmos.org", + "shortName": "evmos", + "chainId": 9001, + "networkId": 9001, + "icon": "evmos", + "explorers": [ + { + "name": "Evmos EVM Explorer (Escan)", + "url": "https://escan.live", + "standard": "none", + "icon": "evmos" + }, + { + "name": "Evmos Cosmos Explorer (Mintscan)", + "url": "https://www.mintscan.io/evmos", + "standard": "none", + "icon": "evmos" + } + ] + }, + { + "name": "BerylBit Mainnet", + "chain": "BRB", + "rpc": ["https://mainnet.berylbit.io"], + "faucets": ["https://t.me/BerylBit"], + "nativeCurrency": { "name": "BerylBit Chain Native Token", "symbol": "BRB", "decimals": 18 }, + "infoURL": "https://www.beryl-bit.com", + "shortName": "brb", + "chainId": 9012, + "networkId": 9012, + "icon": "berylbit", + "explorers": [ + { "name": "berylbit-explorer", "url": "https://explorer.berylbit.io", "standard": "EIP3091" } + ] + }, + { + "name": "Genesis Coin", + "chain": "Genesis", + "rpc": ["https://genesis-gn.com", "wss://genesis-gn.com"], + "faucets": [], + "nativeCurrency": { "name": "GN Coin", "symbol": "GNC", "decimals": 18 }, + "infoURL": "https://genesis-gn.com", + "shortName": "GENEC", + "chainId": 9100, + "networkId": 9100 + }, + { + "name": "Rinia Testnet Old", + "chain": "FIRE", + "icon": "rinia", + "rpc": [], + "faucets": ["https://faucet.thefirechain.com"], + "nativeCurrency": { "name": "Firechain", "symbol": "FIRE", "decimals": 18 }, + "infoURL": "https://thefirechain.com", + "shortName": "_old_tfire", + "chainId": 9170, + "networkId": 9170, + "explorers": [], + "status": "deprecated" + }, + { + "name": "Dogcoin Testnet", + "chain": "DOGS", + "icon": "dogs", + "rpc": ["https://testnet-rpc.dogcoin.me"], + "faucets": ["https://faucet.dogcoin.network"], + "nativeCurrency": { "name": "Dogcoin", "symbol": "DOGS", "decimals": 18 }, + "infoURL": "https://dogcoin.network", + "shortName": "DOGSt", + "chainId": 9339, + "networkId": 9339, + "explorers": [ + { "name": "Dogcoin", "url": "https://testnet.dogcoin.network", "standard": "EIP3091" } + ] + }, + { + "name": "Rangers Protocol Testnet Robin", + "chain": "Rangers", + "icon": "rangers", + "rpc": ["https://robin.rangersprotocol.com/api/jsonrpc"], + "faucets": ["https://robin-faucet.rangersprotocol.com"], + "nativeCurrency": { "name": "Rangers Protocol Gas", "symbol": "tRPG", "decimals": 18 }, + "infoURL": "https://rangersprotocol.com", + "shortName": "trpg", + "chainId": 9527, + "networkId": 9527, + "explorers": [ + { + "name": "rangersscan-robin", + "url": "https://robin-rangersscan.rangersprotocol.com", + "standard": "none" + } + ] + }, + { + "name": "QEasyWeb3 Testnet", + "chain": "QET", + "rpc": ["https://qeasyweb3.com"], + "faucets": ["http://faucet.qeasyweb3.com"], + "nativeCurrency": { "name": "QET", "symbol": "QET", "decimals": 18 }, + "infoURL": "https://www.qeasyweb3.com", + "shortName": "QETTest", + "chainId": 9528, + "networkId": 9528, + "explorers": [ + { "name": "QEasyWeb3 Explorer", "url": "https://www.qeasyweb3.com", "standard": "EIP3091" } + ] + }, + { + "name": "Oort MainnetDev", + "title": "Oort MainnetDev", + "chain": "MainnetDev", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Oort", "symbol": "CCN", "decimals": 18 }, + "infoURL": "https://oortech.com", + "shortName": "MainnetDev", + "chainId": 9700, + "networkId": 9700, + "icon": "ccn" + }, + { + "name": "Boba BNB Testnet", + "chain": "Boba BNB Testnet", + "rpc": [ + "https://testnet.bnb.boba.network", + "wss://wss.testnet.bnb.boba.network", + "https://replica.testnet.bnb.boba.network", + "wss://replica-wss.testnet.bnb.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaBnbTestnet", + "chainId": 9728, + "networkId": 9728, + "explorers": [ + { + "name": "Boba BNB Testnet block explorer", + "url": "https://blockexplorer.testnet.bnb.boba.network", + "standard": "none" + } + ] + }, + { + "name": "MainnetZ Testnet", + "chain": "NetZ", + "icon": "mainnetzTestnet", + "rpc": ["https://testnet-rpc.mainnetz.io"], + "faucets": ["https://faucet.mainnetz.io"], + "nativeCurrency": { "name": "MainnetZ", "symbol": "NetZ", "decimals": 18 }, + "infoURL": "https://testnet.mainnetz.io", + "shortName": "NetZt", + "chainId": 9768, + "networkId": 9768, + "explorers": [ + { "name": "MainnetZ", "url": "https://testnet.mainnetz.io", "standard": "EIP3091" } + ] + }, + { + "name": "myOwn Testnet", + "chain": "myOwn", + "rpc": ["https://geth.dev.bccloud.net"], + "faucets": [], + "nativeCurrency": { "name": "MYN", "symbol": "MYN", "decimals": 18 }, + "infoURL": "https://docs.bccloud.net/", + "shortName": "myn", + "chainId": 9999, + "networkId": 9999 + }, + { + "name": "Smart Bitcoin Cash", + "chain": "smartBCH", + "rpc": [ + "https://smartbch.greyh.at", + "https://rpc-mainnet.smartbch.org", + "https://smartbch.fountainhead.cash/mainnet", + "https://smartbch.devops.cash/mainnet" + ], + "faucets": [], + "nativeCurrency": { "name": "Bitcoin Cash", "symbol": "BCH", "decimals": 18 }, + "infoURL": "https://smartbch.org/", + "shortName": "smartbch", + "chainId": 10000, + "networkId": 10000 + }, + { + "name": "Smart Bitcoin Cash Testnet", + "chain": "smartBCHTest", + "rpc": ["https://rpc-testnet.smartbch.org", "https://smartbch.devops.cash/testnet"], + "faucets": [], + "nativeCurrency": { "name": "Bitcoin Cash Test Token", "symbol": "BCHT", "decimals": 18 }, + "infoURL": "http://smartbch.org/", + "shortName": "smartbchtest", + "chainId": 10001, + "networkId": 10001 + }, + { + "name": "Gon Chain", + "chain": "GonChain", + "icon": "gonchain", + "rpc": ["https://node1.testnet.gaiaopen.network", "http://database1.gaiaopen.network"], + "faucets": [], + "nativeCurrency": { "name": "Gon Token", "symbol": "GT", "decimals": 18 }, + "infoURL": "", + "shortName": "gon", + "chainId": 10024, + "networkId": 10024, + "explorers": [{ "name": "Gon Explorer", "url": "https://gonscan.com", "standard": "none" }] + }, + { + "name": "SJATSH", + "chain": "ETH", + "rpc": ["http://geth.free.idcfengye.com"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://sjis.me", + "shortName": "SJ", + "chainId": 10086, + "networkId": 10086 + }, + { + "name": "Blockchain Genesis Mainnet", + "chain": "GEN", + "rpc": [ + "https://eu.mainnet.xixoio.com", + "https://us.mainnet.xixoio.com", + "https://asia.mainnet.xixoio.com" + ], + "faucets": [], + "nativeCurrency": { "name": "GEN", "symbol": "GEN", "decimals": 18 }, + "infoURL": "https://www.xixoio.com/", + "shortName": "GEN", + "chainId": 10101, + "networkId": 10101 + }, + { + "name": "Chiado Testnet", + "chain": "CHI", + "icon": "gnosis", + "rpc": [ + "https://rpc.chiadochain.net", + "https://rpc.eu-central-2.gateway.fm/v3/gnosis/archival/chiado" + ], + "faucets": ["https://gnosisfaucet.com"], + "nativeCurrency": { "name": "Chiado xDAI", "symbol": "xDAI", "decimals": 18 }, + "infoURL": "https://docs.gnosischain.com", + "shortName": "chi", + "chainId": 10200, + "networkId": 10200, + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout.chiadochain.net", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "0XTade", + "chain": "0XTade Chain", + "rpc": ["https://node.0xtchain.com"], + "faucets": [], + "nativeCurrency": { "name": "0XT", "symbol": "0XT", "decimals": 18 }, + "infoURL": "https://www.0xtrade.finance/", + "shortName": "0xt", + "chainId": 10248, + "networkId": 10248, + "explorers": [{ "name": "0xtrade Scan", "url": "https://www.0xtscan.com", "standard": "none" }] + }, + { + "name": "Numbers Mainnet", + "chain": "NUM", + "icon": "num", + "rpc": ["https://mainnetrpc.num.network"], + "faucets": [], + "nativeCurrency": { "name": "NUM Token", "symbol": "NUM", "decimals": 18 }, + "infoURL": "https://numbersprotocol.io", + "shortName": "Jade", + "chainId": 10507, + "networkId": 10507, + "explorers": [ + { "name": "ethernal", "url": "https://mainnet.num.network", "standard": "EIP3091" } + ] + }, + { + "name": "Numbers Testnet", + "chain": "NUM", + "icon": "num", + "rpc": ["https://testnetrpc.num.network"], + "faucets": ["https://faucet.avax.network/?subnet=num", "https://faucet.num.network"], + "nativeCurrency": { "name": "NUM Token", "symbol": "NUM", "decimals": 18 }, + "infoURL": "https://numbersprotocol.io", + "shortName": "Snow", + "chainId": 10508, + "networkId": 10508, + "explorers": [ + { "name": "ethernal", "url": "https://testnet.num.network", "standard": "EIP3091" } + ] + }, + { + "name": "CryptoCoinPay", + "chain": "CCP", + "rpc": ["http://node106.cryptocoinpay.info:8545", "ws://node106.cryptocoinpay.info:8546"], + "faucets": [], + "icon": "ccp", + "nativeCurrency": { "name": "CryptoCoinPay", "symbol": "CCP", "decimals": 18 }, + "infoURL": "https://www.cryptocoinpay.co", + "shortName": "CCP", + "chainId": 10823, + "networkId": 10823, + "explorers": [ + { "name": "CCP Explorer", "url": "https://cryptocoinpay.info", "standard": "EIP3091" } + ] + }, + { + "name": "Quadrans Blockchain", + "chain": "QDC", + "icon": "quadrans", + "rpc": ["https://rpc.quadrans.io", "https://rpcna.quadrans.io", "https://rpceu.quadrans.io"], + "faucets": [], + "nativeCurrency": { "name": "Quadrans Coin", "symbol": "QDC", "decimals": 18 }, + "infoURL": "https://quadrans.io", + "shortName": "quadrans", + "chainId": 10946, + "networkId": 10946, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.quadrans.io", + "icon": "quadrans", + "standard": "EIP3091" + } + ] + }, + { + "name": "Quadrans Blockchain Testnet", + "chain": "tQDC", + "icon": "quadrans", + "rpc": ["https://rpctest.quadrans.io", "https://rpctest2.quadrans.io"], + "faucets": ["https://faucetpage.quadrans.io"], + "nativeCurrency": { "name": "Quadrans Testnet Coin", "symbol": "tQDC", "decimals": 18 }, + "infoURL": "https://quadrans.io", + "shortName": "quadranstestnet", + "chainId": 10947, + "networkId": 10947, + "explorers": [ + { + "name": "explorer", + "url": "https://explorer.testnet.quadrans.io", + "icon": "quadrans", + "standard": "EIP3091" + } + ] + }, + { + "name": "Astra", + "chain": "Astra", + "rpc": ["https://rpc.astranaut.io", "https://rpc1.astranaut.io"], + "faucets": [], + "nativeCurrency": { "name": "Astra", "symbol": "ASA", "decimals": 18 }, + "infoURL": "https://astranaut.io", + "shortName": "astra", + "chainId": 11110, + "networkId": 11110, + "icon": "astra", + "explorers": [ + { + "name": "Astra EVM Explorer (Blockscout)", + "url": "https://explorer.astranaut.io", + "standard": "none", + "icon": "astra" + }, + { + "name": "Astra PingPub Explorer", + "url": "https://ping.astranaut.io/astra", + "standard": "none", + "icon": "astra" + } + ] + }, + { + "name": "WAGMI", + "chain": "WAGMI", + "icon": "wagmi", + "rpc": ["https://subnets.avax.network/wagmi/wagmi-chain-testnet/rpc"], + "faucets": ["https://faucet.avax.network/?subnet=wagmi"], + "nativeCurrency": { "name": "WAGMI", "symbol": "WGM", "decimals": 18 }, + "infoURL": "https://subnets-test.avax.network/wagmi/details", + "shortName": "WAGMI", + "chainId": 11111, + "networkId": 11111, + "explorers": [ + { + "name": "Avalanche Subnet Explorer", + "url": "https://subnets-test.avax.network/wagmi", + "standard": "EIP3091" + } + ] + }, + { + "name": "Astra Testnet", + "chain": "Astra", + "rpc": ["https://rpc.astranaut.dev"], + "faucets": ["https://faucet.astranaut.dev"], + "nativeCurrency": { "name": "test-Astra", "symbol": "tASA", "decimals": 18 }, + "infoURL": "https://astranaut.io", + "shortName": "astra-testnet", + "chainId": 11115, + "networkId": 11115, + "icon": "astra", + "explorers": [ + { + "name": "Astra EVM Explorer", + "url": "https://explorer.astranaut.dev", + "standard": "EIP3091", + "icon": "astra" + }, + { + "name": "Astra PingPub Explorer", + "url": "https://ping.astranaut.dev/astra", + "standard": "none", + "icon": "astra" + } + ] + }, + { + "name": "HashBit Mainnet", + "chain": "HBIT", + "rpc": ["https://mainnet-rpc.hashbit.org", "https://rpc.hashbit.org"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "HashBit Native Token", "symbol": "HBIT", "decimals": 18 }, + "infoURL": "https://hashbit.org", + "shortName": "hbit", + "chainId": 11119, + "networkId": 11119, + "explorers": [ + { "name": "hashbitscan", "url": "https://explorer.hashbit.org", "standard": "EIP3091" } + ] + }, + { + "name": "Haqq Network", + "chain": "Haqq", + "rpc": ["https://rpc.eth.haqq.network"], + "faucets": [], + "nativeCurrency": { "name": "Islamic Coin", "symbol": "ISLM", "decimals": 18 }, + "infoURL": "https://islamiccoin.net", + "shortName": "ISLM", + "chainId": 11235, + "networkId": 11235, + "explorers": [ + { + "name": "Mainnet HAQQ Explorer", + "url": "https://explorer.haqq.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Shyft Testnet", + "chain": "SHYFTT", + "icon": "shyft", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Shyft Test Token", "symbol": "SHYFTT", "decimals": 18 }, + "infoURL": "https://shyft.network", + "shortName": "shyftt", + "chainId": 11437, + "networkId": 11437, + "explorers": [ + { + "name": "Shyft Testnet BX", + "url": "https://bx.testnet.shyft.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Sardis Testnet", + "chain": "SRDX", + "icon": "sardisTestnet", + "rpc": ["https://testnet-rpc.sardisnetwork.com"], + "faucets": ["https://faucet.sardisnetwork.com"], + "nativeCurrency": { "name": "Sardis", "symbol": "SRDX", "decimals": 18 }, + "infoURL": "https://mysardis.com", + "shortName": "SRDXt", + "chainId": 11612, + "networkId": 11612, + "explorers": [ + { "name": "Sardis", "url": "https://testnet.sardisnetwork.com", "standard": "EIP3091" } + ] + }, + { + "name": "SanR Chain", + "chain": "SanRChain", + "rpc": ["https://sanrchain-node.santiment.net"], + "faucets": [], + "nativeCurrency": { "name": "nSAN", "symbol": "nSAN", "decimals": 18 }, + "infoURL": "https://sanr.app", + "shortName": "SAN", + "chainId": 11888, + "networkId": 11888, + "icon": "sanrchain", + "parent": { "chain": "eip155-1", "type": "L2", "bridges": [{ "url": "https://sanr.app" }] }, + "explorers": [ + { + "name": "SanR Chain Explorer", + "url": "https://sanrchain-explorer.santiment.net", + "standard": "none" + } + ] + }, + { + "name": "Singularity ZERO Testnet", + "chain": "ZERO", + "rpc": ["https://betaenv.singularity.gold:18545"], + "faucets": ["https://nft.singularity.gold"], + "nativeCurrency": { "name": "ZERO", "symbol": "tZERO", "decimals": 18 }, + "infoURL": "https://www.singularity.gold", + "shortName": "tZERO", + "chainId": 12051, + "networkId": 12051, + "explorers": [ + { "name": "zeroscan", "url": "https://betaenv.singularity.gold:18002", "standard": "EIP3091" } + ] + }, + { + "name": "Singularity ZERO Mainnet", + "chain": "ZERO", + "rpc": ["https://zerorpc.singularity.gold"], + "faucets": ["https://zeroscan.singularity.gold"], + "nativeCurrency": { "name": "ZERO", "symbol": "ZERO", "decimals": 18 }, + "infoURL": "https://www.singularity.gold", + "shortName": "ZERO", + "chainId": 12052, + "networkId": 12052, + "slip44": 621, + "explorers": [ + { "name": "zeroscan", "url": "https://zeroscan.singularity.gold", "standard": "EIP3091" } + ] + }, + { + "name": "Fibonacci Mainnet", + "chain": "FIBO", + "icon": "fibonacci", + "rpc": ["https://node1.fibo-api.asia"], + "faucets": [], + "nativeCurrency": { "name": "FIBONACCI UTILITY TOKEN", "symbol": "FIBO", "decimals": 18 }, + "infoURL": "https://fibochain.org", + "shortName": "fibo", + "chainId": 12306, + "networkId": 1230, + "explorers": [ + { "name": "fiboscan", "url": "https://scan.fibochain.org", "standard": "EIP3091" } + ] + }, + { + "name": "BLG Testnet", + "chain": "BLG", + "icon": "blg", + "rpc": ["https://rpc.blgchain.com"], + "faucets": ["https://faucet.blgchain.com"], + "nativeCurrency": { "name": "Blg", "symbol": "BLG", "decimals": 18 }, + "infoURL": "https://blgchain.com", + "shortName": "blgchain", + "chainId": 12321, + "networkId": 12321 + }, + { + "name": "Step Testnet", + "title": "Step Test Network", + "chain": "STEP", + "icon": "step", + "rpc": ["https://rpc.testnet.step.network"], + "faucets": ["https://faucet.step.network"], + "nativeCurrency": { "name": "FITFI", "symbol": "FITFI", "decimals": 18 }, + "infoURL": "https://step.network", + "shortName": "steptest", + "chainId": 12345, + "networkId": 12345, + "explorers": [ + { + "name": "StepScan", + "url": "https://testnet.stepscan.io", + "icon": "step", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-43113" } + }, + { + "name": "Rikeza Network Testnet", + "title": "Rikeza Network Testnet", + "chain": "Rikeza", + "rpc": ["https://testnet-rpc.rikscan.com"], + "faucets": [], + "nativeCurrency": { "name": "Rikeza", "symbol": "RIK", "decimals": 18 }, + "infoURL": "https://rikeza.io", + "shortName": "tRIK", + "chainId": 12715, + "networkId": 12715, + "explorers": [ + { + "name": "Rikeza Blockchain explorer", + "url": "https://testnet.rikscan.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "SPS", + "chain": "SPS", + "rpc": ["https://rpc.ssquad.games"], + "faucets": [], + "nativeCurrency": { "name": "ECG", "symbol": "ECG", "decimals": 18 }, + "infoURL": "https://ssquad.games/", + "shortName": "SPS", + "chainId": 13000, + "networkId": 13000, + "explorers": [ + { "name": "SPS Explorer", "url": "http://spsscan.ssquad.games", "standard": "EIP3091" } + ] + }, + { + "name": "Credit Smartchain Mainnet", + "chain": "CREDIT", + "rpc": ["https://mainnet-rpc.cscscan.io"], + "faucets": [], + "nativeCurrency": { "name": "Credit", "symbol": "CREDIT", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://creditsmartchain.com", + "shortName": "Credit", + "chainId": 13308, + "networkId": 1, + "icon": "credit", + "explorers": [ + { + "name": "CSC Scan", + "url": "https://explorer.cscscan.io", + "icon": "credit", + "standard": "EIP3091" + } + ] + }, + { + "name": "Phoenix Mainnet", + "chain": "Phoenix", + "rpc": ["https://rpc.phoenixplorer.com/"], + "faucets": [], + "nativeCurrency": { "name": "Phoenix", "symbol": "PHX", "decimals": 18 }, + "infoURL": "https://cryptophoenix.org/phoenix", + "shortName": "Phoenix", + "chainId": 13381, + "networkId": 13381, + "icon": "phoenix", + "explorers": [ + { "name": "phoenixplorer", "url": "https://phoenixplorer.com", "standard": "EIP3091" } + ] + }, + { + "name": "Susono", + "chain": "SUS", + "rpc": [ + "https://gateway.opn.network/node/ext/bc/2VsZe5DstWw2bfgdx3YbjKcMsJnNDjni95sZorBEdk9L9Qr9Fr/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "Susono", "symbol": "OPN", "decimals": 18 }, + "infoURL": "", + "shortName": "sus", + "chainId": 13812, + "networkId": 13812, + "explorers": [{ "name": "Susono", "url": "http://explorer.opn.network", "standard": "none" }] + }, + { + "name": "SPS Testnet", + "chain": "SPS-Testnet", + "rpc": ["https://www.3sps.net"], + "faucets": [], + "nativeCurrency": { "name": "ECG", "symbol": "ECG", "decimals": 18 }, + "infoURL": "https://ssquad.games/", + "shortName": "SPS-Test", + "chainId": 14000, + "networkId": 14000, + "explorers": [ + { "name": "SPS Test Explorer", "url": "https://explorer.3sps.net", "standard": "EIP3091" } + ] + }, + { + "name": "LoopNetwork Mainnet", + "chain": "LoopNetwork", + "rpc": ["https://api.mainnetloop.com"], + "faucets": [], + "nativeCurrency": { "name": "LOOP", "symbol": "LOOP", "decimals": 18 }, + "infoURL": "http://theloopnetwork.org/", + "shortName": "loop", + "chainId": 15551, + "networkId": 15551, + "explorers": [ + { "name": "loopscan", "url": "http://explorer.mainnetloop.com", "standard": "none" } + ] + }, + { + "name": "Trust EVM Testnet", + "chain": "Trust EVM Testnet", + "rpc": ["https://api.testnet-dev.trust.one"], + "faucets": ["https://faucet.testnet-dev.trust.one/"], + "nativeCurrency": { "name": "Trust EVM", "symbol": "EVM", "decimals": 18 }, + "infoURL": "https://www.trust.one/", + "shortName": "TrustTestnet", + "chainId": 15555, + "networkId": 15555, + "explorers": [ + { "name": "Trust EVM Explorer", "url": "https://trustscan.one", "standard": "EIP3091" } + ] + }, + { + "name": "MetaDot Mainnet", + "chain": "MTT", + "rpc": ["https://mainnet.metadot.network"], + "faucets": [], + "nativeCurrency": { "name": "MetaDot Token", "symbol": "MTT", "decimals": 18 }, + "infoURL": "https://metadot.network", + "shortName": "mtt", + "chainId": 16000, + "networkId": 16000 + }, + { + "name": "MetaDot Testnet", + "chain": "MTTTest", + "rpc": ["https://testnet.metadot.network"], + "faucets": ["https://faucet.metadot.network/"], + "nativeCurrency": { "name": "MetaDot Token TestNet", "symbol": "MTTest", "decimals": 18 }, + "infoURL": "https://metadot.network", + "shortName": "mtttest", + "chainId": 16001, + "networkId": 16001 + }, + { + "name": "AirDAO Mainnet", + "chain": "ambnet", + "icon": "airdao", + "rpc": ["https://network.ambrosus.io"], + "faucets": [], + "nativeCurrency": { "name": "Amber", "symbol": "AMB", "decimals": 18 }, + "infoURL": "https://airdao.io", + "shortName": "airdao", + "chainId": 16718, + "networkId": 16718, + "explorers": [ + { "name": "AirDAO Network Explorer", "url": "https://airdao.io/explorer", "standard": "none" } + ] + }, + { + "name": "IVAR Chain Testnet", + "chain": "IVAR", + "icon": "ivar", + "rpc": ["https://testnet-rpc.ivarex.com"], + "faucets": ["https://tfaucet.ivarex.com/"], + "nativeCurrency": { "name": "tIvar", "symbol": "tIVAR", "decimals": 18 }, + "infoURL": "https://ivarex.com", + "shortName": "tivar", + "chainId": 16888, + "networkId": 16888, + "explorers": [ + { "name": "ivarscan", "url": "https://testnet.ivarscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Frontier of Dreams Testnet", + "chain": "Game Network", + "rpc": ["https://rpc.fod.games/"], + "nativeCurrency": { "name": "ZKST", "symbol": "ZKST", "decimals": 18 }, + "faucets": [], + "shortName": "ZKST", + "chainId": 18000, + "networkId": 18000, + "infoURL": "https://goexosphere.com", + "explorers": [ + { "name": "Game Network", "url": "https://explorer.fod.games", "standard": "EIP3091" } + ] + }, + { + "name": "Proof Of Memes", + "title": "Proof Of Memes Mainnet", + "chain": "POM", + "icon": "pom", + "rpc": [ + "https://mainnet-rpc.memescan.io", + "https://mainnet-rpc2.memescan.io", + "https://mainnet-rpc3.memescan.io", + "https://mainnet-rpc4.memescan.io" + ], + "faucets": [], + "nativeCurrency": { "name": "Proof Of Memes", "symbol": "POM", "decimals": 18 }, + "infoURL": "https://proofofmemes.org", + "shortName": "pom", + "chainId": 18159, + "networkId": 18159, + "explorers": [ + { "name": "explorer-proofofmemes", "url": "https://memescan.io", "standard": "EIP3091" } + ] + }, + { + "name": "HOME Verse Mainnet", + "chain": "HOME Verse", + "icon": "home_verse", + "rpc": ["https://rpc.mainnet.oasys.homeverse.games/"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://www.homeverse.games/", + "shortName": "HMV", + "chainId": 19011, + "networkId": 19011, + "explorers": [ + { + "name": "HOME Verse Explorer", + "url": "https://explorer.oasys.homeverse.games", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-248" } + }, + { + "name": "BTCIX Network", + "chain": "BTCIX", + "rpc": ["https://seed.btcix.org/rpc"], + "faucets": [], + "nativeCurrency": { "name": "BTCIX Network", "symbol": "BTCIX", "decimals": 18 }, + "infoURL": "https://bitcolojix.org", + "shortName": "btcix", + "chainId": 19845, + "networkId": 19845, + "explorers": [{ "name": "BTCIXScan", "url": "https://btcixscan.com", "standard": "none" }] + }, + { + "name": "Camelark Mainnet", + "chainId": 20001, + "shortName": "Camelark", + "chain": "ETHW", + "icon": "camelark", + "networkId": 20001, + "nativeCurrency": { "name": "EthereumPoW", "symbol": "ETHW", "decimals": 18 }, + "rpc": ["https://mainnet-http-rpc.camelark.com"], + "faucets": [], + "explorers": [ + { "name": "CamelarkScan", "url": "https://scan.camelark.com", "standard": "EIP3091" } + ], + "infoURL": "https://www.camelark.com" + }, + { + "name": "Callisto Testnet", + "chain": "CLO", + "rpc": ["https://testnet-rpc.callisto.network/"], + "faucets": ["https://faucet.callisto.network/"], + "nativeCurrency": { "name": "Callisto", "symbol": "CLO", "decimals": 18 }, + "infoURL": "https://callisto.network", + "shortName": "CLOTestnet", + "chainId": 20729, + "networkId": 79 + }, + { + "name": "P12 Chain", + "chain": "P12", + "icon": "p12", + "rpc": ["https://rpc-chain.p12.games"], + "faucets": [], + "nativeCurrency": { "name": "Hooked P2", "symbol": "hP2", "decimals": 18 }, + "infoURL": "https://p12.network", + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "shortName": "p12", + "chainId": 20736, + "networkId": 20736, + "explorers": [ + { "name": "P12 Chain Explorer", "url": "https://explorer.p12.games", "standard": "EIP3091" } + ] + }, + { + "name": "CENNZnet Azalea", + "chain": "CENNZnet", + "rpc": ["https://cennznet.unfrastructure.io/public"], + "faucets": [], + "nativeCurrency": { "name": "CPAY", "symbol": "CPAY", "decimals": 18 }, + "infoURL": "https://cennz.net", + "shortName": "cennz-a", + "chainId": 21337, + "networkId": 21337, + "icon": "cennz", + "explorers": [{ "name": "UNcover", "url": "https://uncoverexplorer.com", "standard": "none" }] + }, + { + "name": "omChain Mainnet", + "chain": "OML", + "icon": "omlira", + "rpc": ["https://seed.omchain.io"], + "faucets": [], + "nativeCurrency": { "name": "omChain", "symbol": "OMC", "decimals": 18 }, + "infoURL": "https://omchain.io", + "shortName": "omc", + "chainId": 21816, + "networkId": 21816, + "explorers": [ + { "name": "omChain Explorer", "url": "https://explorer.omchain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Taycan", + "chain": "Taycan", + "rpc": ["https://taycan-rpc.hupayx.io:8545"], + "faucets": [], + "nativeCurrency": { "name": "shuffle", "symbol": "SFL", "decimals": 18 }, + "infoURL": "https://hupayx.io", + "shortName": "SFL", + "chainId": 22023, + "networkId": 22023, + "icon": "shuffle", + "explorers": [ + { + "name": "Taycan Explorer(Blockscout)", + "url": "https://taycan-evmscan.hupayx.io", + "standard": "none", + "icon": "shuffle" + }, + { + "name": "Taycan Cosmos Explorer(BigDipper)", + "url": "https://taycan-cosmoscan.hupayx.io", + "standard": "none", + "icon": "shuffle" + } + ] + }, + { + "name": "AirDAO Testnet", + "chain": "ambnet-test", + "icon": "airdao", + "rpc": ["https://network.ambrosus-test.io"], + "faucets": [], + "nativeCurrency": { "name": "Amber", "symbol": "AMB", "decimals": 18 }, + "infoURL": "https://testnet.airdao.io", + "shortName": "airdao-test", + "chainId": 22040, + "networkId": 22040, + "explorers": [ + { + "name": "AirDAO Network Explorer", + "url": "https://testnet.airdao.io/explorer", + "standard": "none" + } + ] + }, + { + "name": "MAP Mainnet", + "chain": "MAP", + "icon": "map", + "rpc": ["https://rpc.maplabs.io"], + "faucets": [], + "nativeCurrency": { "name": "MAP", "symbol": "MAP", "decimals": 18 }, + "infoURL": "https://maplabs.io", + "shortName": "map", + "chainId": 22776, + "networkId": 22776, + "slip44": 60, + "explorers": [{ "name": "mapscan", "url": "https://mapscan.io", "standard": "EIP3091" }] + }, + { + "name": "Opside Testnet", + "chain": "Opside", + "rpc": ["https://testrpc.opside.network"], + "faucets": ["https://faucet.opside.network"], + "nativeCurrency": { "name": "IDE", "symbol": "IDE", "decimals": 18 }, + "infoURL": "https://opside.network", + "shortName": "opside", + "chainId": 23118, + "networkId": 23118, + "icon": "opside", + "explorers": [{ "name": "opsideInfo", "url": "https://opside.info", "standard": "EIP3091" }] + }, + { + "name": "Oasis Sapphire", + "chain": "Sapphire", + "icon": "oasis", + "rpc": ["https://sapphire.oasis.io", "wss://sapphire.oasis.io/ws"], + "faucets": [], + "nativeCurrency": { "name": "Sapphire Rose", "symbol": "ROSE", "decimals": 18 }, + "infoURL": "https://docs.oasis.io/dapp/sapphire", + "shortName": "sapphire", + "chainId": 23294, + "networkId": 23294, + "explorers": [ + { + "name": "Oasis Sapphire Explorer", + "url": "https://explorer.sapphire.oasis.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Oasis Sapphire Testnet", + "chain": "Sapphire", + "icon": "oasis", + "rpc": ["https://testnet.sapphire.oasis.dev", "wss://testnet.sapphire.oasis.dev/ws"], + "faucets": [], + "nativeCurrency": { "name": "Sapphire Test Rose", "symbol": "TEST", "decimals": 18 }, + "infoURL": "https://docs.oasis.io/dapp/sapphire", + "shortName": "sapphire-testnet", + "chainId": 23295, + "networkId": 23295, + "explorers": [ + { + "name": "Oasis Sapphire Testnet Explorer", + "url": "https://testnet.explorer.sapphire.oasis.dev", + "standard": "EIP3091" + } + ] + }, + { + "name": "Webchain", + "chain": "WEB", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Webchain Ether", "symbol": "WEB", "decimals": 18 }, + "infoURL": "https://webchain.network", + "shortName": "web", + "chainId": 24484, + "networkId": 37129, + "slip44": 227 + }, + { + "name": "MintMe.com Coin", + "chain": "MINTME", + "rpc": ["https://node1.mintme.com"], + "faucets": [], + "nativeCurrency": { "name": "MintMe.com Coin", "symbol": "MINTME", "decimals": 18 }, + "infoURL": "https://www.mintme.com", + "shortName": "mintme", + "chainId": 24734, + "networkId": 37480 + }, + { + "name": "Hammer Chain Mainnet", + "chain": "HammerChain", + "rpc": ["https://www.hammerchain.io/rpc"], + "faucets": [], + "nativeCurrency": { "name": "GOLDT", "symbol": "GOLDT", "decimals": 18 }, + "infoURL": "https://www.hammerchain.io", + "shortName": "GOLDT", + "chainId": 25888, + "networkId": 25888, + "explorers": [ + { "name": "Hammer Chain Explorer", "url": "https://www.hammerchain.io", "standard": "none" } + ] + }, + { + "name": "Bitkub Chain Testnet", + "chain": "BKC", + "icon": "bkc", + "rpc": ["https://rpc-testnet.bitkubchain.io", "wss://wss-testnet.bitkubchain.io"], + "faucets": ["https://faucet.bitkubchain.com"], + "nativeCurrency": { "name": "Bitkub Coin", "symbol": "tKUB", "decimals": 18 }, + "infoURL": "https://www.bitkubchain.com/", + "shortName": "bkct", + "chainId": 25925, + "networkId": 25925, + "explorers": [ + { + "name": "bkcscan-testnet", + "url": "https://testnet.bkcscan.com", + "standard": "none", + "icon": "bkc" + } + ] + }, + { + "name": "Hertz Network Mainnet", + "chain": "HTZ", + "rpc": ["https://mainnet-rpc.hertzscan.com"], + "faucets": [], + "nativeCurrency": { "name": "Hertz", "symbol": "HTZ", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://www.hertz-network.com", + "shortName": "HTZ", + "chainId": 26600, + "networkId": 26600, + "icon": "hertz-network", + "explorers": [ + { + "name": "Hertz Scan", + "url": "https://hertzscan.com", + "icon": "hertz-network", + "standard": "EIP3091" + } + ] + }, + { + "name": "OasisChain Mainnet", + "chain": "OasisChain", + "rpc": [ + "https://rpc1.oasischain.io", + "https://rpc2.oasischain.io", + "https://rpc3.oasischain.io" + ], + "faucets": ["http://faucet.oasischain.io"], + "nativeCurrency": { "name": "OAC", "symbol": "OAC", "decimals": 18 }, + "infoURL": "https://scan.oasischain.io", + "shortName": "OAC", + "chainId": 26863, + "networkId": 26863, + "explorers": [ + { "name": "OasisChain Explorer", "url": "https://scan.oasischain.io", "standard": "EIP3091" } + ] + }, + { + "name": "Optimism Bedrock (Goerli Alpha Testnet)", + "chain": "ETH", + "rpc": [ + "https://alpha-1-replica-0.bedrock-goerli.optimism.io", + "https://alpha-1-replica-1.bedrock-goerli.optimism.io", + "https://alpha-1-replica-2.bedrock-goerli.optimism.io", + "https://alpha-1-replica-2.bedrock-goerli.optimism.io" + ], + "faucets": [], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://community.optimism.io/docs/developers/bedrock", + "shortName": "obgor", + "chainId": 28528, + "networkId": 28528, + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout.com/optimism/bedrock-alpha", + "standard": "EIP3091" + } + ] + }, + { + "name": "Piece testnet", + "chain": "PieceNetwork", + "icon": "piecechain", + "rpc": ["https://testnet-rpc0.piecenetwork.com"], + "faucets": ["https://piecenetwork.com/faucet"], + "nativeCurrency": { "name": "ECE", "symbol": "ECE", "decimals": 18 }, + "infoURL": "https://piecenetwork.com", + "shortName": "Piece", + "chainId": 30067, + "networkId": 30067, + "explorers": [ + { + "name": "Piece Scan", + "url": "https://testnet-scan.piecenetwork.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Ethersocial Network", + "chain": "ESN", + "rpc": ["https://api.esn.gonspool.com"], + "faucets": [], + "nativeCurrency": { "name": "Ethersocial Network Ether", "symbol": "ESN", "decimals": 18 }, + "infoURL": "https://ethersocial.org", + "shortName": "esn", + "chainId": 31102, + "networkId": 1, + "slip44": 31102 + }, + { + "name": "CloudTx Mainnet", + "chain": "CLD", + "icon": "cloudtx", + "rpc": ["https://mainnet-rpc.cloudtx.finance"], + "faucets": [], + "nativeCurrency": { "name": "CloudTx", "symbol": "CLD", "decimals": 18 }, + "infoURL": "https://cloudtx.finance", + "shortName": "CLDTX", + "chainId": 31223, + "networkId": 31223, + "explorers": [ + { "name": "cloudtxscan", "url": "https://scan.cloudtx.finance", "standard": "EIP3091" } + ] + }, + { + "name": "CloudTx Testnet", + "chain": "CloudTx", + "icon": "cloudtx", + "rpc": ["https://testnet-rpc.cloudtx.finance"], + "faucets": ["https://faucet.cloudtx.finance"], + "nativeCurrency": { "name": "CloudTx", "symbol": "CLD", "decimals": 18 }, + "infoURL": "https://cloudtx.finance/", + "shortName": "CLD", + "chainId": 31224, + "networkId": 31224, + "explorers": [ + { + "name": "cloudtxexplorer", + "url": "https://explorer.cloudtx.finance", + "standard": "EIP3091" + } + ] + }, + { + "name": "GoChain Testnet", + "chain": "GO", + "rpc": ["https://testnet-rpc.gochain.io"], + "faucets": [], + "nativeCurrency": { "name": "GoChain Coin", "symbol": "GO", "decimals": 18 }, + "infoURL": "https://gochain.io", + "shortName": "got", + "chainId": 31337, + "networkId": 31337, + "slip44": 6060, + "explorers": [ + { + "name": "GoChain Testnet Explorer", + "url": "https://testnet-explorer.gochain.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Filecoin - Wallaby testnet", + "chain": "FIL", + "icon": "filecoin", + "rpc": ["https://wallaby.node.glif.io/rpc/v1"], + "faucets": ["https://wallaby.yoga/#faucet"], + "nativeCurrency": { "name": "testnet filecoin", "symbol": "tFIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin-wallaby", + "chainId": 31415, + "networkId": 31415, + "slip44": 1, + "explorers": [] + }, + { + "name": "Bitgert Mainnet", + "chain": "Brise", + "rpc": [ + "https://rpc.icecreamswap.com", + "https://mainnet-rpc.brisescan.com", + "https://chainrpc.com", + "https://serverrpc.com" + ], + "faucets": [], + "nativeCurrency": { "name": "Bitrise Token", "symbol": "Brise", "decimals": 18 }, + "infoURL": "https://bitgert.com/", + "shortName": "Brise", + "chainId": 32520, + "networkId": 32520, + "icon": "brise", + "explorers": [ + { + "name": "Brise Scan", + "url": "https://brisescan.com", + "icon": "brise", + "standard": "EIP3091" + } + ] + }, + { + "name": "Fusion Mainnet", + "chain": "FSN", + "icon": "fusion", + "rpc": ["https://mainnet.fusionnetwork.io", "wss://mainnet.fusionnetwork.io"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Fusion", "symbol": "FSN", "decimals": 18 }, + "infoURL": "https://fusion.org", + "shortName": "fsn", + "chainId": 32659, + "networkId": 32659, + "slip44": 288, + "explorers": [ + { "name": "fsnscan", "url": "https://fsnscan.com", "icon": "fsnscan", "standard": "EIP3091" } + ] + }, + { + "name": "Zilliqa EVM Testnet", + "chain": "ZIL", + "rpc": ["https://dev-api.zilliqa.com"], + "faucets": ["https://dev-wallet.zilliqa.com/faucet?network=testnet"], + "nativeCurrency": { "name": "Zilliqa", "symbol": "ZIL", "decimals": 18 }, + "infoURL": "https://www.zilliqa.com/", + "shortName": "zil-testnet", + "chainId": 33101, + "networkId": 33101, + "explorers": [ + { "name": "Zilliqa EVM Explorer", "url": "https://evmx.zilliqa.com", "standard": "none" } + ] + }, + { + "name": "Aves Mainnet", + "chain": "AVS", + "rpc": ["https://rpc.avescoin.io"], + "faucets": [], + "nativeCurrency": { "name": "Aves", "symbol": "AVS", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://avescoin.io", + "shortName": "avs", + "chainId": 33333, + "networkId": 33333, + "icon": "aves", + "explorers": [ + { "name": "avescan", "url": "https://avescan.io", "icon": "avescan", "standard": "EIP3091" } + ] + }, + { + "name": "J2O Taro", + "chain": "TARO", + "rpc": ["https://rpc.j2o.io"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "TARO Coin", "symbol": "taro", "decimals": 18 }, + "infoURL": "https://j2o.io", + "shortName": "j2o", + "chainId": 35011, + "networkId": 35011, + "explorers": [ + { + "name": "J2O Taro Explorer", + "url": "https://exp.j2o.io", + "icon": "j2otaro", + "standard": "EIP3091" + } + ] + }, + { + "name": "Q Mainnet", + "chain": "Q", + "rpc": ["https://rpc.q.org"], + "faucets": [], + "nativeCurrency": { "name": "Q token", "symbol": "Q", "decimals": 18 }, + "infoURL": "https://q.org", + "shortName": "q", + "chainId": 35441, + "networkId": 35441, + "icon": "q", + "explorers": [ + { "name": "Q explorer", "url": "https://explorer.q.org", "icon": "q", "standard": "EIP3091" } + ] + }, + { + "name": "Q Testnet", + "chain": "Q", + "rpc": ["https://rpc.qtestnet.org"], + "faucets": [], + "nativeCurrency": { "name": "Q token", "symbol": "Q", "decimals": 18 }, + "infoURL": "https://q.org/", + "shortName": "q-testnet", + "chainId": 35443, + "networkId": 35443, + "icon": "q", + "explorers": [ + { + "name": "Q explorer", + "url": "https://explorer.qtestnet.org", + "icon": "q", + "standard": "EIP3091" + } + ] + }, + { + "name": "Energi Mainnet", + "chain": "NRG", + "rpc": ["https://nodeapi.energi.network"], + "faucets": [], + "nativeCurrency": { "name": "Energi", "symbol": "NRG", "decimals": 18 }, + "infoURL": "https://www.energi.world/", + "shortName": "nrg", + "chainId": 39797, + "networkId": 39797, + "slip44": 39797 + }, + { + "name": "OHO Mainnet", + "chain": "OHO", + "rpc": ["https://mainnet.oho.ai"], + "faucets": [], + "nativeCurrency": { "name": "OHO", "symbol": "OHO", "decimals": 18 }, + "infoURL": "https://oho.ai", + "shortName": "oho", + "chainId": 39815, + "networkId": 39815, + "icon": "oho", + "explorers": [ + { "name": "ohoscan", "url": "https://ohoscan.com", "icon": "ohoscan", "standard": "EIP3091" } + ] + }, + { + "name": "Opulent-X BETA", + "chainId": 41500, + "shortName": "ox-beta", + "chain": "Opulent-X", + "networkId": 41500, + "nativeCurrency": { "name": "Oxyn Gas", "symbol": "OXYN", "decimals": 18 }, + "rpc": ["https://connect.opulent-x.com"], + "faucets": [], + "infoURL": "https://beta.opulent-x.com", + "explorers": [ + { + "name": "Opulent-X BETA Explorer", + "url": "https://explorer.opulent-x.com", + "standard": "none" + } + ] + }, + { + "name": "pegglecoin", + "chain": "42069", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "pegglecoin", "symbol": "peggle", "decimals": 18 }, + "infoURL": "https://teampeggle.com", + "shortName": "PC", + "chainId": 42069, + "networkId": 42069 + }, + { + "name": "Arbitrum One", + "chainId": 42161, + "shortName": "arb1", + "chain": "ETH", + "networkId": 42161, + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "rpc": [ + "https://arbitrum-mainnet.infura.io/v3/${INFURA_API_KEY}", + "https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}", + "https://arb1.arbitrum.io/rpc" + ], + "faucets": [], + "explorers": [ + { "name": "Arbiscan", "url": "https://arbiscan.io", "standard": "EIP3091" }, + { "name": "Arbitrum Explorer", "url": "https://explorer.arbitrum.io", "standard": "EIP3091" } + ], + "infoURL": "https://arbitrum.io", + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.arbitrum.io" }] + } + }, + { + "name": "Arbitrum Nova", + "chainId": 42170, + "shortName": "arb-nova", + "chain": "ETH", + "networkId": 42170, + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "rpc": ["https://nova.arbitrum.io/rpc"], + "faucets": [], + "explorers": [ + { + "name": "Arbitrum Nova Chain Explorer", + "url": "https://nova-explorer.arbitrum.io", + "icon": "blockscout", + "standard": "EIP3091" + } + ], + "infoURL": "https://arbitrum.io", + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://bridge.arbitrum.io" }] + } + }, + { + "name": "Celo Mainnet", + "chainId": 42220, + "shortName": "celo", + "chain": "CELO", + "networkId": 42220, + "nativeCurrency": { "name": "CELO", "symbol": "CELO", "decimals": 18 }, + "rpc": ["https://forno.celo.org", "wss://forno.celo.org/ws"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "infoURL": "https://docs.celo.org/", + "explorers": [ + { "name": "Celoscan", "url": "https://celoscan.io", "standard": "EIP3091" }, + { "name": "blockscout", "url": "https://explorer.celo.org", "standard": "none" } + ] + }, + { + "name": "Oasis Emerald Testnet", + "chain": "Emerald", + "icon": "oasis", + "rpc": ["https://testnet.emerald.oasis.dev/", "wss://testnet.emerald.oasis.dev/ws"], + "faucets": ["https://faucet.testnet.oasis.dev/"], + "nativeCurrency": { "name": "Emerald Rose", "symbol": "ROSE", "decimals": 18 }, + "infoURL": "https://docs.oasis.io/dapp/emerald", + "shortName": "emerald-testnet", + "chainId": 42261, + "networkId": 42261, + "explorers": [ + { + "name": "Oasis Emerald Testnet Explorer", + "url": "https://testnet.explorer.emerald.oasis.dev", + "standard": "EIP3091" + } + ] + }, + { + "name": "Oasis Emerald", + "chain": "Emerald", + "icon": "oasis", + "rpc": ["https://emerald.oasis.dev", "wss://emerald.oasis.dev/ws"], + "faucets": [], + "nativeCurrency": { "name": "Emerald Rose", "symbol": "ROSE", "decimals": 18 }, + "infoURL": "https://docs.oasis.io/dapp/emerald", + "shortName": "emerald", + "chainId": 42262, + "networkId": 42262, + "explorers": [ + { + "name": "Oasis Emerald Explorer", + "url": "https://explorer.emerald.oasis.dev", + "standard": "EIP3091" + } + ] + }, + { + "name": "Athereum", + "chain": "ATH", + "rpc": ["https://ava.network:21015/ext/evm/rpc"], + "faucets": ["http://athfaucet.ava.network//?address=${ADDRESS}"], + "nativeCurrency": { "name": "Athereum Ether", "symbol": "ATH", "decimals": 18 }, + "infoURL": "https://athereum.ava.network", + "shortName": "avaeth", + "chainId": 43110, + "networkId": 43110 + }, + { + "name": "Avalanche Fuji Testnet", + "chain": "AVAX", + "icon": "avax", + "rpc": ["https://api.avax-test.network/ext/bc/C/rpc"], + "faucets": ["https://faucet.avax-test.network/"], + "nativeCurrency": { "name": "Avalanche", "symbol": "AVAX", "decimals": 18 }, + "infoURL": "https://cchain.explorer.avax-test.network", + "shortName": "Fuji", + "chainId": 43113, + "networkId": 1, + "explorers": [ + { "name": "snowtrace", "url": "https://testnet.snowtrace.io", "standard": "EIP3091" } + ] + }, + { + "name": "Avalanche C-Chain", + "chain": "AVAX", + "icon": "avax", + "rpc": ["https://api.avax.network/ext/bc/C/rpc", "https://avalanche-c-chain.publicnode.com"], + "features": [{ "name": "EIP1559" }], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "Avalanche", "symbol": "AVAX", "decimals": 18 }, + "infoURL": "https://www.avax.network/", + "shortName": "avax", + "chainId": 43114, + "networkId": 43114, + "slip44": 9005, + "explorers": [{ "name": "snowtrace", "url": "https://snowtrace.io", "standard": "EIP3091" }] + }, + { + "name": "Boba Avax", + "chain": "Boba Avax", + "rpc": [ + "https://avax.boba.network", + "wss://wss.avax.boba.network", + "https://replica.avax.boba.network", + "wss://replica-wss.avax.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://docs.boba.network/for-developers/network-avalanche", + "shortName": "bobaavax", + "chainId": 43288, + "networkId": 43288, + "explorers": [ + { + "name": "Boba Avax Explorer", + "url": "https://blockexplorer.avax.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Frenchain", + "chain": "fren", + "rpc": ["https://rpc-02.frenscan.io"], + "faucets": [], + "nativeCurrency": { "name": "FREN", "symbol": "FREN", "decimals": 18 }, + "infoURL": "https://frenchain.app", + "shortName": "FREN", + "chainId": 44444, + "networkId": 44444, + "icon": "fren", + "explorers": [ + { "name": "blockscout", "url": "https://frenscan.io", "icon": "fren", "standard": "EIP3091" } + ] + }, + { + "name": "Celo Alfajores Testnet", + "chainId": 44787, + "shortName": "ALFA", + "chain": "CELO", + "networkId": 44787, + "nativeCurrency": { "name": "CELO", "symbol": "CELO", "decimals": 18 }, + "rpc": [ + "https://alfajores-forno.celo-testnet.org", + "wss://alfajores-forno.celo-testnet.org/ws" + ], + "faucets": [ + "https://celo.org/developers/faucet", + "https://cauldron.pretoriaresearchlab.io/alfajores-faucet" + ], + "infoURL": "https://docs.celo.org/", + "explorers": [{ "name": "Celoscan", "url": "https://celoscan.io", "standard": "EIP3091" }] + }, + { + "name": "Autobahn Network", + "chain": "TXL", + "rpc": ["https://rpc.autobahn.network"], + "faucets": [], + "nativeCurrency": { "name": "TXL", "symbol": "TXL", "decimals": 18 }, + "infoURL": "https://autobahn.network", + "shortName": "AutobahnNetwork", + "chainId": 45000, + "networkId": 45000, + "icon": "autobahn", + "explorers": [ + { + "name": "autobahn explorer", + "url": "https://explorer.autobahn.network", + "icon": "autobahn", + "standard": "EIP3091" + } + ] + }, + { + "name": "Fusion Testnet", + "chain": "FSN", + "icon": "fusion", + "rpc": ["https://testnet.fusionnetwork.io", "wss://testnet.fusionnetwork.io"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Testnet Fusion", "symbol": "T-FSN", "decimals": 18 }, + "infoURL": "https://fusion.org", + "shortName": "tfsn", + "chainId": 46688, + "networkId": 46688, + "slip44": 288, + "explorers": [ + { + "name": "fsnscan", + "url": "https://testnet.fsnscan.com", + "icon": "fsnscan", + "standard": "EIP3091" + } + ] + }, + { + "name": "REI Network", + "chain": "REI", + "rpc": ["https://rpc.rei.network", "wss://rpc.rei.network"], + "faucets": [], + "nativeCurrency": { "name": "REI", "symbol": "REI", "decimals": 18 }, + "infoURL": "https://rei.network/", + "shortName": "REI", + "chainId": 47805, + "networkId": 47805, + "explorers": [{ "name": "rei-scan", "url": "https://scan.rei.network", "standard": "none" }] + }, + { + "name": "Floripa", + "title": "Wireshape Testnet Floripa", + "chain": "Wireshape", + "rpc": ["https://rpc-floripa.wireshape.org"], + "faucets": [], + "nativeCurrency": { "name": "WIRE", "symbol": "WIRE", "decimals": 18 }, + "infoURL": "https://wireshape.org", + "shortName": "floripa", + "chainId": 49049, + "networkId": 49049, + "explorers": [ + { + "name": "Wire Explorer", + "url": "https://floripa-explorer.wireshape.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Bifrost Testnet", + "title": "The Bifrost Testnet network", + "chain": "BFC", + "rpc": [ + "https://public-01.testnet.thebifrost.io/rpc", + "https://public-02.testnet.thebifrost.io/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "Bifrost", "symbol": "BFC", "decimals": 18 }, + "infoURL": "https://thebifrost.io", + "shortName": "tbfc", + "chainId": 49088, + "networkId": 49088, + "icon": "bifrost", + "explorers": [ + { + "name": "explorer-thebifrost", + "url": "https://explorer.testnet.thebifrost.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Energi Testnet", + "chain": "NRG", + "rpc": ["https://nodeapi.test.energi.network"], + "faucets": [], + "nativeCurrency": { "name": "Energi", "symbol": "NRG", "decimals": 18 }, + "infoURL": "https://www.energi.world/", + "shortName": "tnrg", + "chainId": 49797, + "networkId": 49797, + "slip44": 49797 + }, + { + "name": "Liveplex OracleEVM", + "chain": "Liveplex OracleEVM Network", + "rpc": ["https://rpc.oracle.liveplex.io"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "", + "shortName": "LOE", + "chainId": 50001, + "networkId": 50001, + "explorers": [] + }, + { + "name": "GTON Testnet", + "chain": "GTON Testnet", + "rpc": ["https://testnet.gton.network/"], + "faucets": [], + "nativeCurrency": { "name": "GCD", "symbol": "GCD", "decimals": 18 }, + "infoURL": "https://gton.capital", + "shortName": "tgton", + "chainId": 50021, + "networkId": 50021, + "explorers": [ + { + "name": "GTON Testnet Network Explorer", + "url": "https://explorer.testnet.gton.network", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-3" } + }, + { + "name": "Sardis Mainnet", + "chain": "SRDX", + "icon": "sardis", + "rpc": ["https://mainnet-rpc.sardisnetwork.com"], + "faucets": ["https://faucet.sardisnetwork.com"], + "nativeCurrency": { "name": "Sardis", "symbol": "SRDX", "decimals": 18 }, + "infoURL": "https://mysardis.com", + "shortName": "SRDXm", + "chainId": 51712, + "networkId": 51712, + "explorers": [ + { + "name": "Sardis", + "url": "https://contract-mainnet.sardisnetwork.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "DFK Chain", + "chain": "DFK", + "icon": "dfk", + "rpc": ["https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Jewel", "symbol": "JEWEL", "decimals": 18 }, + "infoURL": "https://defikingdoms.com", + "shortName": "DFK", + "chainId": 53935, + "networkId": 53935, + "explorers": [ + { + "name": "ethernal", + "url": "https://explorer.dfkchain.com", + "icon": "ethereum", + "standard": "none" + } + ] + }, + { + "name": "Haqq Chain Testnet", + "chain": "TestEdge2", + "rpc": ["https://rpc.eth.testedge2.haqq.network"], + "faucets": ["https://testedge2.haqq.network"], + "nativeCurrency": { "name": "Islamic Coin", "symbol": "ISLMT", "decimals": 18 }, + "infoURL": "https://islamiccoin.net", + "shortName": "ISLMT", + "chainId": 54211, + "networkId": 54211, + "explorers": [ + { + "name": "TestEdge HAQQ Explorer", + "url": "https://explorer.testedge2.haqq.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "REI Chain Mainnet", + "chain": "REI", + "icon": "reichain", + "rpc": ["https://rei-rpc.moonrhythm.io"], + "faucets": ["http://kururu.finance/faucet?chainId=55555"], + "nativeCurrency": { "name": "Rei", "symbol": "REI", "decimals": 18 }, + "infoURL": "https://reichain.io", + "shortName": "reichain", + "chainId": 55555, + "networkId": 55555, + "explorers": [{ "name": "reiscan", "url": "https://reiscan.com", "standard": "EIP3091" }] + }, + { + "name": "REI Chain Testnet", + "chain": "REI", + "icon": "reichain", + "rpc": ["https://rei-testnet-rpc.moonrhythm.io"], + "faucets": ["http://kururu.finance/faucet?chainId=55556"], + "nativeCurrency": { "name": "tRei", "symbol": "tREI", "decimals": 18 }, + "infoURL": "https://reichain.io", + "shortName": "trei", + "chainId": 55556, + "networkId": 55556, + "explorers": [ + { "name": "reiscan", "url": "https://testnet.reiscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Boba BNB Mainnet", + "chain": "Boba BNB Mainnet", + "rpc": [ + "https://bnb.boba.network", + "wss://wss.bnb.boba.network", + "https://replica.bnb.boba.network", + "wss://replica-wss.bnb.boba.network" + ], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaBnb", + "chainId": 56288, + "networkId": 56288, + "explorers": [ + { + "name": "Boba BNB block explorer", + "url": "https://blockexplorer.bnb.boba.network", + "standard": "none" + } + ] + }, + { + "name": "Syscoin Rollux Testnet", + "chain": "SYS", + "rpc": ["https://rpc-tanenbaum.rollux.com", "wss://rpc-tanenbaum.rollux.com/wss"], + "faucets": [], + "nativeCurrency": { "name": "Rollux Testnet Syscoin", "symbol": "tSYS", "decimals": 18 }, + "infoURL": "https://syscoin.org", + "shortName": "tsys-rollux", + "chainId": 57000, + "networkId": 57000, + "explorers": [ + { + "name": "Syscoin Rollux Testnet Explorer", + "url": "https://rollux.tanenbaum.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "Linea Testnet", + "title": "Linea Testnet", + "chain": "ETH", + "rpc": ["https://rpc.goerli.linea.build"], + "faucets": ["https://faucetlink.to/goerli"], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://linea.build", + "shortName": "linea-testnet", + "chainId": 59140, + "networkId": 59140, + "icon": "linea", + "parent": { + "type": "L2", + "chain": "eip155-5", + "bridges": [ + { + "url": "https://goerli.hop.exchange/#/send?token=ETH&sourceNetwork=ethereum&destNetwork=linea" + } + ] + }, + "explorers": [ + { + "name": "Linea Testnet Explorer", + "url": "https://explorer.goerli.linea.build", + "standard": "EIP3091", + "icon": "linea" + } + ], + "status": "active" + }, + { + "name": "Thinkium Testnet Chain 0", + "chain": "Thinkium", + "rpc": ["https://test.thinkiumrpc.net/"], + "faucets": ["https://www.thinkiumdev.net/faucet"], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM-test0", + "chainId": 60000, + "networkId": 60000, + "explorers": [ + { "name": "thinkiumscan", "url": "https://test0.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Testnet Chain 1", + "chain": "Thinkium", + "rpc": ["https://test1.thinkiumrpc.net/"], + "faucets": ["https://www.thinkiumdev.net/faucet"], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM-test1", + "chainId": 60001, + "networkId": 60001, + "explorers": [ + { "name": "thinkiumscan", "url": "https://test1.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Testnet Chain 2", + "chain": "Thinkium", + "rpc": ["https://test2.thinkiumrpc.net/"], + "faucets": ["https://www.thinkiumdev.net/faucet"], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM-test2", + "chainId": 60002, + "networkId": 60002, + "explorers": [ + { "name": "thinkiumscan", "url": "https://test2.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Testnet Chain 103", + "chain": "Thinkium", + "rpc": ["https://test103.thinkiumrpc.net/"], + "faucets": ["https://www.thinkiumdev.net/faucet"], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM-test103", + "chainId": 60103, + "networkId": 60103, + "explorers": [ + { "name": "thinkiumscan", "url": "https://test103.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Etica Mainnet", + "chain": "Etica Protocol (ETI/EGAZ)", + "icon": "etica", + "rpc": ["https://eticamainnet.eticascan.org", "https://eticamainnet.eticaprotocol.org"], + "faucets": ["http://faucet.etica-stats.org/"], + "nativeCurrency": { "name": "EGAZ", "symbol": "EGAZ", "decimals": 18 }, + "infoURL": "https://eticaprotocol.org", + "shortName": "Etica", + "chainId": 61803, + "networkId": 61803, + "explorers": [ + { "name": "eticascan", "url": "https://eticascan.org", "standard": "EIP3091" }, + { "name": "eticastats", "url": "http://explorer.etica-stats.org", "standard": "EIP3091" } + ] + }, + { + "name": "DoKEN Super Chain Mainnet", + "chain": "DoKEN Super Chain", + "rpc": ["https://sgrpc.doken.dev", "https://nyrpc.doken.dev", "https://ukrpc.doken.dev"], + "faucets": [], + "nativeCurrency": { "name": "DoKEN", "symbol": "DKN", "decimals": 18 }, + "infoURL": "https://doken.dev/", + "shortName": "DoKEN", + "chainId": 61916, + "networkId": 61916, + "icon": "doken", + "explorers": [ + { + "name": "DSC Scan", + "url": "https://explore.doken.dev", + "icon": "doken", + "standard": "EIP3091" + } + ] + }, + { + "name": "Celo Baklava Testnet", + "chainId": 62320, + "shortName": "BKLV", + "chain": "CELO", + "networkId": 62320, + "nativeCurrency": { "name": "CELO", "symbol": "CELO", "decimals": 18 }, + "rpc": ["https://baklava-forno.celo-testnet.org"], + "faucets": [ + "https://docs.google.com/forms/d/e/1FAIpQLSdfr1BwUTYepVmmvfVUDRCwALejZ-TUva2YujNpvrEmPAX2pg/viewform", + "https://cauldron.pretoriaresearchlab.io/baklava-faucet" + ], + "infoURL": "https://docs.celo.org/" + }, + { + "name": "MultiVAC Mainnet", + "chain": "MultiVAC", + "icon": "multivac", + "rpc": ["https://rpc.mtv.ac", "https://rpc-eu.mtv.ac"], + "faucets": [], + "nativeCurrency": { "name": "MultiVAC", "symbol": "MTV", "decimals": 18 }, + "infoURL": "https://mtv.ac", + "shortName": "mtv", + "chainId": 62621, + "networkId": 62621, + "explorers": [{ "name": "MultiVAC Explorer", "url": "https://e.mtv.ac", "standard": "none" }] + }, + { + "name": "eCredits Mainnet", + "chain": "ECS", + "rpc": ["https://rpc.ecredits.com"], + "faucets": [], + "nativeCurrency": { "name": "eCredits", "symbol": "ECS", "decimals": 18 }, + "infoURL": "https://ecredits.com", + "shortName": "ecs", + "chainId": 63000, + "networkId": 63000, + "icon": "ecredits", + "explorers": [ + { + "name": "eCredits MainNet Explorer", + "url": "https://explorer.ecredits.com", + "icon": "ecredits", + "standard": "EIP3091" + } + ] + }, + { + "name": "eCredits Testnet", + "chain": "ECS", + "rpc": ["https://rpc.tst.ecredits.com"], + "faucets": ["https://faucet.tst.ecredits.com"], + "nativeCurrency": { "name": "eCredits", "symbol": "ECS", "decimals": 18 }, + "infoURL": "https://ecredits.com", + "shortName": "ecs-testnet", + "chainId": 63001, + "networkId": 63001, + "icon": "ecredits", + "explorers": [ + { + "name": "eCredits TestNet Explorer", + "url": "https://explorer.tst.ecredits.com", + "icon": "ecredits", + "standard": "EIP3091" + } + ] + }, + { + "name": "Scolcoin Mainnet", + "chain": "SCOLWEI", + "rpc": ["https://mainnet-rpc.scolcoin.com"], + "faucets": [], + "nativeCurrency": { "name": "Scolcoin", "symbol": "SCOL", "decimals": 18 }, + "infoURL": "https://scolcoin.com", + "shortName": "SRC", + "chainId": 65450, + "networkId": 65450, + "icon": "scolcoin", + "explorers": [ + { "name": "Scolscan Explorer", "url": "https://explorer.scolcoin.com", "standard": "EIP3091" } + ] + }, + { + "name": "SiriusNet", + "chain": "SIN", + "status": "deprecated", + "rpc": [ + "https://u0tnafcv6j:o2T045sxuCNXL878RDQLp5__Zj-es2cvdjtgkl4etn0@u0v7kwtvtg-u0wj114sve-rpc.us0-aws.kaleido.io/" + ], + "faucets": [], + "nativeCurrency": { "name": "MCD", "symbol": "MCD", "decimals": 18 }, + "infoURL": "https://macaucasinolisboa.xyz", + "shortName": "mcl", + "chainId": 67390, + "networkId": 67390, + "explorers": [ + { "name": "siriusnetscan", "url": "https://siriusnet.tryethernal.com", "standard": "EIP3091" } + ] + }, + { + "name": "Condrieu", + "title": "Ethereum Verkle Testnet Condrieu", + "chain": "ETH", + "rpc": ["https://rpc.condrieu.ethdevops.io:8545"], + "faucets": ["https://faucet.condrieu.ethdevops.io"], + "nativeCurrency": { "name": "Condrieu Testnet Ether", "symbol": "CTE", "decimals": 18 }, + "infoURL": "https://condrieu.ethdevops.io", + "shortName": "cndr", + "chainId": 69420, + "networkId": 69420, + "explorers": [ + { + "name": "Condrieu explorer", + "url": "https://explorer.condrieu.ethdevops.io", + "standard": "none" + } + ] + }, + { + "name": "Thinkium Mainnet Chain 0", + "chain": "Thinkium", + "rpc": ["https://proxy.thinkiumrpc.net/"], + "faucets": [], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM0", + "chainId": 70000, + "networkId": 70000, + "explorers": [ + { "name": "thinkiumscan", "url": "https://chain0.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Mainnet Chain 1", + "chain": "Thinkium", + "rpc": ["https://proxy1.thinkiumrpc.net/"], + "faucets": [], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM1", + "chainId": 70001, + "networkId": 70001, + "explorers": [ + { "name": "thinkiumscan", "url": "https://chain1.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Mainnet Chain 2", + "chain": "Thinkium", + "rpc": ["https://proxy2.thinkiumrpc.net/"], + "faucets": [], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM2", + "chainId": 70002, + "networkId": 70002, + "explorers": [ + { "name": "thinkiumscan", "url": "https://chain2.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Thinkium Mainnet Chain 103", + "chain": "Thinkium", + "rpc": ["https://proxy103.thinkiumrpc.net/"], + "faucets": [], + "nativeCurrency": { "name": "TKM", "symbol": "TKM", "decimals": 18 }, + "infoURL": "https://thinkium.net/", + "shortName": "TKM103", + "chainId": 70103, + "networkId": 70103, + "explorers": [ + { "name": "thinkiumscan", "url": "https://chain103.thinkiumscan.net", "standard": "EIP3091" } + ] + }, + { + "name": "Polyjuice Testnet", + "chain": "CKB", + "icon": "polyjuice", + "rpc": [ + "https://godwoken-testnet-web3-rpc.ckbapp.dev", + "ws://godwoken-testnet-web3-rpc.ckbapp.dev/ws" + ], + "faucets": ["https://faucet.nervos.org/"], + "nativeCurrency": { "name": "CKB", "symbol": "CKB", "decimals": 8 }, + "infoURL": "https://github.com/nervosnetwork/godwoken", + "shortName": "ckb", + "chainId": 71393, + "networkId": 1 + }, + { + "name": "Godwoken Testnet v1", + "chain": "GWT", + "rpc": ["https://godwoken-testnet-v1.ckbapp.dev", "https://v1.testnet.godwoken.io/rpc"], + "faucets": ["https://testnet.bridge.godwoken.io"], + "nativeCurrency": { "name": "pCKB", "symbol": "pCKB", "decimals": 18 }, + "infoURL": "https://www.nervos.org", + "shortName": "gw-testnet-v1", + "chainId": 71401, + "networkId": 71401, + "explorers": [ + { + "name": "GWScout Explorer", + "url": "https://gw-testnet-explorer.nervosdao.community", + "standard": "none" + }, + { + "name": "GWScan Block Explorer", + "url": "https://v1.testnet.gwscan.com", + "standard": "none" + } + ] + }, + { + "name": "Godwoken Mainnet", + "chain": "GWT", + "rpc": ["https://v1.mainnet.godwoken.io/rpc"], + "faucets": [], + "nativeCurrency": { "name": "pCKB", "symbol": "pCKB", "decimals": 18 }, + "infoURL": "https://www.nervos.org", + "shortName": "gw-mainnet-v1", + "chainId": 71402, + "networkId": 71402, + "explorers": [ + { + "name": "GWScout Explorer", + "url": "https://gw-mainnet-explorer.nervosdao.community", + "standard": "none" + }, + { "name": "GWScan Block Explorer", "url": "https://v1.gwscan.com", "standard": "none" } + ] + }, + { + "name": "Energy Web Volta Testnet", + "chain": "Volta", + "rpc": ["https://volta-rpc.energyweb.org", "wss://volta-rpc.energyweb.org/ws"], + "faucets": ["https://voltafaucet.energyweb.org"], + "nativeCurrency": { "name": "Volta Token", "symbol": "VT", "decimals": 18 }, + "infoURL": "https://energyweb.org", + "shortName": "vt", + "chainId": 73799, + "networkId": 73799 + }, + { + "name": "Mixin Virtual Machine", + "chain": "MVM", + "rpc": ["https://geth.mvm.dev"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://mvm.dev", + "shortName": "mvm", + "chainId": 73927, + "networkId": 73927, + "icon": "mvm", + "explorers": [ + { "name": "mvmscan", "url": "https://scan.mvm.dev", "icon": "mvm", "standard": "EIP3091" } + ] + }, + { + "name": "ResinCoin Mainnet", + "chain": "RESIN", + "icon": "resincoin", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "RESIN", "decimals": 18 }, + "infoURL": "https://resincoin.dev", + "shortName": "resin", + "chainId": 75000, + "networkId": 75000, + "explorers": [ + { "name": "ResinScan", "url": "https://explorer.resincoin.dev", "standard": "none" } + ] + }, + { + "name": "Vention Smart Chain Mainnet", + "chain": "VSC", + "icon": "vention", + "rpc": ["https://mainnet-rpc.vention.network"], + "faucets": ["https://faucet.vention.network"], + "nativeCurrency": { "name": "VNT", "symbol": "VNT", "decimals": 18 }, + "infoURL": "https://ventionscan.io", + "shortName": "vscm", + "chainId": 77612, + "networkId": 77612, + "explorers": [{ "name": "ventionscan", "url": "https://ventionscan.io", "standard": "EIP3091" }] + }, + { + "name": "Firenze test network", + "chain": "ETH", + "rpc": ["https://ethnode.primusmoney.com/firenze"], + "faucets": [], + "nativeCurrency": { "name": "Firenze Ether", "symbol": "FIN", "decimals": 18 }, + "infoURL": "https://primusmoney.com", + "shortName": "firenze", + "chainId": 78110, + "networkId": 78110 + }, + { + "name": "Gold Smart Chain Testnet", + "chain": "STAND", + "icon": "standTestnet", + "rpc": ["https://rpc-testnet.goldsmartchain.com"], + "faucets": ["https://faucet.goldsmartchain.com"], + "nativeCurrency": { "name": "Standard in Gold", "symbol": "STAND", "decimals": 18 }, + "infoURL": "https://goldsmartchain.com", + "shortName": "STANDt", + "chainId": 79879, + "networkId": 79879, + "explorers": [ + { + "name": "Gold Smart Chain", + "url": "https://testnet.goldsmartchain.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "Mumbai", + "title": "Polygon Testnet Mumbai", + "chain": "Polygon", + "icon": "polygon", + "rpc": [ + "https://matic-mumbai.chainstacklabs.com", + "https://rpc-mumbai.maticvigil.com", + "https://matic-testnet-archive-rpc.bwarelabs.com" + ], + "faucets": ["https://faucet.polygon.technology/"], + "nativeCurrency": { "name": "MATIC", "symbol": "MATIC", "decimals": 18 }, + "infoURL": "https://polygon.technology/", + "shortName": "maticmum", + "chainId": 80001, + "networkId": 80001, + "explorers": [ + { "name": "polygonscan", "url": "https://mumbai.polygonscan.com", "standard": "EIP3091" } + ] + }, + { + "name": "Amana Testnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Amana Testnet", "symbol": "MEER-T", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "amanatest", + "icon": "meer", + "chainId": 81341, + "networkId": 81341, + "status": "incubating" + }, + { + "name": "Amana Mixnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Amana Mixnet", "symbol": "MEER-M", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "amanamix", + "icon": "meer", + "chainId": 81342, + "networkId": 81342, + "status": "incubating" + }, + { + "name": "Amana Privnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Amana Privnet", "symbol": "MEER-P", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "amanapriv", + "icon": "meer", + "chainId": 81343, + "networkId": 81343, + "status": "incubating" + }, + { + "name": "Flana Testnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Flana Testnet", "symbol": "MEER-T", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "flanatest", + "icon": "meer", + "chainId": 81351, + "networkId": 81351, + "status": "incubating" + }, + { + "name": "Flana Mixnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Flana Mixnet", "symbol": "MEER-M", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "flanamix", + "icon": "meer", + "chainId": 81352, + "networkId": 81352, + "status": "incubating" + }, + { + "name": "Flana Privnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Flana Privnet", "symbol": "MEER-P", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "flanapriv", + "icon": "meer", + "chainId": 81353, + "networkId": 81353, + "status": "incubating" + }, + { + "name": "Mizana Testnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Mizana Testnet", "symbol": "MEER-T", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "mizanatest", + "icon": "meer", + "chainId": 81361, + "networkId": 81361, + "status": "incubating" + }, + { + "name": "Mizana Mixnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Mizana Mixnet", "symbol": "MEER-M", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "mizanamix", + "icon": "meer", + "chainId": 81362, + "networkId": 81362, + "status": "incubating" + }, + { + "name": "Mizana Privnet", + "chain": "MEER", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Mizana Privnet", "symbol": "MEER-P", "decimals": 18 }, + "infoURL": "https://github.com/Qitmeer", + "shortName": "mizanapriv", + "icon": "meer", + "chainId": 81363, + "networkId": 81363, + "status": "incubating" + }, + { + "name": "Base Goerli Testnet", + "chain": "ETH", + "rpc": ["https://goerli.base.org"], + "faucets": ["https://www.coinbase.com/faucets/base-ethereum-goerli-faucet"], + "nativeCurrency": { "name": "Goerli Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://base.org", + "shortName": "basegor", + "chainId": 84531, + "networkId": 84531, + "explorers": [ + { "name": "basescan", "url": "https://goerli.basescan.org", "standard": "none" }, + { "name": "basescout", "url": "https://base-goerli.blockscout.com", "standard": "none" } + ] + }, + { + "name": "CYBERTRUST", + "chain": "CYBER", + "rpc": ["http://testnet.cybertrust.space:48501"], + "faucets": [], + "nativeCurrency": { "name": "Cyber Trust", "symbol": "CYBER", "decimals": 18 }, + "infoURL": "https://cybertrust.space", + "shortName": "Cyber", + "chainId": 85449, + "networkId": 48501 + }, + { + "name": "Chiliz Scoville Testnet", + "chain": "CHZ", + "rpc": ["https://scoville-rpc.chiliz.com"], + "faucets": ["https://scoville-faucet.chiliz.com"], + "nativeCurrency": { "name": "Chiliz", "symbol": "CHZ", "decimals": 18 }, + "icon": "chiliz", + "infoURL": "https://www.chiliz.com/en/chain", + "shortName": "chz", + "chainId": 88880, + "networkId": 88880, + "explorers": [ + { + "name": "scoville-explorer", + "url": "https://scoville-explorer.chiliz.com", + "standard": "none" + } + ] + }, + { + "name": "IVAR Chain Mainnet", + "chain": "IVAR", + "icon": "ivar", + "rpc": ["https://mainnet-rpc.ivarex.com"], + "faucets": ["https://faucet.ivarex.com/"], + "nativeCurrency": { "name": "Ivar", "symbol": "IVAR", "decimals": 18 }, + "infoURL": "https://ivarex.com", + "shortName": "ivar", + "chainId": 88888, + "networkId": 88888, + "explorers": [{ "name": "ivarscan", "url": "https://ivarscan.com", "standard": "EIP3091" }] + }, + { + "name": "Beverly Hills", + "title": "Ethereum multi-client Verkle Testnet Beverly Hills", + "chain": "ETH", + "rpc": ["https://rpc.beverlyhills.ethdevops.io:8545"], + "faucets": ["https://faucet.beverlyhills.ethdevops.io"], + "nativeCurrency": { "name": "Beverly Hills Testnet Ether", "symbol": "BVE", "decimals": 18 }, + "infoURL": "https://beverlyhills.ethdevops.io", + "shortName": "bvhl", + "chainId": 90210, + "networkId": 90210, + "status": "incubating", + "explorers": [ + { + "name": "Beverly Hills explorer", + "url": "https://explorer.beverlyhills.ethdevops.io", + "standard": "none" + } + ] + }, + { + "name": "Lambda Testnet", + "chain": "Lambda", + "rpc": ["https://evm.lambda.top/"], + "faucets": ["https://faucet.lambda.top"], + "nativeCurrency": { "name": "test-Lamb", "symbol": "LAMB", "decimals": 18 }, + "infoURL": "https://lambda.im", + "shortName": "lambda-testnet", + "chainId": 92001, + "networkId": 92001, + "icon": "lambda", + "explorers": [ + { + "name": "Lambda EVM Explorer", + "url": "https://explorer.lambda.top", + "standard": "EIP3091", + "icon": "lambda" + } + ] + }, + { + "name": "Boba BNB Mainnet Old", + "chain": "Boba BNB Mainnet", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Boba Token", "symbol": "BOBA", "decimals": 18 }, + "infoURL": "https://boba.network", + "shortName": "BobaBnbOld", + "chainId": 97288, + "networkId": 97288, + "explorers": [ + { + "name": "Boba BNB block explorer", + "url": "https://blockexplorer.bnb.boba.network", + "standard": "none" + } + ], + "status": "deprecated" + }, + { + "name": "UB Smart Chain(testnet)", + "chain": "USC", + "rpc": ["https://testnet.rpc.uschain.network"], + "faucets": [], + "nativeCurrency": { "name": "UBC", "symbol": "UBC", "decimals": 18 }, + "infoURL": "https://www.ubchain.site", + "shortName": "usctest", + "chainId": 99998, + "networkId": 99998 + }, + { + "name": "UB Smart Chain", + "chain": "USC", + "rpc": ["https://rpc.uschain.network"], + "faucets": [], + "nativeCurrency": { "name": "UBC", "symbol": "UBC", "decimals": 18 }, + "infoURL": "https://www.ubchain.site/", + "shortName": "usc", + "chainId": 99999, + "networkId": 99999 + }, + { + "name": "QuarkChain Mainnet Root", + "chain": "QuarkChain", + "rpc": ["http://jrpc.mainnet.quarkchain.io:38391"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-r", + "chainId": 100000, + "networkId": 100000 + }, + { + "name": "QuarkChain Mainnet Shard 0", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s0-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39000" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s0", + "chainId": 100001, + "networkId": 100001, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/0", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 1", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s1-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39001" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s1", + "chainId": 100002, + "networkId": 100002, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/1", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 2", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s2-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39002" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s2", + "chainId": 100003, + "networkId": 100003, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/2", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 3", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s3-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39003" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s3", + "chainId": 100004, + "networkId": 100004, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/3", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 4", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s4-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39004" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s4", + "chainId": 100005, + "networkId": 100005, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/4", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 5", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s5-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39005" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s5", + "chainId": 100006, + "networkId": 100006, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/5", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 6", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s6-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39006" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s6", + "chainId": 100007, + "networkId": 100007, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/6", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Mainnet Shard 7", + "chain": "QuarkChain", + "rpc": [ + "https://mainnet-s7-ethapi.quarkchain.io", + "http://eth-jrpc.mainnet.quarkchain.io:39007" + ], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-s7", + "chainId": 100008, + "networkId": 100008, + "parent": { "chain": "eip155-100000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-mainnet", + "url": "https://mainnet.quarkchain.io/7", + "standard": "EIP3091" + } + ] + }, + { + "name": "VeChain", + "chain": "VeChain", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "VeChain", "symbol": "VET", "decimals": 18 }, + "infoURL": "https://vechain.org", + "shortName": "vechain", + "chainId": 100009, + "networkId": 100009, + "explorers": [ + { "name": "VeChain Stats", "url": "https://vechainstats.com", "standard": "none" }, + { "name": "VeChain Explorer", "url": "https://explore.vechain.org", "standard": "none" } + ] + }, + { + "name": "VeChain Testnet", + "chain": "VeChain", + "rpc": [], + "faucets": ["https://faucet.vecha.in"], + "nativeCurrency": { "name": "VeChain", "symbol": "VET", "decimals": 18 }, + "infoURL": "https://vechain.org", + "shortName": "vechain-testnet", + "chainId": 100010, + "networkId": 100010, + "explorers": [ + { + "name": "VeChain Explorer", + "url": "https://explore-testnet.vechain.org", + "standard": "none" + } + ] + }, + { + "name": "Deprecated Chiado Testnet", + "chain": "CHI1", + "icon": "gnosis", + "rpc": ["https://rpc-chiado.gnosistestnet.com"], + "faucets": ["https://gnosisfaucet.com"], + "nativeCurrency": { "name": "Chiado xDAI", "symbol": "xDAI", "decimals": 18 }, + "infoURL": "https://docs.gnosischain.com", + "shortName": "chi1", + "chainId": 100100, + "networkId": 100100, + "explorers": [ + { + "name": "blockscout", + "url": "https://blockscout-chiado.gnosistestnet.com", + "icon": "blockscout", + "standard": "EIP3091" + } + ], + "status": "deprecated" + }, + { + "name": "Soverun Testnet", + "chain": "SVRN", + "icon": "soverunTestnet", + "rpc": ["https://testnet-rpc.soverun.com"], + "faucets": ["https://faucet.soverun.com"], + "nativeCurrency": { "name": "Soverun", "symbol": "SVRN", "decimals": 18 }, + "infoURL": "https://soverun.com", + "shortName": "SVRNt", + "chainId": 101010, + "networkId": 101010, + "explorers": [ + { "name": "Soverun", "url": "https://testnet.soverun.com", "standard": "EIP3091" } + ] + }, + { + "name": "Crystaleum", + "chain": "crystal", + "rpc": ["https://evm.cryptocurrencydevs.org", "https://rpc.crystaleum.org"], + "faucets": [], + "nativeCurrency": { "name": "CRFI", "symbol": "◈", "decimals": 18 }, + "infoURL": "https://crystaleum.org", + "shortName": "CRFI", + "chainId": 103090, + "networkId": 1, + "icon": "crystal", + "explorers": [ + { + "name": "blockscout", + "url": "https://scan.crystaleum.org", + "icon": "crystal", + "standard": "EIP3091" + } + ] + }, + { + "name": "BROChain Mainnet", + "chain": "BRO", + "rpc": [ + "https://rpc.brochain.org", + "http://rpc.brochain.org", + "https://rpc.brochain.org/mainnet", + "http://rpc.brochain.org/mainnet" + ], + "faucets": [], + "nativeCurrency": { "name": "Brother", "symbol": "BRO", "decimals": 18 }, + "infoURL": "https://brochain.org", + "shortName": "bro", + "chainId": 108801, + "networkId": 108801, + "explorers": [ + { "name": "BROChain Explorer", "url": "https://explorer.brochain.org", "standard": "EIP3091" } + ] + }, + { + "name": "QuarkChain Devnet Root", + "chain": "QuarkChain", + "rpc": ["http://jrpc.devnet.quarkchain.io:38391"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-r", + "chainId": 110000, + "networkId": 110000 + }, + { + "name": "QuarkChain Devnet Shard 0", + "chain": "QuarkChain", + "rpc": ["https://devnet-s0-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39900"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s0", + "chainId": 110001, + "networkId": 110001, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/0", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 1", + "chain": "QuarkChain", + "rpc": ["https://devnet-s1-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39901"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s1", + "chainId": 110002, + "networkId": 110002, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/1", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 2", + "chain": "QuarkChain", + "rpc": ["https://devnet-s2-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39902"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s2", + "chainId": 110003, + "networkId": 110003, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/2", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 3", + "chain": "QuarkChain", + "rpc": ["https://devnet-s3-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39903"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s3", + "chainId": 110004, + "networkId": 110004, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/3", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 4", + "chain": "QuarkChain", + "rpc": ["https://devnet-s4-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39904"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s4", + "chainId": 110005, + "networkId": 110005, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/4", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 5", + "chain": "QuarkChain", + "rpc": ["https://devnet-s5-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39905"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s5", + "chainId": 110006, + "networkId": 110006, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/5", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 6", + "chain": "QuarkChain", + "rpc": ["https://devnet-s6-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39906"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s6", + "chainId": 110007, + "networkId": 110007, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/6", + "standard": "EIP3091" + } + ] + }, + { + "name": "QuarkChain Devnet Shard 7", + "chain": "QuarkChain", + "rpc": ["https://devnet-s7-ethapi.quarkchain.io", "http://eth-jrpc.devnet.quarkchain.io:39907"], + "faucets": [], + "nativeCurrency": { "name": "QKC", "symbol": "QKC", "decimals": 18 }, + "infoURL": "https://www.quarkchain.io", + "shortName": "qkc-d-s7", + "chainId": 110008, + "networkId": 110008, + "parent": { "chain": "eip155-110000", "type": "shard" }, + "explorers": [ + { + "name": "quarkchain-devnet", + "url": "https://devnet.quarkchain.io/7", + "standard": "EIP3091" + } + ] + }, + { + "name": "Siberium Network", + "chain": "SBR", + "rpc": ["https://rpc.main.siberium.net", "https://rpc.main.siberium.net.ru"], + "faucets": [], + "nativeCurrency": { "name": "Siberium", "symbol": "SBR", "decimals": 18 }, + "infoURL": "https://siberium.net", + "shortName": "sbr", + "chainId": 111111, + "networkId": 111111, + "icon": "siberium", + "explorers": [ + { + "name": "Siberium Mainnet Explorer - blockscout - 1", + "url": "https://explorer.main.siberium.net", + "icon": "siberium", + "standard": "EIP3091" + }, + { + "name": "Siberium Mainnet Explorer - blockscout - 2", + "url": "https://explorer.main.siberium.net.ru", + "icon": "siberium", + "standard": "EIP3091" + } + ] + }, + { + "name": "ADIL Devnet", + "chain": "ADIL", + "icon": "adil", + "rpc": ["https://devnet.adilchain-rpc.io"], + "faucets": [], + "nativeCurrency": { "name": "Devnet ADIL", "symbol": "ADIL", "decimals": 18 }, + "infoURL": "https://adilchain.io", + "shortName": "dadil", + "chainId": 123456, + "networkId": 123456, + "explorers": [ + { + "name": "ADIL Devnet Explorer", + "url": "https://devnet.adilchain-scan.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "ETND Chain Mainnets", + "chain": "ETND", + "rpc": ["https://rpc.node1.etnd.pro/"], + "faucets": [], + "nativeCurrency": { "name": "ETND", "symbol": "ETND", "decimals": 18 }, + "infoURL": "https://www.etnd.pro", + "shortName": "ETND", + "chainId": 131419, + "networkId": 131419, + "icon": "ETND", + "explorers": [ + { "name": "etndscan", "url": "https://scan.etnd.pro", "icon": "ETND", "standard": "none" } + ] + }, + { + "name": "Taiko (Alpha-2 Testnet)", + "chain": "ETH", + "icon": "taiko", + "rpc": ["https://rpc.a2.taiko.xyz"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://taiko.xyz", + "shortName": "taiko-a2", + "chainId": 167004, + "networkId": 167004, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.a2.taiko.xyz", "standard": "EIP3091" } + ] + }, + { + "name": "Condor Test Network", + "chain": "CONDOR", + "icon": "condor", + "rpc": ["https://testnet.condor.systems/rpc"], + "faucets": ["https://faucet.condor.systems"], + "nativeCurrency": { "name": "Condor Native Token", "symbol": "CONDOR", "decimals": 18 }, + "infoURL": "https://condor.systems", + "shortName": "condor", + "chainId": 188881, + "networkId": 188881, + "explorers": [ + { "name": "CondorScan", "url": "https://explorer.condor.systems", "standard": "none" } + ] + }, + { + "name": "Milkomeda C1 Testnet", + "chain": "milkTAda", + "icon": "milkomeda", + "rpc": [ + "https://rpc-devnet-cardano-evm.c1.milkomeda.com", + "wss://rpc-devnet-cardano-evm.c1.milkomeda.com" + ], + "faucets": [], + "nativeCurrency": { "name": "milkTAda", "symbol": "mTAda", "decimals": 18 }, + "infoURL": "https://milkomeda.com", + "shortName": "milkTAda", + "chainId": 200101, + "networkId": 200101, + "explorers": [ + { + "name": "Blockscout", + "url": "https://explorer-devnet-cardano-evm.c1.milkomeda.com", + "standard": "none" + } + ] + }, + { + "name": "Milkomeda A1 Testnet", + "chain": "milkTAlgo", + "icon": "milkomeda", + "rpc": ["https://rpc-devnet-algorand-rollup.a1.milkomeda.com"], + "faucets": [], + "nativeCurrency": { "name": "milkTAlgo", "symbol": "mTAlgo", "decimals": 18 }, + "infoURL": "https://milkomeda.com", + "shortName": "milkTAlgo", + "chainId": 200202, + "networkId": 200202, + "explorers": [ + { + "name": "Blockscout", + "url": "https://explorer-devnet-algorand-rollup.a1.milkomeda.com", + "standard": "none" + } + ] + }, + { + "name": "Akroma", + "chain": "AKA", + "rpc": ["https://remote.akroma.io"], + "faucets": [], + "nativeCurrency": { "name": "Akroma Ether", "symbol": "AKA", "decimals": 18 }, + "infoURL": "https://akroma.io", + "shortName": "aka", + "chainId": 200625, + "networkId": 200625, + "slip44": 200625 + }, + { + "name": "Alaya Mainnet", + "chain": "Alaya", + "rpc": ["https://openapi.alaya.network/rpc", "wss://openapi.alaya.network/ws"], + "faucets": [], + "nativeCurrency": { "name": "ATP", "symbol": "atp", "decimals": 18 }, + "infoURL": "https://www.alaya.network/", + "shortName": "alaya", + "chainId": 201018, + "networkId": 1, + "icon": "alaya", + "explorers": [ + { "name": "alaya explorer", "url": "https://scan.alaya.network", "standard": "none" } + ] + }, + { + "name": "Alaya Dev Testnet", + "chain": "Alaya", + "rpc": ["https://devnetopenapi.alaya.network/rpc", "wss://devnetopenapi.alaya.network/ws"], + "faucets": ["https://faucet.alaya.network/faucet/?id=f93426c0887f11eb83b900163e06151c"], + "nativeCurrency": { "name": "ATP", "symbol": "atp", "decimals": 18 }, + "infoURL": "https://www.alaya.network/", + "shortName": "alayadev", + "chainId": 201030, + "networkId": 1, + "icon": "alaya", + "explorers": [ + { "name": "alaya explorer", "url": "https://devnetscan.alaya.network", "standard": "none" } + ] + }, + { + "name": "Mythical Chain", + "chain": "MYTH", + "rpc": ["https://chain-rpc.mythicalgames.com"], + "faucets": [], + "nativeCurrency": { "name": "Mythos", "symbol": "MYTH", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://mythicalgames.com/", + "shortName": "myth", + "chainId": 201804, + "networkId": 201804, + "icon": "mythical", + "explorers": [ + { + "name": "Mythical Chain Explorer", + "url": "https://explorer.mythicalgames.com", + "icon": "mythical", + "standard": "EIP3091" + } + ] + }, + { + "name": "Decimal Smart Chain Testnet", + "chain": "tDSC", + "rpc": ["https://testnet-val.decimalchain.com/web3"], + "faucets": [], + "nativeCurrency": { "name": "Decimal", "symbol": "tDEL", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://decimalchain.com", + "shortName": "tDSC", + "chainId": 202020, + "networkId": 202020, + "icon": "dsc", + "explorers": [ + { + "name": "DSC Explorer Testnet", + "url": "https://testnet.explorer.decimalchain.com", + "icon": "dsc", + "standard": "EIP3091" + } + ] + }, + { + "name": "Jellie", + "title": "Twala Testnet Jellie", + "shortName": "twl-jellie", + "chain": "ETH", + "chainId": 202624, + "networkId": 202624, + "icon": "twala", + "nativeCurrency": { "name": "Twala Coin", "symbol": "TWL", "decimals": 18 }, + "rpc": ["https://jellie-rpc.twala.io/", "wss://jellie-rpc-wss.twala.io/"], + "faucets": [], + "infoURL": "https://twala.io/", + "explorers": [ + { + "name": "Jellie Blockchain Explorer", + "url": "https://jellie.twala.io", + "standard": "EIP3091", + "icon": "twala" + } + ] + }, + { + "name": "PlatON Mainnet", + "chain": "PlatON", + "rpc": ["https://openapi2.platon.network/rpc", "wss://openapi2.platon.network/ws"], + "faucets": [], + "nativeCurrency": { "name": "LAT", "symbol": "lat", "decimals": 18 }, + "infoURL": "https://www.platon.network", + "shortName": "platon", + "chainId": 210425, + "networkId": 1, + "icon": "platon", + "explorers": [ + { "name": "PlatON explorer", "url": "https://scan.platon.network", "standard": "none" } + ] + }, + { + "name": "Mas Mainnet", + "chain": "MAS", + "rpc": ["http://node.masnet.ai:8545"], + "faucets": [], + "nativeCurrency": { "name": "Master Bank", "symbol": "MAS", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://masterbank.org", + "shortName": "mas", + "chainId": 220315, + "networkId": 220315, + "icon": "mas", + "explorers": [ + { "name": "explorer masnet", "url": "https://explorer.masnet.ai", "standard": "EIP3091" } + ] + }, + { + "name": "Taf ECO Chain Mainnet", + "chain": "Taf ECO Chain", + "icon": "taf", + "rpc": ["https://mainnet.tafchain.com/v1"], + "faucets": [], + "nativeCurrency": { "name": "Taf ECO Chain Mainnet", "symbol": "TAFECO", "decimals": 18 }, + "infoURL": "https://www.tafchain.com", + "shortName": "TAFECO", + "chainId": 224168, + "networkId": 224168, + "explorers": [ + { + "name": "Taf ECO Chain Mainnet", + "url": "https://ecoscan.tafchain.com", + "standard": "EIP3091" + } + ] + }, + { + "name": "HashKey Chain Testnet", + "chain": "HashKey", + "rpc": ["https://testnet.hashkeychain/rpc"], + "faucets": ["https://testnet.hashkeychain/faucet"], + "nativeCurrency": { "name": "HashKey Token", "symbol": "tHSK", "decimals": 18 }, + "infoURL": "https://www.hashkey.com", + "shortName": "hsktest", + "chainId": 230315, + "networkId": 230315, + "icon": "hsk", + "explorers": [ + { + "name": "HashKey Chain Testnet Explorer", + "url": "https://testnet.hashkeyscan.io", + "standard": "none" + } + ] + }, + { + "name": "Haymo Testnet", + "chain": "tHYM", + "rpc": ["https://testnet1.haymo.network"], + "faucets": [], + "nativeCurrency": { "name": "HAYMO", "symbol": "HYM", "decimals": 18 }, + "infoURL": "https://haymoswap.web.app/", + "shortName": "hym", + "chainId": 234666, + "networkId": 234666 + }, + { + "name": "ARTIS sigma1", + "chain": "ARTIS", + "rpc": ["https://rpc.sigma1.artis.network"], + "faucets": [], + "nativeCurrency": { "name": "ARTIS sigma1 Ether", "symbol": "ATS", "decimals": 18 }, + "infoURL": "https://artis.eco", + "shortName": "ats", + "chainId": 246529, + "networkId": 246529, + "slip44": 246529 + }, + { + "name": "ARTIS Testnet tau1", + "chain": "ARTIS", + "rpc": ["https://rpc.tau1.artis.network"], + "faucets": [], + "nativeCurrency": { "name": "ARTIS tau1 Ether", "symbol": "tATS", "decimals": 18 }, + "infoURL": "https://artis.network", + "shortName": "atstau", + "chainId": 246785, + "networkId": 246785 + }, + { + "name": "Saakuru Testnet", + "chain": "Saakuru", + "icon": "saakuru", + "rpc": ["https://rpc-testnet.saakuru.network"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://saakuru.network", + "shortName": "saakuru-testnet", + "chainId": 247253, + "networkId": 247253, + "explorers": [ + { + "name": "saakuru-explorer-testnet", + "url": "https://explorer-testnet.saakuru.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "CMP-Mainnet", + "chain": "CMP", + "rpc": ["https://mainnet.block.caduceus.foundation", "wss://mainnet.block.caduceus.foundation"], + "faucets": [], + "nativeCurrency": { "name": "Caduceus Token", "symbol": "CMP", "decimals": 18 }, + "infoURL": "https://caduceus.foundation/", + "shortName": "cmp-mainnet", + "chainId": 256256, + "networkId": 256256, + "explorers": [ + { + "name": "Mainnet Scan", + "url": "https://mainnet.scan.caduceus.foundation", + "standard": "none" + } + ] + }, + { + "name": "Gear Zero Network Testnet", + "chain": "GearZero", + "rpc": ["https://gzn-test.linksme.info"], + "faucets": [], + "nativeCurrency": { "name": "Gear Zero Network Native Token", "symbol": "GZN", "decimals": 18 }, + "infoURL": "https://token.gearzero.ca/testnet", + "shortName": "gz-testnet", + "chainId": 266256, + "networkId": 266256, + "slip44": 266256, + "explorers": [] + }, + { + "name": "Social Smart Chain Mainnet", + "chain": "SoChain", + "rpc": ["https://socialsmartchain.digitalnext.business"], + "faucets": [], + "nativeCurrency": { "name": "SoChain", "symbol": "$OC", "decimals": 18 }, + "infoURL": "https://digitalnext.business/SocialSmartChain", + "shortName": "SoChain", + "chainId": 281121, + "networkId": 281121, + "explorers": [] + }, + { + "name": "Filecoin - Calibration testnet", + "chain": "FIL", + "icon": "filecoin", + "rpc": ["https://api.calibration.node.glif.io/rpc/v1"], + "faucets": ["https://faucet.calibration.fildev.network/"], + "nativeCurrency": { "name": "testnet filecoin", "symbol": "tFIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin-calibration", + "chainId": 314159, + "networkId": 314159, + "slip44": 1, + "explorers": [ + { + "name": "Filscan - Calibration", + "url": "https://calibration.filscan.io", + "standard": "none" + }, + { + "name": "Filscout - Calibration", + "url": "https://calibration.filscout.com/en", + "standard": "none" + }, + { + "name": "Filfox - Calibration", + "url": "https://calibration.filfox.info", + "standard": "none" + } + ] + }, + { + "name": "Oone Chain Testnet", + "chain": "OONE", + "rpc": ["https://blockchain-test.adigium.world"], + "faucets": ["https://apps-test.adigium.com/faucet"], + "nativeCurrency": { "name": "Oone", "symbol": "tOONE", "decimals": 18 }, + "infoURL": "https://oone.world", + "shortName": "oonetest", + "chainId": 333777, + "networkId": 333777, + "explorers": [ + { "name": "expedition", "url": "https://explorer-test.adigium.world", "standard": "none" } + ] + }, + { + "name": "Polis Testnet", + "chain": "Sparta", + "icon": "polis", + "rpc": ["https://sparta-rpc.polis.tech"], + "faucets": ["https://faucet.polis.tech"], + "nativeCurrency": { "name": "tPolis", "symbol": "tPOLIS", "decimals": 18 }, + "infoURL": "https://polis.tech", + "shortName": "sparta", + "chainId": 333888, + "networkId": 333888 + }, + { + "name": "Polis Mainnet", + "chain": "Olympus", + "icon": "polis", + "rpc": ["https://rpc.polis.tech"], + "faucets": ["https://faucet.polis.tech"], + "nativeCurrency": { "name": "Polis", "symbol": "POLIS", "decimals": 18 }, + "infoURL": "https://polis.tech", + "shortName": "olympus", + "chainId": 333999, + "networkId": 333999 + }, + { + "name": "HAPchain Testnet", + "chain": "HAPchain", + "rpc": ["https://jsonrpc-test.hap.land"], + "faucets": [], + "nativeCurrency": { "name": "HAP", "symbol": "HAP", "decimals": 18 }, + "infoURL": "https://hap.land", + "shortName": "hap-testnet", + "chainId": 373737, + "networkId": 373737, + "icon": "hap", + "explorers": [ + { + "name": "HAP EVM Explorer (Blockscout)", + "url": "https://blockscout-test.hap.land", + "standard": "none", + "icon": "hap" + } + ] + }, + { + "name": "Metal C-Chain", + "chain": "Metal", + "rpc": ["https://api.metalblockchain.org/ext/bc/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Metal", "symbol": "METAL", "decimals": 18 }, + "infoURL": "https://www.metalblockchain.org/", + "shortName": "metal", + "chainId": 381931, + "networkId": 381931, + "slip44": 9005, + "explorers": [{ "name": "metalscan", "url": "https://metalscan.io", "standard": "EIP3091" }] + }, + { + "name": "Metal Tahoe C-Chain", + "chain": "Metal", + "rpc": ["https://tahoe.metalblockchain.org/ext/bc/C/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Metal", "symbol": "METAL", "decimals": 18 }, + "infoURL": "https://www.metalblockchain.org/", + "shortName": "Tahoe", + "chainId": 381932, + "networkId": 381932, + "slip44": 9005, + "explorers": [ + { "name": "metalscan", "url": "https://tahoe.metalscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "Tipboxcoin Mainnet", + "chain": "TPBX", + "icon": "tipboxcoinIcon", + "rpc": ["https://mainnet-rpc.tipboxcoin.net"], + "faucets": ["https://faucet.tipboxcoin.net"], + "nativeCurrency": { "name": "Tipboxcoin", "symbol": "TPBX", "decimals": 18 }, + "infoURL": "https://tipboxcoin.net", + "shortName": "TPBXm", + "chainId": 404040, + "networkId": 404040, + "explorers": [{ "name": "Tipboxcoin", "url": "https://tipboxcoin.net", "standard": "EIP3091" }] + }, + { + "name": "Kekchain", + "chain": "kek", + "rpc": ["https://mainnet.kekchain.com"], + "faucets": [], + "nativeCurrency": { "name": "KEK", "symbol": "KEK", "decimals": 18 }, + "infoURL": "https://kekchain.com", + "shortName": "KEK", + "chainId": 420420, + "networkId": 103090, + "icon": "kek", + "explorers": [ + { + "name": "blockscout", + "url": "https://mainnet-explorer.kekchain.com", + "icon": "kek", + "standard": "EIP3091" + } + ] + }, + { + "name": "Kekchain (kektest)", + "chain": "kek", + "rpc": ["https://testnet.kekchain.com"], + "faucets": [], + "nativeCurrency": { "name": "tKEK", "symbol": "tKEK", "decimals": 18 }, + "infoURL": "https://kekchain.com", + "shortName": "tKEK", + "chainId": 420666, + "networkId": 1, + "icon": "kek", + "explorers": [ + { + "name": "blockscout", + "url": "https://testnet-explorer.kekchain.com", + "icon": "kek", + "standard": "EIP3091" + } + ] + }, + { + "name": "Arbitrum Rinkeby", + "title": "Arbitrum Testnet Rinkeby", + "chainId": 421611, + "shortName": "arb-rinkeby", + "chain": "ETH", + "networkId": 421611, + "nativeCurrency": { "name": "Arbitrum Rinkeby Ether", "symbol": "ETH", "decimals": 18 }, + "rpc": ["https://rinkeby.arbitrum.io/rpc"], + "faucets": ["http://fauceth.komputing.org?chain=421611&address=${ADDRESS}"], + "infoURL": "https://arbitrum.io", + "explorers": [ + { "name": "arbiscan-testnet", "url": "https://testnet.arbiscan.io", "standard": "EIP3091" }, + { + "name": "arbitrum-rinkeby", + "url": "https://rinkeby-explorer.arbitrum.io", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-4", + "bridges": [{ "url": "https://bridge.arbitrum.io" }] + } + }, + { + "name": "Arbitrum Goerli", + "title": "Arbitrum Goerli Rollup Testnet", + "chainId": 421613, + "shortName": "arb-goerli", + "chain": "ETH", + "networkId": 421613, + "nativeCurrency": { "name": "Arbitrum Goerli Ether", "symbol": "AGOR", "decimals": 18 }, + "rpc": ["https://goerli-rollup.arbitrum.io/rpc/"], + "faucets": [], + "infoURL": "https://arbitrum.io/", + "explorers": [ + { + "name": "Arbitrum Goerli Rollup Explorer", + "url": "https://goerli-rollup-explorer.arbitrum.io", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-5", + "bridges": [{ "url": "https://bridge.arbitrum.io/" }] + } + }, + { + "name": "Fastex Chain testnet", + "chain": "FTN", + "title": "Fastex Chain testnet", + "rpc": ["https://rpc.testnet.fastexchain.com"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "FTN", "symbol": "FTN", "decimals": 18 }, + "infoURL": "https://fastex.com", + "shortName": "ftn", + "chainId": 424242, + "networkId": 424242, + "explorers": [ + { "name": "blockscout", "url": "https://testnet.ftnscan.com", "standard": "none" } + ] + }, + { + "name": "Dexalot Subnet Testnet", + "chain": "DEXALOT", + "icon": "dexalot", + "rpc": ["https://subnets.avax.network/dexalot/testnet/rpc"], + "faucets": ["https://faucet.avax.network/?subnet=dexalot"], + "nativeCurrency": { "name": "Dexalot", "symbol": "ALOT", "decimals": 18 }, + "infoURL": "https://dexalot.com", + "shortName": "dexalot-testnet", + "chainId": 432201, + "networkId": 432201, + "explorers": [ + { + "name": "Avalanche Subnet Testnet Explorer", + "url": "https://subnets-test.avax.network/dexalot", + "standard": "EIP3091" + } + ] + }, + { + "name": "Dexalot Subnet", + "chain": "DEXALOT", + "icon": "dexalot", + "rpc": ["https://subnets.avax.network/dexalot/mainnet/rpc"], + "faucets": [], + "nativeCurrency": { "name": "Dexalot", "symbol": "ALOT", "decimals": 18 }, + "infoURL": "https://dexalot.com", + "shortName": "dexalot", + "chainId": 432204, + "networkId": 432204, + "explorers": [ + { + "name": "Avalanche Subnet Explorer", + "url": "https://subnets.avax.network/dexalot", + "standard": "EIP3091" + } + ] + }, + { + "name": "Weelink Testnet", + "chain": "WLK", + "rpc": ["https://weelinknode1c.gw002.oneitfarm.com"], + "faucets": ["https://faucet.weelink.gw002.oneitfarm.com"], + "nativeCurrency": { "name": "Weelink Chain Token", "symbol": "tWLK", "decimals": 18 }, + "infoURL": "https://weelink.cloud", + "shortName": "wlkt", + "chainId": 444900, + "networkId": 444900, + "explorers": [ + { + "name": "weelink-testnet", + "url": "https://weelink.cloud/#/blockView/overview", + "standard": "none" + } + ] + }, + { + "name": "OpenChain Mainnet", + "chain": "OpenChain", + "rpc": ["https://baas-rpc.luniverse.io:18545?lChainId=1641349324562974539"], + "faucets": [], + "nativeCurrency": { "name": "OpenCoin", "symbol": "OPC", "decimals": 10 }, + "infoURL": "https://www.openchain.live", + "shortName": "oc", + "chainId": 474142, + "networkId": 474142, + "explorers": [ + { + "name": "SIDE SCAN", + "url": "https://sidescan.luniverse.io/1641349324562974539", + "standard": "none" + } + ] + }, + { + "name": "CMP-Testnet", + "chain": "CMP", + "rpc": ["https://galaxy.block.caduceus.foundation", "wss://galaxy.block.caduceus.foundation"], + "faucets": ["https://dev.caduceus.foundation/testNetwork"], + "nativeCurrency": { "name": "Caduceus Testnet Token", "symbol": "CMP", "decimals": 18 }, + "infoURL": "https://caduceus.foundation/", + "shortName": "cmp", + "chainId": 512512, + "networkId": 512512, + "explorers": [ + { + "name": "Galaxy Scan", + "url": "https://galaxy.scan.caduceus.foundation", + "standard": "none" + } + ] + }, + { + "name": "ethereum Fair", + "chainId": 513100, + "networkId": 513100, + "shortName": "ethf", + "chain": "ETHF", + "nativeCurrency": { "name": "EthereumFair", "symbol": "ETHF", "decimals": 18 }, + "rpc": ["https://rpc.etherfair.org"], + "faucets": [], + "explorers": [ + { "name": "etherfair", "url": "https://www.oklink.com/ethf", "standard": "EIP3091" } + ], + "infoURL": "https://etherfair.org" + }, + { + "name": "Scroll", + "chain": "ETH", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://scroll.io", + "shortName": "scr", + "chainId": 534352, + "networkId": 534352, + "explorers": [], + "parent": { "type": "L2", "chain": "eip155-1", "bridges": [] } + }, + { + "name": "Scroll Alpha Testnet", + "chain": "ETH", + "status": "incubating", + "rpc": ["https://alpha-rpc.scroll.io/l2"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://scroll.io", + "shortName": "scr-alpha", + "chainId": 534353, + "networkId": 534353, + "explorers": [ + { + "name": "Scroll Alpha Testnet Block Explorer", + "url": "https://blockscout.scroll.io", + "standard": "EIP3091" + } + ], + "parent": { "type": "L2", "chain": "eip155-5", "bridges": [] } + }, + { + "name": "Scroll Pre-Alpha Testnet", + "chain": "ETH", + "rpc": ["https://prealpha-rpc.scroll.io/l2"], + "faucets": ["https://prealpha.scroll.io/faucet"], + "nativeCurrency": { "name": "Ether", "symbol": "TSETH", "decimals": 18 }, + "infoURL": "https://scroll.io", + "shortName": "scr-prealpha", + "chainId": 534354, + "networkId": 534354, + "explorers": [ + { + "name": "Scroll L2 Block Explorer", + "url": "https://l2scan.scroll.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "BeanEco SmartChain", + "title": "BESC Mainnet", + "chain": "BESC", + "rpc": ["https://mainnet-rpc.bescscan.io"], + "faucets": ["faucet.bescscan.ion"], + "nativeCurrency": { "name": "BeanEco SmartChain", "symbol": "BESC", "decimals": 18 }, + "infoURL": "besceco.finance", + "shortName": "BESC", + "chainId": 535037, + "networkId": 535037, + "explorers": [{ "name": "bescscan", "url": "https://Bescscan.io", "standard": "EIP3091" }] + }, + { + "name": "Bear Network Chain Mainnet", + "chain": "BRNKC", + "icon": "brnkc", + "rpc": ["https://brnkc-mainnet.bearnetwork.net", "https://brnkc-mainnet1.bearnetwork.net"], + "faucets": [], + "nativeCurrency": { + "name": "Bear Network Chain Native Token", + "symbol": "BRNKC", + "decimals": 18 + }, + "infoURL": "https://bearnetwork.net", + "shortName": "BRNKC", + "chainId": 641230, + "networkId": 641230, + "explorers": [ + { "name": "brnkscan", "url": "https://brnkscan.bearnetwork.net", "standard": "EIP3091" } + ] + }, + { + "name": "Vision - Vpioneer Test Chain", + "chain": "Vision-Vpioneer", + "rpc": ["https://vpioneer.infragrid.v.network/ethereum/compatible"], + "faucets": ["https://vpioneerfaucet.visionscan.org"], + "nativeCurrency": { "name": "VS", "symbol": "VS", "decimals": 18 }, + "infoURL": "https://visionscan.org", + "shortName": "vpioneer", + "chainId": 666666, + "networkId": 666666, + "slip44": 60 + }, + { + "name": "Bear Network Chain Testnet", + "chain": "BRNKCTEST", + "icon": "brnkc", + "rpc": ["https://brnkc-test.bearnetwork.net"], + "faucets": ["https://faucet.bearnetwork.net"], + "nativeCurrency": { + "name": "Bear Network Chain Testnet Token", + "symbol": "tBRNKC", + "decimals": 18 + }, + "infoURL": "https://bearnetwork.net", + "shortName": "BRNKCTEST", + "chainId": 751230, + "networkId": 751230, + "explorers": [ + { + "name": "brnktestscan", + "url": "https://brnktest-scan.bearnetwork.net", + "standard": "EIP3091" + } + ] + }, + { + "name": "OctaSpace", + "chain": "OCTA", + "rpc": ["https://rpc.octa.space", "wss://rpc.octa.space"], + "faucets": [], + "nativeCurrency": { "name": "OctaSpace", "symbol": "OCTA", "decimals": 18 }, + "infoURL": "https://octa.space", + "shortName": "octa", + "chainId": 800001, + "networkId": 800001, + "icon": "octaspace", + "explorers": [ + { + "name": "blockscout", + "url": "https://explorer.octa.space", + "icon": "blockscout", + "standard": "EIP3091" + } + ] + }, + { + "name": "4GoodNetwork", + "chain": "4GN", + "rpc": ["https://chain.deptofgood.com"], + "faucets": [], + "nativeCurrency": { "name": "APTA", "symbol": "APTA", "decimals": 18 }, + "infoURL": "https://bloqs4good.com", + "shortName": "bloqs4good", + "chainId": 846000, + "networkId": 846000 + }, + { + "name": "Vision - Mainnet", + "chain": "Vision", + "rpc": ["https://infragrid.v.network/ethereum/compatible"], + "faucets": [], + "nativeCurrency": { "name": "VS", "symbol": "VS", "decimals": 18 }, + "infoURL": "https://www.v.network", + "explorers": [ + { "name": "Visionscan", "url": "https://www.visionscan.org", "standard": "EIP3091" } + ], + "shortName": "vision", + "chainId": 888888, + "networkId": 888888, + "slip44": 60 + }, + { + "name": "Posichain Mainnet Shard 0", + "chain": "PSC", + "rpc": ["https://api.posichain.org", "https://api.s0.posichain.org"], + "faucets": ["https://faucet.posichain.org/"], + "nativeCurrency": { "name": "Posichain Native Token", "symbol": "POSI", "decimals": 18 }, + "infoURL": "https://posichain.org", + "shortName": "psc-s0", + "chainId": 900000, + "networkId": 900000, + "explorers": [ + { + "name": "Posichain Explorer", + "url": "https://explorer.posichain.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Posichain Testnet Shard 0", + "chain": "PSC", + "rpc": ["https://api.s0.t.posichain.org"], + "faucets": ["https://faucet.posichain.org/"], + "nativeCurrency": { "name": "Posichain Native Token", "symbol": "POSI", "decimals": 18 }, + "infoURL": "https://posichain.org", + "shortName": "psc-t-s0", + "chainId": 910000, + "networkId": 910000, + "explorers": [ + { + "name": "Posichain Explorer Testnet", + "url": "https://explorer-testnet.posichain.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Posichain Devnet Shard 0", + "chain": "PSC", + "rpc": ["https://api.s0.d.posichain.org"], + "faucets": ["https://faucet.posichain.org/"], + "nativeCurrency": { "name": "Posichain Native Token", "symbol": "POSI", "decimals": 18 }, + "infoURL": "https://posichain.org", + "shortName": "psc-d-s0", + "chainId": 920000, + "networkId": 920000, + "explorers": [ + { + "name": "Posichain Explorer Devnet", + "url": "https://explorer-devnet.posichain.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Posichain Devnet Shard 1", + "chain": "PSC", + "rpc": ["https://api.s1.d.posichain.org"], + "faucets": ["https://faucet.posichain.org/"], + "nativeCurrency": { "name": "Posichain Native Token", "symbol": "POSI", "decimals": 18 }, + "infoURL": "https://posichain.org", + "shortName": "psc-d-s1", + "chainId": 920001, + "networkId": 920001, + "explorers": [ + { + "name": "Posichain Explorer Devnet", + "url": "https://explorer-devnet.posichain.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "FNCY Testnet", + "chain": "FNCY", + "rpc": ["https://fncy-testnet-seed.fncy.world"], + "faucets": ["https://faucet-testnet.fncy.world"], + "nativeCurrency": { "name": "FNCY", "symbol": "FNCY", "decimals": 18 }, + "infoURL": "https://fncyscan-testnet.fncy.world", + "shortName": "tFNCY", + "chainId": 923018, + "networkId": 923018, + "icon": "fncy", + "explorers": [ + { + "name": "fncy scan testnet", + "url": "https://fncyscan-testnet.fncy.world", + "icon": "fncy", + "standard": "EIP3091" + } + ] + }, + { + "name": "Eluvio Content Fabric", + "chain": "Eluvio", + "rpc": [ + "https://host-76-74-28-226.contentfabric.io/eth/", + "https://host-76-74-28-232.contentfabric.io/eth/", + "https://host-76-74-29-2.contentfabric.io/eth/", + "https://host-76-74-29-8.contentfabric.io/eth/", + "https://host-76-74-29-34.contentfabric.io/eth/", + "https://host-76-74-29-35.contentfabric.io/eth/", + "https://host-154-14-211-98.contentfabric.io/eth/", + "https://host-154-14-192-66.contentfabric.io/eth/", + "https://host-60-240-133-202.contentfabric.io/eth/", + "https://host-64-235-250-98.contentfabric.io/eth/" + ], + "faucets": [], + "nativeCurrency": { "name": "ELV", "symbol": "ELV", "decimals": 18 }, + "infoURL": "https://eluv.io", + "shortName": "elv", + "chainId": 955305, + "networkId": 955305, + "slip44": 1011, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.eluv.io", "standard": "EIP3091" } + ] + }, + { + "name": "Etho Protocol", + "chain": "ETHO", + "rpc": ["https://rpc.ethoprotocol.com"], + "faucets": [], + "nativeCurrency": { "name": "Etho Protocol", "symbol": "ETHO", "decimals": 18 }, + "infoURL": "https://ethoprotocol.com", + "shortName": "etho", + "chainId": 1313114, + "networkId": 1313114, + "slip44": 1313114, + "explorers": [ + { "name": "blockscout", "url": "https://explorer.ethoprotocol.com", "standard": "none" } + ] + }, + { + "name": "Xerom", + "chain": "XERO", + "rpc": ["https://rpc.xerom.org"], + "faucets": [], + "nativeCurrency": { "name": "Xerom Ether", "symbol": "XERO", "decimals": 18 }, + "infoURL": "https://xerom.org", + "shortName": "xero", + "chainId": 1313500, + "networkId": 1313500 + }, + { + "name": "Kintsugi", + "title": "Kintsugi merge testnet", + "chain": "ETH", + "rpc": ["https://rpc.kintsugi.themerge.dev"], + "faucets": [ + "http://fauceth.komputing.org?chain=1337702&address=${ADDRESS}", + "https://faucet.kintsugi.themerge.dev" + ], + "nativeCurrency": { "name": "kintsugi Ethere", "symbol": "kiETH", "decimals": 18 }, + "infoURL": "https://kintsugi.themerge.dev/", + "shortName": "kintsugi", + "chainId": 1337702, + "networkId": 1337702, + "explorers": [ + { + "name": "kintsugi explorer", + "url": "https://explorer.kintsugi.themerge.dev", + "standard": "EIP3091" + } + ] + }, + { + "name": "Kiln", + "chain": "ETH", + "rpc": ["https://rpc.kiln.themerge.dev"], + "faucets": [ + "https://faucet.kiln.themerge.dev", + "https://kiln-faucet.pk910.de", + "https://kilnfaucet.com" + ], + "nativeCurrency": { "name": "Testnet ETH", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://kiln.themerge.dev/", + "shortName": "kiln", + "chainId": 1337802, + "networkId": 1337802, + "icon": "ethereum", + "explorers": [ + { + "name": "Kiln Explorer", + "url": "https://explorer.kiln.themerge.dev", + "icon": "ethereum", + "standard": "EIP3091" + } + ] + }, + { + "name": "Zhejiang", + "chain": "ETH", + "rpc": ["https://rpc.zhejiang.ethpandaops.io"], + "faucets": ["https://faucet.zhejiang.ethpandaops.io", "https://zhejiang-faucet.pk910.de"], + "nativeCurrency": { "name": "Testnet ETH", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://zhejiang.ethpandaops.io", + "shortName": "zhejiang", + "chainId": 1337803, + "networkId": 1337803, + "icon": "ethereum", + "explorers": [ + { + "name": "Zhejiang Explorer", + "url": "https://zhejiang.beaconcha.in", + "icon": "ethereum", + "standard": "EIP3091" + } + ] + }, + { + "name": "Plian Mainnet Main", + "chain": "Plian", + "rpc": ["https://mainnet.plian.io/pchain"], + "faucets": [], + "nativeCurrency": { "name": "Plian Token", "symbol": "PI", "decimals": 18 }, + "infoURL": "https://plian.org/", + "shortName": "plian-mainnet", + "chainId": 2099156, + "networkId": 2099156, + "explorers": [ + { "name": "piscan", "url": "https://piscan.plian.org/pchain", "standard": "EIP3091" } + ] + }, + { + "name": "PlatON Dev Testnet Deprecated", + "chain": "PlatON", + "rpc": ["https://devnetopenapi2.platon.network/rpc", "wss://devnetopenapi2.platon.network/ws"], + "faucets": ["https://devnet2faucet.platon.network/faucet"], + "nativeCurrency": { "name": "LAT", "symbol": "lat", "decimals": 18 }, + "infoURL": "https://www.platon.network", + "shortName": "platondev", + "chainId": 2203181, + "networkId": 1, + "icon": "platon", + "status": "deprecated", + "explorers": [ + { "name": "PlatON explorer", "url": "https://devnetscan.platon.network", "standard": "none" } + ] + }, + { + "name": "PlatON Dev Testnet2", + "chain": "PlatON", + "rpc": ["https://devnet2openapi.platon.network/rpc", "wss://devnet2openapi.platon.network/ws"], + "faucets": ["https://devnet2faucet.platon.network/faucet"], + "nativeCurrency": { "name": "LAT", "symbol": "lat", "decimals": 18 }, + "infoURL": "https://www.platon.network", + "shortName": "platondev2", + "chainId": 2206132, + "networkId": 1, + "icon": "platon", + "explorers": [ + { "name": "PlatON explorer", "url": "https://devnet2scan.platon.network", "standard": "none" } + ] + }, + { + "name": "Filecoin - Butterfly testnet", + "chain": "FIL", + "status": "incubating", + "rpc": [], + "faucets": ["https://faucet.butterfly.fildev.network"], + "nativeCurrency": { "name": "testnet filecoin", "symbol": "tFIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin-butterfly", + "icon": "filecoin", + "chainId": 3141592, + "networkId": 3141592, + "slip44": 1, + "explorers": [] + }, + { + "name": "Imversed Mainnet", + "chain": "Imversed", + "rpc": ["https://jsonrpc.imversed.network", "https://ws-jsonrpc.imversed.network"], + "faucets": [], + "nativeCurrency": { "name": "Imversed Token", "symbol": "IMV", "decimals": 18 }, + "infoURL": "https://imversed.com", + "shortName": "imversed", + "chainId": 5555555, + "networkId": 5555555, + "icon": "imversed", + "explorers": [ + { + "name": "Imversed EVM explorer (Blockscout)", + "url": "https://txe.imversed.network", + "icon": "imversed", + "standard": "EIP3091" + }, + { + "name": "Imversed Cosmos Explorer (Big Dipper)", + "url": "https://tex-c.imversed.com", + "icon": "imversed", + "standard": "none" + } + ] + }, + { + "name": "Imversed Testnet", + "chain": "Imversed", + "rpc": ["https://jsonrpc-test.imversed.network", "https://ws-jsonrpc-test.imversed.network"], + "faucets": [], + "nativeCurrency": { "name": "Imversed Token", "symbol": "IMV", "decimals": 18 }, + "infoURL": "https://imversed.com", + "shortName": "imversed-testnet", + "chainId": 5555558, + "networkId": 5555558, + "icon": "imversed", + "explorers": [ + { + "name": "Imversed EVM Explorer (Blockscout)", + "url": "https://txe-test.imversed.network", + "icon": "imversed", + "standard": "EIP3091" + }, + { + "name": "Imversed Cosmos Explorer (Big Dipper)", + "url": "https://tex-t.imversed.com", + "icon": "imversed", + "standard": "none" + } + ] + }, + { + "name": "Saakuru Mainnet", + "chain": "Saakuru", + "icon": "saakuru", + "rpc": ["https://rpc.saakuru.network"], + "faucets": [], + "nativeCurrency": { "name": "OAS", "symbol": "OAS", "decimals": 18 }, + "infoURL": "https://saakuru.network", + "shortName": "saakuru", + "chainId": 7225878, + "networkId": 7225878, + "explorers": [ + { + "name": "saakuru-explorer", + "url": "https://explorer.saakuru.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "OpenVessel", + "chain": "VSL", + "icon": "vsl", + "rpc": ["https://mainnet-external.openvessel.io"], + "faucets": [], + "nativeCurrency": { "name": "Vessel ETH", "symbol": "VETH", "decimals": 18 }, + "infoURL": "https://www.openvessel.io", + "shortName": "vsl", + "chainId": 7355310, + "networkId": 7355310, + "explorers": [ + { + "name": "openvessel-mainnet", + "url": "https://mainnet-explorer.openvessel.io", + "standard": "none" + } + ] + }, + { + "name": "QL1 Testnet", + "chain": "QOM", + "status": "incubating", + "rpc": ["https://rpc.testnet.qom.one"], + "faucets": ["https://faucet.qom.one"], + "nativeCurrency": { "name": "Shiba Predator", "symbol": "QOM", "decimals": 18 }, + "infoURL": "https://qom.one", + "shortName": "tqom", + "chainId": 7668378, + "networkId": 7668378, + "icon": "qom", + "explorers": [ + { + "name": "QL1 Testnet Explorer", + "url": "https://testnet.qom.one", + "icon": "qom", + "standard": "EIP3091" + } + ] + }, + { + "name": "Musicoin", + "chain": "MUSIC", + "rpc": ["https://mewapi.musicoin.tw"], + "faucets": [], + "nativeCurrency": { "name": "Musicoin", "symbol": "MUSIC", "decimals": 18 }, + "infoURL": "https://musicoin.tw", + "shortName": "music", + "chainId": 7762959, + "networkId": 7762959, + "slip44": 184 + }, + { + "name": "Plian Mainnet Subchain 1", + "chain": "Plian", + "rpc": ["https://mainnet.plian.io/child_0"], + "faucets": [], + "nativeCurrency": { "name": "Plian Token", "symbol": "PI", "decimals": 18 }, + "infoURL": "https://plian.org", + "shortName": "plian-mainnet-l2", + "chainId": 8007736, + "networkId": 8007736, + "explorers": [ + { "name": "piscan", "url": "https://piscan.plian.org/child_0", "standard": "EIP3091" } + ], + "parent": { "chain": "eip155-2099156", "type": "L2" } + }, + { + "name": "HAPchain", + "chain": "HAPchain", + "rpc": ["https://jsonrpc.hap.land"], + "faucets": [], + "nativeCurrency": { "name": "HAP", "symbol": "HAP", "decimals": 18 }, + "infoURL": "https://hap.land", + "shortName": "hap", + "chainId": 8794598, + "networkId": 8794598, + "icon": "hap", + "explorers": [ + { + "name": "HAP EVM Explorer (Blockscout)", + "url": "https://blockscout.hap.land", + "standard": "none", + "icon": "hap" + } + ] + }, + { + "name": "Quarix Testnet", + "chain": "Quarix", + "status": "incubating", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Q", "symbol": "Q", "decimals": 18 }, + "infoURL": "", + "shortName": "quarix-testnet", + "chainId": 8888881, + "networkId": 8888881, + "icon": "quarix", + "explorers": [] + }, + { + "name": "Quarix", + "chain": "Quarix", + "status": "incubating", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Q", "symbol": "Q", "decimals": 18 }, + "infoURL": "", + "shortName": "quarix", + "chainId": 8888888, + "networkId": 8888888, + "icon": "quarix", + "explorers": [] + }, + { + "name": "Plian Testnet Subchain 1", + "chain": "Plian", + "rpc": ["https://testnet.plian.io/child_test"], + "faucets": [], + "nativeCurrency": { "name": "Plian Token", "symbol": "TPI", "decimals": 18 }, + "infoURL": "https://plian.org/", + "shortName": "plian-testnet-l2", + "chainId": 10067275, + "networkId": 10067275, + "explorers": [ + { "name": "piscan", "url": "https://testnet.plian.org/child_test", "standard": "EIP3091" } + ], + "parent": { "chain": "eip155-16658437", "type": "L2" } + }, + { + "name": "Soverun Mainnet", + "chain": "SVRN", + "icon": "soverun", + "rpc": ["https://mainnet-rpc.soverun.com"], + "faucets": ["https://faucet.soverun.com"], + "nativeCurrency": { "name": "Soverun", "symbol": "SVRN", "decimals": 18 }, + "infoURL": "https://soverun.com", + "shortName": "SVRNm", + "chainId": 10101010, + "networkId": 10101010, + "explorers": [ + { "name": "Soverun", "url": "https://explorer.soverun.com", "standard": "EIP3091" } + ] + }, + { + "name": "Sepolia", + "title": "Ethereum Testnet Sepolia", + "chain": "ETH", + "rpc": ["https://rpc.sepolia.org", "https://rpc2.sepolia.org", "https://rpc-sepolia.rockx.com"], + "faucets": ["http://fauceth.komputing.org?chain=11155111&address=${ADDRESS}"], + "nativeCurrency": { "name": "Sepolia Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://sepolia.otterscan.io", + "shortName": "sep", + "chainId": 11155111, + "networkId": 11155111, + "explorers": [ + { "name": "etherscan-sepolia", "url": "https://sepolia.etherscan.io", "standard": "EIP3091" }, + { "name": "otterscan-sepolia", "url": "https://sepolia.otterscan.io", "standard": "EIP3091" } + ] + }, + { + "name": "PepChain Churchill", + "chain": "PEP", + "rpc": ["https://churchill-rpc.pepchain.io"], + "faucets": [], + "nativeCurrency": { "name": "PepChain Churchill Ether", "symbol": "TPEP", "decimals": 18 }, + "infoURL": "https://pepchain.io", + "shortName": "tpep", + "chainId": 13371337, + "networkId": 13371337 + }, + { + "name": "Anduschain Mainnet", + "chain": "anduschain", + "rpc": ["https://rpc.anduschain.io/rpc", "wss://rpc.anduschain.io/ws"], + "faucets": [], + "nativeCurrency": { "name": "DAON", "symbol": "DEB", "decimals": 18 }, + "infoURL": "https://anduschain.io/", + "shortName": "anduschain-mainnet", + "chainId": 14288640, + "networkId": 14288640, + "explorers": [ + { + "name": "anduschain explorer", + "url": "https://explorer.anduschain.io", + "icon": "daon", + "standard": "none" + } + ] + }, + { + "name": "Plian Testnet Main", + "chain": "Plian", + "rpc": ["https://testnet.plian.io/testnet"], + "faucets": [], + "nativeCurrency": { "name": "Plian Testnet Token", "symbol": "TPI", "decimals": 18 }, + "infoURL": "https://plian.org", + "shortName": "plian-testnet", + "chainId": 16658437, + "networkId": 16658437, + "explorers": [ + { "name": "piscan", "url": "https://testnet.plian.org/testnet", "standard": "EIP3091" } + ] + }, + { + "name": "IOLite", + "chain": "ILT", + "rpc": ["https://net.iolite.io"], + "faucets": [], + "nativeCurrency": { "name": "IOLite Ether", "symbol": "ILT", "decimals": 18 }, + "infoURL": "https://iolite.io", + "shortName": "ilt", + "chainId": 18289463, + "networkId": 18289463 + }, + { + "name": "SmartMesh Mainnet", + "chain": "Spectrum", + "rpc": ["https://jsonapi1.smartmesh.cn"], + "faucets": [], + "nativeCurrency": { "name": "SmartMesh Native Token", "symbol": "SMT", "decimals": 18 }, + "infoURL": "https://smartmesh.io", + "shortName": "spectrum", + "chainId": 20180430, + "networkId": 1, + "explorers": [{ "name": "spectrum", "url": "https://spectrum.pub", "standard": "none" }] + }, + { + "name": "quarkblockchain", + "chain": "QKI", + "rpc": ["https://hz.rpc.qkiscan.cn", "https://jp.rpc.qkiscan.io"], + "faucets": [], + "nativeCurrency": { "name": "quarkblockchain Native Token", "symbol": "QKI", "decimals": 18 }, + "infoURL": "https://quarkblockchain.org/", + "shortName": "qki", + "chainId": 20181205, + "networkId": 20181205 + }, + { + "name": "Excelon Mainnet", + "chain": "XLON", + "icon": "xlon", + "rpc": ["https://edgewallet1.xlon.org/"], + "faucets": [], + "nativeCurrency": { "name": "Excelon", "symbol": "xlon", "decimals": 18 }, + "infoURL": "https://xlon.org", + "shortName": "xlon", + "chainId": 22052002, + "networkId": 22052002, + "explorers": [ + { "name": "Excelon explorer", "url": "https://explorer.excelon.io", "standard": "EIP3091" } + ] + }, + { + "name": "Excoincial Chain Volta-Testnet", + "chain": "TEXL", + "icon": "exl", + "rpc": ["https://testnet-rpc.exlscan.com"], + "faucets": ["https://faucet.exlscan.com"], + "nativeCurrency": { "name": "TExlcoin", "symbol": "TEXL", "decimals": 18 }, + "infoURL": "", + "shortName": "exlvolta", + "chainId": 27082017, + "networkId": 27082017, + "explorers": [ + { + "name": "exlscan", + "url": "https://testnet-explorer.exlscan.com", + "icon": "exl", + "standard": "EIP3091" + } + ] + }, + { + "name": "Excoincial Chain Mainnet", + "chain": "EXL", + "icon": "exl", + "rpc": ["https://rpc.exlscan.com"], + "faucets": [], + "nativeCurrency": { "name": "Exlcoin", "symbol": "EXL", "decimals": 18 }, + "infoURL": "", + "shortName": "exl", + "chainId": 27082022, + "networkId": 27082022, + "explorers": [ + { "name": "exlscan", "url": "https://exlscan.com", "icon": "exl", "standard": "EIP3091" } + ] + }, + { + "name": "Auxilium Network Mainnet", + "chain": "AUX", + "rpc": ["https://rpc.auxilium.global"], + "faucets": [], + "nativeCurrency": { "name": "Auxilium coin", "symbol": "AUX", "decimals": 18 }, + "infoURL": "https://auxilium.global", + "shortName": "auxi", + "chainId": 28945486, + "networkId": 28945486, + "slip44": 344 + }, + { + "name": "Flachain Mainnet", + "chain": "FLX", + "icon": "flacoin", + "rpc": ["https://flachain.flaexchange.top/"], + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "faucets": [], + "nativeCurrency": { "name": "Flacoin", "symbol": "FLA", "decimals": 18 }, + "infoURL": "https://www.flaexchange.top", + "shortName": "fla", + "chainId": 29032022, + "networkId": 29032022, + "explorers": [ + { "name": "FLXExplorer", "url": "https://explorer.flaexchange.top", "standard": "EIP3091" } + ] + }, + { + "name": "Filecoin - Local testnet", + "chain": "FIL", + "status": "incubating", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "testnet filecoin", "symbol": "tFIL", "decimals": 18 }, + "infoURL": "https://filecoin.io", + "shortName": "filecoin-local", + "icon": "filecoin", + "chainId": 31415926, + "networkId": 31415926, + "slip44": 1, + "explorers": [] + }, + { + "name": "Joys Digital Mainnet", + "chain": "JOYS", + "rpc": ["https://node.joys.digital"], + "faucets": [], + "nativeCurrency": { "name": "JOYS", "symbol": "JOYS", "decimals": 18 }, + "infoURL": "https://joys.digital", + "shortName": "JOYS", + "chainId": 35855456, + "networkId": 35855456 + }, + { + "name": "maistestsubnet", + "chain": "MAI", + "rpc": [ + "http://174.138.9.169:9650/ext/bc/VUKSzFZKckx4PoZF9gX5QAqLPxbLzvu1vcssPG5QuodaJtdHT/rpc" + ], + "faucets": [], + "nativeCurrency": { "name": "maistestsubnet", "symbol": "MAI", "decimals": 18 }, + "infoURL": "", + "shortName": "mais", + "chainId": 43214913, + "networkId": 43214913, + "explorers": [ + { + "name": "maistesntet", + "url": "http://174.138.9.169:3006/?network=maistesntet", + "standard": "none" + } + ] + }, + { + "name": "Aquachain", + "chain": "AQUA", + "rpc": ["https://c.onical.org", "https://tx.aquacha.in/api"], + "faucets": ["https://aquacha.in/faucet"], + "nativeCurrency": { "name": "Aquachain Ether", "symbol": "AQUA", "decimals": 18 }, + "infoURL": "https://aquachain.github.io", + "shortName": "aqua", + "chainId": 61717561, + "networkId": 61717561, + "slip44": 61717561 + }, + { + "name": "Autonity Bakerloo (Thames) Testnet", + "chain": "AUT", + "rpc": ["https://rpc1.bakerloo.autonity.org/", "wss://rpc1.bakerloo.autonity.org/ws/"], + "faucets": ["https://faucet.autonity.org/"], + "nativeCurrency": { "name": "Bakerloo Auton", "symbol": "ATN", "decimals": 18 }, + "infoURL": "https://autonity.org/", + "shortName": "bakerloo-0", + "chainId": 65010000, + "networkId": 65010000, + "icon": "autonity", + "explorers": [ + { + "name": "autonity-blockscout", + "url": "https://bakerloo.autonity.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "Autonity Piccadilly (Thames) Testnet", + "chain": "AUT", + "rpc": ["https://rpc1.piccadilly.autonity.org/", "wss://rpc1.piccadilly.autonity.org/ws/"], + "faucets": ["https://faucet.autonity.org/"], + "nativeCurrency": { "name": "Piccadilly Auton", "symbol": "ATN", "decimals": 18 }, + "infoURL": "https://autonity.org/", + "shortName": "piccadilly-0", + "chainId": 65100000, + "networkId": 65100000, + "icon": "autonity", + "explorers": [ + { + "name": "autonity-blockscout", + "url": "https://piccadilly.autonity.org", + "standard": "EIP3091" + } + ] + }, + { + "name": "T.E.A.M Blockchain", + "chain": "TEAM", + "icon": "team", + "rpc": ["https://rpc.teamblockchain.team"], + "faucets": [], + "nativeCurrency": { "name": "TEAM", "symbol": "$TEAM", "decimals": 18 }, + "features": [{ "name": "EIP155" }, { "name": "EIP1559" }], + "infoURL": "https://teamblockchain.team", + "shortName": "team", + "chainId": 88888888, + "networkId": 88888888, + "explorers": [ + { "name": "teamscan", "url": "https://teamblockchain.team", "standard": "EIP3091" } + ] + }, + { + "name": "Joys Digital TestNet", + "chain": "TOYS", + "rpc": ["https://toys.joys.cash/"], + "faucets": ["https://faucet.joys.digital/"], + "nativeCurrency": { "name": "TOYS", "symbol": "TOYS", "decimals": 18 }, + "infoURL": "https://joys.digital", + "shortName": "TOYS", + "chainId": 99415706, + "networkId": 99415706 + }, + { + "name": "Gather Mainnet Network", + "chain": "GTH", + "rpc": ["https://mainnet.gather.network"], + "faucets": [], + "nativeCurrency": { "name": "Gather", "symbol": "GTH", "decimals": 18 }, + "infoURL": "https://gather.network", + "shortName": "GTH", + "chainId": 192837465, + "networkId": 192837465, + "icon": "gather", + "explorers": [ + { + "name": "Blockscout", + "url": "https://explorer.gather.network", + "icon": "gather", + "standard": "none" + } + ] + }, + { + "name": "Neon EVM DevNet", + "chain": "Solana", + "rpc": ["https://devnet.neonevm.org"], + "faucets": ["https://neonfaucet.org"], + "icon": "neon", + "nativeCurrency": { "name": "Neon", "symbol": "NEON", "decimals": 18 }, + "infoURL": "https://neon-labs.org", + "shortName": "neonevm-devnet", + "chainId": 245022926, + "networkId": 245022926, + "explorers": [ + { "name": "native", "url": "https://devnet.explorer.neon-labs.org", "standard": "EIP3091" }, + { "name": "neonscan", "url": "https://devnet.neonscan.org", "standard": "EIP3091" } + ] + }, + { + "name": "Neon EVM MainNet", + "chain": "Solana", + "rpc": ["https://mainnet.neonevm.org"], + "faucets": [], + "icon": "neon", + "nativeCurrency": { "name": "Neon", "symbol": "NEON", "decimals": 18 }, + "infoURL": "https://neon-labs.org", + "shortName": "neonevm-mainnet", + "chainId": 245022934, + "networkId": 245022934, + "explorers": [ + { "name": "native", "url": "https://mainnet.explorer.neon-labs.org", "standard": "EIP3091" }, + { "name": "neonscan", "url": "https://mainnet.neonscan.org", "standard": "EIP3091" } + ] + }, + { + "name": "Neon EVM TestNet", + "chain": "Solana", + "rpc": ["https://testnet.neonevm.org"], + "faucets": [], + "icon": "neon", + "nativeCurrency": { "name": "Neon", "symbol": "NEON", "decimals": 18 }, + "infoURL": "https://neon-labs.org", + "shortName": "neonevm-testnet", + "chainId": 245022940, + "networkId": 245022940, + "explorers": [ + { "name": "native", "url": "https://testnet.explorer.neon-labs.org", "standard": "EIP3091" }, + { "name": "neonscan", "url": "https://testnet.neonscan.org", "standard": "EIP3091" } + ] + }, + { + "name": "OneLedger Mainnet", + "chain": "OLT", + "icon": "oneledger", + "rpc": ["https://mainnet-rpc.oneledger.network"], + "faucets": [], + "nativeCurrency": { "name": "OLT", "symbol": "OLT", "decimals": 18 }, + "infoURL": "https://oneledger.io", + "shortName": "oneledger", + "chainId": 311752642, + "networkId": 311752642, + "explorers": [ + { + "name": "OneLedger Block Explorer", + "url": "https://mainnet-explorer.oneledger.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Calypso NFT Hub (SKALE Testnet)", + "title": "Calypso NFT Hub Testnet", + "chain": "staging-utter-unripe-menkar", + "rpc": ["https://staging-v3.skalenodes.com/v1/staging-utter-unripe-menkar"], + "faucets": ["https://sfuel.dirtroad.dev/staging"], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://calypsohub.network/", + "shortName": "calypso-testnet", + "chainId": 344106930, + "networkId": 344106930, + "explorers": [ + { + "name": "Blockscout", + "url": "https://staging-utter-unripe-menkar.explorer.staging-v3.skalenodes.com", + "icon": "calypso", + "standard": "EIP3091" + } + ] + }, + { + "name": "Gather Testnet Network", + "chain": "GTH", + "rpc": ["https://testnet.gather.network"], + "faucets": [], + "nativeCurrency": { "name": "Gather", "symbol": "GTH", "decimals": 18 }, + "infoURL": "https://gather.network", + "shortName": "tGTH", + "chainId": 356256156, + "networkId": 356256156, + "icon": "gather", + "explorers": [ + { + "name": "Blockscout", + "url": "https://testnet-explorer.gather.network", + "icon": "gather", + "standard": "none" + } + ] + }, + { + "name": "Gather Devnet Network", + "chain": "GTH", + "rpc": ["https://devnet.gather.network"], + "faucets": [], + "nativeCurrency": { "name": "Gather", "symbol": "GTH", "decimals": 18 }, + "infoURL": "https://gather.network", + "shortName": "dGTH", + "chainId": 486217935, + "networkId": 486217935, + "explorers": [ + { "name": "Blockscout", "url": "https://devnet-explorer.gather.network", "standard": "none" } + ] + }, + { + "name": "Nebula Staging", + "chain": "staging-faint-slimy-achird", + "rpc": [ + "https://staging-v3.skalenodes.com/v1/staging-faint-slimy-achird", + "wss://staging-v3.skalenodes.com/v1/ws/staging-faint-slimy-achird" + ], + "faucets": [], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://nebulachain.io/", + "shortName": "nebula-staging", + "chainId": 503129905, + "networkId": 503129905, + "explorers": [ + { + "name": "nebula", + "url": "https://staging-faint-slimy-achird.explorer.staging-v3.skalenodes.com", + "icon": "nebula", + "standard": "EIP3091" + } + ] + }, + { + "name": "IPOS Network", + "chain": "IPOS", + "rpc": ["https://rpc.iposlab.com", "https://rpc2.iposlab.com"], + "faucets": [], + "nativeCurrency": { "name": "IPOS Network Ether", "symbol": "IPOS", "decimals": 18 }, + "infoURL": "https://iposlab.com", + "shortName": "ipos", + "chainId": 1122334455, + "networkId": 1122334455 + }, + { + "name": "CyberdeckNet", + "chain": "cyberdeck", + "rpc": ["http://cybeth1.cyberdeck.eu:8545"], + "faucets": [], + "nativeCurrency": { "name": "Cyb", "symbol": "CYB", "decimals": 18 }, + "infoURL": "https://cyberdeck.eu", + "shortName": "cyb", + "chainId": 1146703430, + "networkId": 1146703430, + "icon": "cyberdeck", + "status": "active", + "explorers": [ + { + "name": "CybEthExplorer", + "url": "http://cybeth1.cyberdeck.eu:8000", + "icon": "cyberdeck", + "standard": "none" + } + ] + }, + { + "name": "HUMAN Protocol", + "title": "HUMAN Protocol", + "chain": "wan-red-ain", + "rpc": ["https://mainnet.skalenodes.com/v1/wan-red-ain"], + "faucets": ["https://dashboard.humanprotocol.org/faucet"], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://www.humanprotocol.org", + "shortName": "human-mainnet", + "chainId": 1273227453, + "networkId": 1273227453, + "explorers": [ + { + "name": "Blockscout", + "url": "https://wan-red-ain.explorer.mainnet.skalenodes.com", + "icon": "human", + "standard": "EIP3091" + } + ] + }, + { + "name": "Aurora Mainnet", + "chain": "NEAR", + "rpc": ["https://mainnet.aurora.dev"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://aurora.dev", + "shortName": "aurora", + "chainId": 1313161554, + "networkId": 1313161554, + "explorers": [ + { "name": "aurorascan.dev", "url": "https://aurorascan.dev", "standard": "EIP3091" } + ] + }, + { + "name": "Aurora Testnet", + "chain": "NEAR", + "rpc": ["https://testnet.aurora.dev/"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://aurora.dev", + "shortName": "aurora-testnet", + "chainId": 1313161555, + "networkId": 1313161555, + "explorers": [ + { "name": "aurorascan.dev", "url": "https://testnet.aurorascan.dev", "standard": "EIP3091" } + ] + }, + { + "name": "Aurora Betanet", + "chain": "NEAR", + "rpc": [], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://aurora.dev", + "shortName": "aurora-betanet", + "chainId": 1313161556, + "networkId": 1313161556 + }, + { + "name": "RaptorChain", + "chain": "RPTR", + "rpc": ["https://rpc.raptorchain.io/web3"], + "faucets": [], + "nativeCurrency": { "name": "Raptor", "symbol": "RPTR", "decimals": 18 }, + "features": [{ "name": "EIP155" }], + "infoURL": "https://raptorchain.io", + "shortName": "rptr", + "chainId": 1380996178, + "networkId": 1380996178, + "icon": "raptorchain", + "explorers": [ + { + "name": "RaptorChain Explorer", + "url": "https://explorer.raptorchain.io", + "icon": "raptorchain_explorer", + "standard": "EIP3091" + } + ] + }, + { + "name": "Nebula Mainnet", + "chain": "green-giddy-denebola", + "rpc": [ + "https://mainnet.skalenodes.com/v1/green-giddy-denebola", + "wss://mainnet-proxy.skalenodes.com/v1/ws/green-giddy-denebola" + ], + "faucets": [], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://nebulachain.io/", + "shortName": "nebula-mainnet", + "chainId": 1482601649, + "networkId": 1482601649, + "explorers": [ + { + "name": "nebula", + "url": "https://green-giddy-denebola.explorer.mainnet.skalenodes.com", + "icon": "nebula", + "standard": "EIP3091" + } + ] + }, + { + "name": "Calypso NFT Hub (SKALE)", + "title": "Calypso NFT Hub Mainnet", + "chain": "honorable-steel-rasalhague", + "rpc": ["https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague"], + "faucets": ["https://sfuel.dirtroad.dev"], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://calypsohub.network/", + "shortName": "calypso-mainnet", + "chainId": 1564830818, + "networkId": 1564830818, + "explorers": [ + { + "name": "Blockscout", + "url": "https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com", + "icon": "calypso", + "standard": "EIP3091" + } + ] + }, + { + "name": "Harmony Mainnet Shard 0", + "chain": "Harmony", + "rpc": ["https://api.harmony.one", "https://api.s0.t.hmny.io"], + "faucets": ["https://free-online-app.com/faucet-for-eth-evm-chains/"], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-s0", + "chainId": 1666600000, + "networkId": 1666600000, + "explorers": [ + { + "name": "Harmony Block Explorer", + "url": "https://explorer.harmony.one", + "standard": "EIP3091" + } + ] + }, + { + "name": "Harmony Mainnet Shard 1", + "chain": "Harmony", + "rpc": ["https://api.s1.t.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-s1", + "chainId": 1666600001, + "networkId": 1666600001 + }, + { + "name": "Harmony Mainnet Shard 2", + "chain": "Harmony", + "rpc": ["https://api.s2.t.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-s2", + "chainId": 1666600002, + "networkId": 1666600002 + }, + { + "name": "Harmony Mainnet Shard 3", + "chain": "Harmony", + "rpc": ["https://api.s3.t.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-s3", + "chainId": 1666600003, + "networkId": 1666600003 + }, + { + "name": "Harmony Testnet Shard 0", + "chain": "Harmony", + "rpc": ["https://api.s0.b.hmny.io"], + "faucets": ["https://faucet.pops.one"], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-b-s0", + "chainId": 1666700000, + "networkId": 1666700000, + "explorers": [ + { + "name": "Harmony Testnet Block Explorer", + "url": "https://explorer.pops.one", + "standard": "EIP3091" + } + ] + }, + { + "name": "Harmony Testnet Shard 1", + "chain": "Harmony", + "rpc": ["https://api.s1.b.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-b-s1", + "chainId": 1666700001, + "networkId": 1666700001 + }, + { + "name": "Harmony Testnet Shard 2", + "chain": "Harmony", + "rpc": ["https://api.s2.b.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-b-s2", + "chainId": 1666700002, + "networkId": 1666700002 + }, + { + "name": "Harmony Testnet Shard 3", + "chain": "Harmony", + "rpc": ["https://api.s3.b.hmny.io"], + "faucets": [], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-b-s3", + "chainId": 1666700003, + "networkId": 1666700003 + }, + { + "name": "Harmony Devnet Shard 0", + "chain": "Harmony", + "rpc": ["https://api.s1.ps.hmny.io", "https://api.s1.ps.hmny.io"], + "faucets": ["http://dev.faucet.easynode.one/"], + "nativeCurrency": { "name": "ONE", "symbol": "ONE", "decimals": 18 }, + "infoURL": "https://www.harmony.one/", + "shortName": "hmy-ps-s0", + "chainId": 1666900000, + "networkId": 1666900000, + "explorers": [ + { + "name": "Harmony Block Explorer", + "url": "https://explorer.ps.hmny.io", + "standard": "EIP3091" + } + ] + }, + { + "name": "DataHopper", + "chain": "HOP", + "rpc": ["https://23.92.21.121:8545"], + "faucets": [], + "nativeCurrency": { "name": "DataHoppers", "symbol": "HOP", "decimals": 18 }, + "infoURL": "https://www.DataHopper.com", + "shortName": "hop", + "chainId": 2021121117, + "networkId": 2021121117 + }, + { + "name": "Europa SKALE Chain", + "chain": "europa", + "icon": "europa", + "rpc": [ + "https://mainnet.skalenodes.com/v1/elated-tan-skat", + "wss://mainnet.skalenodes.com/v1/elated-tan-skat" + ], + "faucets": ["https://ruby.exchange/faucet.html", "https://sfuel.mylilius.com/"], + "nativeCurrency": { "name": "sFUEL", "symbol": "sFUEL", "decimals": 18 }, + "infoURL": "https://europahub.network/", + "shortName": "europa", + "chainId": 2046399126, + "networkId": 2046399126, + "explorers": [ + { + "name": "Blockscout", + "url": "https://elated-tan-skat.explorer.mainnet.skalenodes.com", + "standard": "EIP3091" + } + ], + "parent": { + "type": "L2", + "chain": "eip155-1", + "bridges": [{ "url": "https://ruby.exchange/bridge.html" }] + } + }, + { + "name": "Pirl", + "chain": "PIRL", + "rpc": ["https://wallrpc.pirl.io"], + "faucets": [], + "nativeCurrency": { "name": "Pirl Ether", "symbol": "PIRL", "decimals": 18 }, + "infoURL": "https://pirl.io", + "shortName": "pirl", + "chainId": 3125659152, + "networkId": 3125659152, + "slip44": 164 + }, + { + "name": "OneLedger Testnet Frankenstein", + "chain": "OLT", + "icon": "oneledger", + "rpc": ["https://frankenstein-rpc.oneledger.network"], + "faucets": ["https://frankenstein-faucet.oneledger.network"], + "nativeCurrency": { "name": "OLT", "symbol": "OLT", "decimals": 18 }, + "infoURL": "https://oneledger.io", + "shortName": "frankenstein", + "chainId": 4216137055, + "networkId": 4216137055, + "explorers": [ + { + "name": "OneLedger Block Explorer", + "url": "https://frankenstein-explorer.oneledger.network", + "standard": "EIP3091" + } + ] + }, + { + "name": "Palm Testnet", + "chain": "Palm", + "rpc": ["https://palm-testnet.infura.io/v3/${INFURA_API_KEY}"], + "faucets": [], + "nativeCurrency": { "name": "PALM", "symbol": "PALM", "decimals": 18 }, + "infoURL": "https://palm.io", + "shortName": "tpalm", + "chainId": 11297108099, + "networkId": 11297108099, + "explorers": [ + { + "name": "Palm Testnet Explorer", + "url": "https://explorer.palm-uat.xyz", + "standard": "EIP3091" + } + ] + }, + { + "name": "Palm", + "chain": "Palm", + "rpc": ["https://palm-mainnet.infura.io/v3/${INFURA_API_KEY}"], + "faucets": [], + "nativeCurrency": { "name": "PALM", "symbol": "PALM", "decimals": 18 }, + "infoURL": "https://palm.io", + "shortName": "palm", + "chainId": 11297108109, + "networkId": 11297108109, + "explorers": [ + { "name": "Palm Explorer", "url": "https://explorer.palm.io", "standard": "EIP3091" } + ] + }, + { + "name": "Ntity Mainnet", + "chain": "Ntity", + "rpc": ["https://rpc.ntity.io"], + "faucets": [], + "nativeCurrency": { "name": "Ntity", "symbol": "NTT", "decimals": 18 }, + "infoURL": "https://ntity.io", + "shortName": "ntt", + "chainId": 197710212030, + "networkId": 197710212030, + "icon": "ntity", + "explorers": [ + { + "name": "Ntity Blockscout", + "url": "https://blockscout.ntity.io", + "icon": "ntity", + "standard": "EIP3091" + } + ] + }, + { + "name": "Haradev Testnet", + "chain": "Ntity", + "rpc": ["https://blockchain.haradev.com"], + "faucets": [], + "nativeCurrency": { "name": "Ntity Haradev", "symbol": "NTTH", "decimals": 18 }, + "infoURL": "https://ntity.io", + "shortName": "ntt-haradev", + "chainId": 197710212031, + "networkId": 197710212031, + "icon": "ntity", + "explorers": [ + { + "name": "Ntity Haradev Blockscout", + "url": "https://blockscout.haradev.com", + "icon": "ntity", + "standard": "EIP3091" + } + ] + }, + { + "name": "Zeniq", + "chain": "ZENIQ", + "rpc": ["https://smart.zeniq.network:9545"], + "faucets": ["https://faucet.zeniq.net/"], + "nativeCurrency": { "name": "Zeniq", "symbol": "ZENIQ", "decimals": 18 }, + "infoURL": "https://www.zeniq.dev/", + "shortName": "zeniq", + "chainId": 383414847825, + "networkId": 383414847825, + "explorers": [ + { + "name": "zeniq-smart-chain-explorer", + "url": "https://smart.zeniq.net", + "standard": "EIP3091" + } + ] + }, + { + "name": "PDC Mainnet", + "chain": "IPDC", + "rpc": ["https://mainnet.ipdc.io/"], + "faucets": [], + "nativeCurrency": { "name": "PDC", "symbol": "PDC", "decimals": 18 }, + "infoURL": "https://ipdc.io", + "shortName": "ipdc", + "chainId": 666301171999, + "networkId": 666301171999, + "explorers": [{ "name": "ipdcscan", "url": "https://scan.ipdc.io", "standard": "EIP3091" }] + }, + { + "name": "Molereum Network", + "chain": "ETH", + "rpc": ["https://molereum.jdubedition.com"], + "faucets": [], + "nativeCurrency": { "name": "Molereum Ether", "symbol": "MOLE", "decimals": 18 }, + "infoURL": "https://github.com/Jdubedition/molereum", + "shortName": "mole", + "chainId": 6022140761023, + "networkId": 6022140761023 + }, + { + "name": "Godwoken Testnet (V1)", + "chain": "GWT", + "rpc": ["https://godwoken-testnet-web3-v1-rpc.ckbapp.dev"], + "faucets": ["https://homura.github.io/light-godwoken"], + "nativeCurrency": { "name": "CKB", "symbol": "CKB", "decimals": 8 }, + "infoURL": "https://www.nervos.org", + "shortName": "gw-testnet-v1-deprecated", + "chainId": 868455272153094, + "networkId": 868455272153094, + "status": "deprecated", + "explorers": [ + { "name": "GWScan Block Explorer", "url": "https://v1.aggron.gwscan.com", "standard": "none" } + ] + }, + { + "name": "Athena testnet", + "chain": "athena", + "rpc": ["https://athena-testnet.webb.tools"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://webb.tools", + "shortName": "athena", + "chainId": 3884533461, + "networkId": 3884533461, + "status": "testnet", + "explorers": [ + { + "name": "athena explorer", + "url": "https://athena-explorer.webb.tools", + "standard": "eip3091" + } + ] + }, + { + "name": "Hermes testnet", + "chain": "hermes", + "rpc": ["https://hermes-testnet.webb.tools"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://webb.tools", + "shortName": "hermes", + "chainId": 3884533462, + "networkId": 3884533462, + "status": "testnet", + "explorers": [ + { + "name": "hermes explorer", + "url": "https://hermes-explorer.webb.tools", + "standard": "eip3091" + } + ] + }, + { + "name": "Demeter testnet", + "chain": "demeter", + "rpc": ["https://demeter-testnet.webb.tools"], + "faucets": [], + "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, + "infoURL": "https://webb.tools", + "shortName": "demeter", + "chainId": 3884533463, + "networkId": 3884533463, + "status": "testnet", + "explorers": [ + { + "name": "demeter explorer", + "url": "https://demeter-explorer.webb.tools", + "standard": "eip3091" + } + ] + } +] diff --git a/crates/chains-info/fixtures/coingecko_coins_list.json b/crates/chains-info/fixtures/coingecko_coins_list.json new file mode 100644 index 000000000..c3b9307a4 --- /dev/null +++ b/crates/chains-info/fixtures/coingecko_coins_list.json @@ -0,0 +1 @@ +[{"id":"01coin","symbol":"zoc","name":"01coin"},{"id":"0chain","symbol":"zcn","name":"Zus"},{"id":"0vix-protocol","symbol":"vix","name":"0VIX Protocol"},{"id":"0x","symbol":"zrx","name":"0x"},{"id":"0x0-ai-ai-smart-contract","symbol":"0x0","name":"0x0.ai: AI Smart Contract"},{"id":"0xdao","symbol":"oxd","name":"0xDAO"},{"id":"0xdao-v2","symbol":"oxd v2","name":"0xDAO V2"},{"id":"0xmonero","symbol":"0xmr","name":"0xMonero"},{"id":"0xtrade","symbol":"0xt","name":"0xTrade"},{"id":"0xwallet-token","symbol":"0xw","name":"0xWallet Token"},{"id":"12405-santa-rosa","symbol":"realt-s-12405-santa-rosa-dr-detroit-mi","name":"RealT - 12405 Santa Rosa Dr, Detroit, MI 48204"},{"id":"12ships","symbol":"tshp","name":"12Ships"},{"id":"1337","symbol":"1337","name":"Elite"},{"id":"14066-santa-rosa","symbol":"realt-s-14066-santa-rosa-dr-detroit-mi","name":"RealT - 14066 Santa Rosa Dr, Detroit, MI 48238"},{"id":"1617-s-avers","symbol":"realt-s-1617-s.avers-ave-chicago-il","name":"RealT - 1617 S Avers Ave, Chicago, IL 60623"},{"id":"1art","symbol":"1art","name":"OneArt"},{"id":"1bch","symbol":"1bch","name":"1BCH"},{"id":"1box","symbol":"1box","name":"1BOX"},{"id":"1doge","symbol":"1doge","name":"1Doge"},{"id":"1eco","symbol":"1eco","name":"1eco"},{"id":"1eth","symbol":"1eth","name":"1ETH"},{"id":"1hive-water","symbol":"water","name":"1Hive Water"},{"id":"1inch","symbol":"1inch","name":"1inch"},{"id":"1inch-yvault","symbol":"yv1inch","name":"1INCH yVault"},{"id":"1million-nfts","symbol":"1mil","name":"1MillionNFTs"},{"id":"1minbet","symbol":"1mb","name":"1minBET"},{"id":"1move token","symbol":"1mt","name":"1Move Token"},{"id":"1peco","symbol":"1peco","name":"1peco"},{"id":"1reward-token","symbol":"1rt","name":"1Reward Token"},{"id":"1safu","symbol":"safu","name":"1SAFU"},{"id":"1sol","symbol":"1sol","name":"1Sol"},{"id":"1sol-io-wormhole","symbol":"1sol","name":"1sol.io (Wormhole)"},{"id":"1-up","symbol":"1-up","name":"1-UP"},{"id":"1world","symbol":"1wo","name":"1World"},{"id":"2044-nuclear-apocalypse","symbol":"2044","name":"2044 Nuclear Apocalypse"},{"id":"20weth-80bal","symbol":"20weth-80bal","name":"20WETH-80BAL"},{"id":"28vck","symbol":"vck","name":"28VCK"},{"id":"2acoin","symbol":"arms","name":"2ACoin"},{"id":"2crazynft","symbol":"2crz","name":"2crazyNFT"},{"id":"2g-carbon-coin","symbol":"2gcc","name":"2G Carbon Coin"},{"id":"2local-2","symbol":"2lc","name":"2local"},{"id":"2omb-finance","symbol":"2omb","name":"2omb"},{"id":"2share","symbol":"2shares","name":"2SHARE"},{"id":"300fit","symbol":"fit","name":"300FIT"},{"id":"30mb-token","symbol":"3omb","name":"3OMB"},{"id":"3air","symbol":"3air","name":"3air"},{"id":"3gg","symbol":"3gg","name":"3gg"},{"id":"3-kingdoms-multiverse","symbol":"3km","name":"3 Kingdoms Multiverse"},{"id":"3shares","symbol":"3share","name":"3Share"},{"id":"3xcalibur","symbol":"xcal","name":"3xcalibur Ecosystem Token"},{"id":"42-coin","symbol":"42","name":"42-coin"},{"id":"4852-4854-w-cortez","symbol":"realt-s-4852-4854-w.cortez-st-chicago-il","name":"RealT - 4852-4854 W Cortez St, Chicago, IL 60651"},{"id":"4artechnologies","symbol":"4art","name":"4ART Coin"},{"id":"4d-twin-maps","symbol":"map","name":"4D Twin Maps"},{"id":"4int","symbol":"4int","name":"4INT"},{"id":"4jnet","symbol":"4jnet","name":"4JNET"},{"id":"4mw","symbol":"4mw","name":"4 Meta World"},{"id":"4play","symbol":"4play","name":"4PLAY"},{"id":"50cent","symbol":"50c","name":"50Cent"},{"id":"5g-cash","symbol":"vgc","name":"5G-CASH"},{"id":"5km-run","symbol":"run","name":"5KM RUN"},{"id":"7pixels","symbol":"7pxs","name":"7Pixels"},{"id":"888tron","symbol":"888","name":"888tron"},{"id":"88mph","symbol":"mph","name":"88mph"},{"id":"8bitearn","symbol":"8bit","name":"8BITEARN"},{"id":"8pay","symbol":"8pay","name":"8Pay"},{"id":"8x8-protocol","symbol":"exe","name":"8X8 Protocol"},{"id":"99defi","symbol":"99defi","name":"99Defi"},{"id":"99starz","symbol":"stz","name":"99Starz"},{"id":"9-lives-network","symbol":"ninefi","name":"9 Lives Network"},{"id":"a4-finance","symbol":"a4","name":"A4 Finance"},{"id":"aada-finance","symbol":"aada","name":"Aada Finance"},{"id":"aag-ventures","symbol":"aag","name":"AAG"},{"id":"aave","symbol":"aave","name":"Aave"},{"id":"aave-aave","symbol":"aaave","name":"Aave AAVE"},{"id":"aave-amm-bptbalweth","symbol":"aammbptbalweth","name":"Aave AMM BptBALWETH"},{"id":"aave-amm-bptwbtcweth","symbol":"aammbptwbtcweth","name":"Aave AMM BptWBTCWETH"},{"id":"aave-amm-dai","symbol":"aammdai","name":"Aave AMM DAI"},{"id":"aave-amm-uniaaveweth","symbol":"aammuniaaveweth","name":"Aave AMM UniAAVEWETH"},{"id":"aave-amm-unibatweth","symbol":"aammunibatweth","name":"Aave AMM UniBATWETH"},{"id":"aave-amm-unicrvweth","symbol":"aammunicrvweth","name":"Aave AMM UniCRVWETH"},{"id":"aave-amm-unidaiusdc","symbol":"aammunidaiusdc","name":"Aave AMM UniDAIUSDC"},{"id":"aave-amm-unidaiweth","symbol":"aammunidaiweth","name":"Aave AMM UniDAIWETH"},{"id":"aave-amm-unilinkweth","symbol":"aammunilinkweth","name":"Aave AMM UniLINKWETH"},{"id":"aave-amm-unimkrweth","symbol":"aammunimkrweth","name":"Aave AMM UniMKRWETH"},{"id":"aave-amm-unirenweth","symbol":"aammunirenweth","name":"Aave AMM UniRENWETH"},{"id":"aave-amm-unisnxweth","symbol":"aammunisnxweth","name":"Aave AMM UniSNXWETH"},{"id":"aave-amm-uniuniweth","symbol":"aammuniuniweth","name":"Aave AMM UniUNIWETH"},{"id":"aave-amm-uniusdcweth","symbol":"aammuniusdcweth","name":"Aave AMM UniUSDCWETH"},{"id":"aave-amm-uniwbtcusdc","symbol":"aammuniwbtcusdc","name":"Aave AMM UniWBTCUSDC"},{"id":"aave-amm-uniwbtcweth","symbol":"aammuniwbtcweth","name":"Aave AMM UniWBTCWETH"},{"id":"aave-amm-uniyfiweth","symbol":"aammuniyfiweth","name":"Aave AMM UniYFIWETH"},{"id":"aave-amm-usdc","symbol":"aammusdc","name":"Aave AMM USDC"},{"id":"aave-amm-usdt","symbol":"aammusdt","name":"Aave AMM USDT"},{"id":"aave-amm-wbtc","symbol":"aammwbtc","name":"Aave AMM WBTC"},{"id":"aave-amm-weth","symbol":"aammweth","name":"Aave AMM WETH"},{"id":"aave-bal","symbol":"abal","name":"Aave BAL"},{"id":"aave-balancer-pool-token","symbol":"abpt","name":"Aave Balancer Pool Token"},{"id":"aave-bat","symbol":"abat","name":"Aave BAT"},{"id":"aave-bat-v1","symbol":"abat","name":"Aave BAT v1"},{"id":"aave-busd","symbol":"abusd","name":"Aave BUSD"},{"id":"aave-busd-v1","symbol":"abusd","name":"Aave BUSD v1"},{"id":"aave-crv","symbol":"acrv","name":"Aave CRV"},{"id":"aave-dai","symbol":"adai","name":"Aave DAI"},{"id":"aave-dai-v1","symbol":"adai","name":"Aave DAI v1"},{"id":"aave-enj","symbol":"aenj","name":"Aave ENJ"},{"id":"aave-enj-v1","symbol":"aenj","name":"Aave ENJ v1"},{"id":"aave-eth-v1","symbol":"aeth","name":"Aave ETH v1"},{"id":"aavegotchi","symbol":"ghst","name":"Aavegotchi"},{"id":"aavegotchi-alpha","symbol":"alpha","name":"Aavegotchi ALPHA"},{"id":"aavegotchi-fomo","symbol":"fomo","name":"Aavegotchi FOMO"},{"id":"aavegotchi-fud","symbol":"fud","name":"Aavegotchi FUD"},{"id":"aavegotchi-kek","symbol":"kek","name":"Aavegotchi KEK"},{"id":"aave-gusd","symbol":"agusd","name":"Aave GUSD"},{"id":"aave-interest-bearing-steth","symbol":"asteth","name":"Aave Interest Bearing STETH"},{"id":"aave-knc","symbol":"aknc","name":"Aave KNC"},{"id":"aave-knc-v1","symbol":"aknc","name":"Aave KNC v1"},{"id":"aave-link","symbol":"alink","name":"Aave LINK"},{"id":"aave-link-v1","symbol":"alink","name":"Aave LINK v1"},{"id":"aave-mana","symbol":"amana","name":"Aave MANA"},{"id":"aave-mana-v1","symbol":"amana","name":"Aave MANA v1"},{"id":"aave-mkr","symbol":"amkr","name":"Aave MKR"},{"id":"aave-mkr-v1","symbol":"amkr","name":"Aave MKR v1"},{"id":"aave-polygon-aave","symbol":"amaave","name":"Aave Polygon AAVE"},{"id":"aave-polygon-dai","symbol":"amdai","name":"Aave Polygon DAI"},{"id":"aave-polygon-usdc","symbol":"amusdc","name":"Aave Polygon USDC"},{"id":"aave-polygon-usdt","symbol":"amusdt","name":"Aave Polygon USDT"},{"id":"aave-polygon-wbtc","symbol":"amwbtc","name":"Aave Polygon WBTC"},{"id":"aave-polygon-weth","symbol":"amweth","name":"Aave Polygon WETH"},{"id":"aave-polygon-wmatic","symbol":"amwmatic","name":"Aave Polygon WMATIC"},{"id":"aave-rai","symbol":"arai","name":"Aave RAI"},{"id":"aave-ren","symbol":"aren","name":"Aave REN"},{"id":"aave-ren-v1","symbol":"aren","name":"Aave REN v1"},{"id":"aave-snx","symbol":"asnx","name":"Aave SNX"},{"id":"aave-snx-v1","symbol":"asnx","name":"Aave SNX v1"},{"id":"aave-susd","symbol":"asusd","name":"Aave SUSD"},{"id":"aave-susd-v1","symbol":"asusd","name":"Aave SUSD v1"},{"id":"aave-tusd","symbol":"atusd","name":"Aave TUSD"},{"id":"aave-tusd-v1","symbol":"atusd","name":"Aave TUSD v1"},{"id":"aave-uni","symbol":"auni","name":"Aave UNI"},{"id":"aave-usdc","symbol":"ausdc","name":"Aave USDC"},{"id":"aave-usdc-v1","symbol":"ausdc","name":"Aave USDC v1"},{"id":"aave-usdt","symbol":"ausdt","name":"Aave USDT"},{"id":"aave-usdt-v1","symbol":"ausdt","name":"Aave USDT v1"},{"id":"aave-wbtc","symbol":"awbtc","name":"Aave WBTC"},{"id":"aave-wbtc-v1","symbol":"awbtc","name":"Aave WBTC v1"},{"id":"aave-weth","symbol":"aweth","name":"Aave WETH"},{"id":"aave-xsushi","symbol":"axsushi","name":"Aave XSUSHI"},{"id":"aave-yfi","symbol":"ayfi","name":"Aave YFI"},{"id":"aave-yvault","symbol":"yvaave","name":"Aave yVault"},{"id":"aave-zrx","symbol":"azrx","name":"Aave ZRX"},{"id":"aave-zrx-v1","symbol":"azrx","name":"Aave ZRX v1"},{"id":"abachi","symbol":"abi","name":"Abachi"},{"id":"abased","symbol":"$abased","name":"aBASED"},{"id":"abcmeta","symbol":"meta","name":"ABCMETA"},{"id":"abel-finance","symbol":"abel","name":"ABEL Finance"},{"id":"abell-coin","symbol":"abc","name":"Abell Coin"},{"id":"abey","symbol":"abey","name":"Abey"},{"id":"able-finance","symbol":"able","name":"Able Finance"},{"id":"aboat-token","symbol":"aboat","name":"Aboat Token"},{"id":"absolute-sync-token","symbol":"ast","name":"Absolute Sync"},{"id":"acala","symbol":"aca","name":"Acala"},{"id":"acala-dollar","symbol":"ausd","name":"Acala Dollar (Karura)"},{"id":"acala-dollar-acala","symbol":"ausd","name":"Acala Dollar (Acala)"},{"id":"aca-token","symbol":"aca","name":"ACA"},{"id":"access-protocol","symbol":"acs","name":"Access Protocol"},{"id":"acent","symbol":"ace","name":"Acent"},{"id":"acestarter","symbol":"astar","name":"AceStarter"},{"id":"acetoken","symbol":"ace","name":"ACEToken"},{"id":"acet-token","symbol":"act","name":"Acet"},{"id":"ac-exchange-token","symbol":"acxt","name":"ACDX Exchange"},{"id":"achain","symbol":"act","name":"Achain"},{"id":"acid","symbol":"acid","name":"Acid"},{"id":"acknoledger","symbol":"ack","name":"AcknoLedger"},{"id":"ac-milan-fan-token","symbol":"acm","name":"AC Milan Fan Token"},{"id":"acoconut","symbol":"ac","name":"ACoconut"},{"id":"acoin","symbol":"acoin","name":"Acoin"},{"id":"acquire-fi","symbol":"acq","name":"Acquire.Fi"},{"id":"acreage-coin","symbol":"acr","name":"Acreage Coin"},{"id":"acria","symbol":"acria","name":"Acria.AI"},{"id":"across-protocol","symbol":"acx","name":"Across Protocol"},{"id":"acryptos","symbol":"acs","name":"ACryptoS"},{"id":"acryptosi","symbol":"acsi","name":"ACryptoSI"},{"id":"actinium","symbol":"acm","name":"Actinium"},{"id":"action-coin","symbol":"actn","name":"Action Coin"},{"id":"active-world-rewards-token","symbol":"awrt","name":"Active World Rewards"},{"id":"acute-angle-cloud","symbol":"aac","name":"Double-A Chain"},{"id":"acy-finance","symbol":"acy","name":"ACY Finance"},{"id":"adacash","symbol":"adacash","name":"ADAcash"},{"id":"adadao","symbol":"adao","name":"ADADao"},{"id":"adalend","symbol":"adal","name":"Adalend"},{"id":"adamant","symbol":"addy","name":"Adamant"},{"id":"adana-demirspor","symbol":"demir","name":"Adana Demirspor"},{"id":"adanaspor-fan-token","symbol":"adana","name":"Adanaspor Fan Token"},{"id":"adapad","symbol":"adapad","name":"ADAPad"},{"id":"adappter-token","symbol":"adp","name":"Adappter"},{"id":"adaswap","symbol":"asw","name":"AdaSwap"},{"id":"adax","symbol":"adax","name":"ADAX"},{"id":"adazoo","symbol":"zoo","name":"ADAZOO"},{"id":"add-xyz-new","symbol":"add","name":"Add.xyz (NEW)"},{"id":"adex","symbol":"adx","name":"Ambire AdEx"},{"id":"ad-flex-token","symbol":"adf","name":"Ad Flex"},{"id":"aditus","symbol":"adi","name":"Aditus"},{"id":"ado-network","symbol":"ado","name":"ADO.Network"},{"id":"adonis-2","symbol":"adon","name":"Adonis"},{"id":"adora-token","symbol":"ara","name":"Adora"},{"id":"adreward","symbol":"ad","name":"ADreward"},{"id":"adroverse","symbol":"adr","name":"Adroverse"},{"id":"adshares","symbol":"ads","name":"Adshares"},{"id":"adtoken","symbol":"adt","name":"adChain"},{"id":"adv3nture-xyz-gemstone","symbol":"gem","name":"Adv3nture.xyz Gemstone"},{"id":"adv3nture-xyz-gold","symbol":"gold","name":"Adv3nture.xyz Gold"},{"id":"advanced-internet-block","symbol":"aib","name":"Advanced Integrated Blocks"},{"id":"advanced-united-continent","symbol":"auc","name":"Advanced United Continent"},{"id":"advantis","symbol":"advt","name":"Advantis"},{"id":"adventure-gold","symbol":"agld","name":"Adventure Gold"},{"id":"adventurer-gold","symbol":"gold","name":"Adventurer Gold"},{"id":"advertise-coin","symbol":"adco","name":"Advertise Coin"},{"id":"aeggs","symbol":"aeggs","name":"aEGGS"},{"id":"aegis","symbol":"ags","name":"Aegis"},{"id":"aegis-token-f7934368-2fb3-4091-9edc-39283e87f55d","symbol":"aeg","name":"Aegis Token"},{"id":"aelf","symbol":"elf","name":"aelf"},{"id":"aelin","symbol":"aelin","name":"Aelin"},{"id":"aelysir","symbol":"ael","name":"Aelysir"},{"id":"aen-smart-token","symbol":"aens","name":"AEN Smart"},{"id":"aeon","symbol":"aeon","name":"Aeon"},{"id":"aerarium-fi","symbol":"aera","name":"Aerarium Fi"},{"id":"aerdrop","symbol":"aer","name":"Aerdrop"},{"id":"aergo","symbol":"aergo","name":"Aergo"},{"id":"aeron","symbol":"arnx","name":"Aeron"},{"id":"aerotyne","symbol":"atyne","name":"Aerotyne"},{"id":"aerovek-aviation","symbol":"aero","name":"Aerovek Aviation"},{"id":"aeternity","symbol":"ae","name":"Aeternity"},{"id":"aetherius","symbol":"aeth","name":"Aetherius"},{"id":"aeur","symbol":"aeur","name":"AEUR"},{"id":"aezora","symbol":"azr","name":"Azzure"},{"id":"afen-blockchain","symbol":"afen","name":"AFEN Blockchain"},{"id":"affinity","symbol":"afnty","name":"Affinity"},{"id":"affyn","symbol":"fyn","name":"Affyn"},{"id":"afin-coin","symbol":"afin","name":"Asian Fintech"},{"id":"afkdao","symbol":"afk","name":"AFKDAO"},{"id":"afrep","symbol":"afrep","name":"Afrep"},{"id":"afreum","symbol":"afr","name":"Afreum"},{"id":"afrix","symbol":"afx","name":"Afrix"},{"id":"afrostar","symbol":"afro","name":"Afrostar"},{"id":"afyonspor-fan-token","symbol":"afyon","name":"Afyonspor Fan Token"},{"id":"aga-carbon-credit","symbol":"agac","name":"AGA Carbon Credit"},{"id":"aga-carbon-rewards","symbol":"acar","name":"AGA Carbon Rewards"},{"id":"agame","symbol":"ag","name":"AGAME"},{"id":"aga-rewards","symbol":"edc","name":"Edcoin"},{"id":"aga-token","symbol":"aga","name":"AGA"},{"id":"agavecoin","symbol":"agvc","name":"AgaveCoin"},{"id":"agave-token","symbol":"agve","name":"Agave"},{"id":"agenor","symbol":"age","name":"Agenor"},{"id":"ageofgods","symbol":"aog","name":"AgeOfGods"},{"id":"age-of-tanks","symbol":"a.o.t","name":"Age Of Tanks"},{"id":"age-of-zalmoxis-koson","symbol":"koson","name":"Age of Zalmoxis KOSON"},{"id":"ageur","symbol":"ageur","name":"agEUR"},{"id":"ageur-plenty-bridge","symbol":"egeur.e","name":"agEUR (Plenty Bridge)"},{"id":"aggle-io","symbol":"aggl","name":"aggle.io"},{"id":"aggregated-finance","symbol":"agfi","name":"Aggregated Finance"},{"id":"agile","symbol":"agl","name":"Agile"},{"id":"agora-defi","symbol":"agora","name":"Agora Defi"},{"id":"agoras-currency-of-tau","symbol":"agrs","name":"Agoras: Currency of Tau"},{"id":"agoric","symbol":"bld","name":"Agoric"},{"id":"agrello","symbol":"dlt","name":"Agrello"},{"id":"agricoin","symbol":"agn","name":"Agricoin"},{"id":"agrinode","symbol":"agn","name":"AgriNode"},{"id":"agro-global","symbol":"agro","name":"Agro Global"},{"id":"agronomist","symbol":"agte","name":"Agronomist"},{"id":"agx-coin","symbol":"agx","name":"AGX Coin"},{"id":"ahatoken","symbol":"aht","name":"AhaToken"},{"id":"a-hunters-dream","symbol":"caw","name":"A Hunters Dream"},{"id":"aibra","symbol":"abr","name":"AIBRA"},{"id":"ai-card-render","symbol":"acr","name":"AI Card Render"},{"id":"aichain","symbol":"ait","name":"AICHAIN"},{"id":"aicon","symbol":"aico","name":"Aicon"},{"id":"aidcoin","symbol":"aid","name":"AidCoin"},{"id":"aidi-finance","symbol":"aidi","name":"Aidi Finance [OLD]"},{"id":"aidi-finance-2","symbol":"aidi","name":"Aidi Finance"},{"id":"ai-dogemini","symbol":"aidogemini","name":"AI DogeMini"},{"id":"aidos-kuneen","symbol":"adk","name":"Aidos Kuneen"},{"id":"ai-floki","symbol":"aifloki","name":"AI Floki"},{"id":"aimedis-new","symbol":"aimx","name":"Aimedis (NEW)"},{"id":"ai-network","symbol":"ain","name":"AI Network"},{"id":"ainu-token","symbol":"ainu","name":"Ainu"},{"id":"aion","symbol":"aion","name":"Aion"},{"id":"aioz-network","symbol":"aioz","name":"AIOZ Network"},{"id":"aipad","symbol":"aipad","name":"AIPad"},{"id":"aipro","symbol":"aipro","name":"AIPRO"},{"id":"airbloc-protocol","symbol":"abl","name":"Airbloc"},{"id":"aircoin-2","symbol":"air","name":"AirCoin"},{"id":"aircoins","symbol":"airx","name":"Aircoins"},{"id":"airight","symbol":"airi","name":"aiRight"},{"id":"airnft-token","symbol":"airt","name":"AirNFT"},{"id":"airswap","symbol":"ast","name":"AirSwap"},{"id":"airtnt","symbol":"airtnt","name":"AirTnT"},{"id":"airtor-protocol","symbol":"ator","name":"AirTor Protocol"},{"id":"ai-smart-chain","symbol":"aisc","name":"Ai Smart Chain"},{"id":"ai-trader","symbol":"ait","name":"AI Trader"},{"id":"aitravis","symbol":"tai","name":"AITravis"},{"id":"aiwallet","symbol":"aiwallet","name":"AiWallet"},{"id":"aiwork","symbol":"awo","name":"AiWork"},{"id":"ajuna-network","symbol":"baju","name":"Ajuna Network"},{"id":"akash-network","symbol":"akt","name":"Akash Network"},{"id":"akil-coin","symbol":"akl","name":"Akil Coin"},{"id":"aki-protocol","symbol":"aki","name":"Aki Protocol"},{"id":"akita-dao","symbol":"hachi","name":"Akita DAO"},{"id":"akita-inu","symbol":"akita","name":"Akita Inu"},{"id":"akita-inu-asa","symbol":"akta","name":"Akita Inu ASA"},{"id":"akitavax","symbol":"akitax","name":"Akitavax"},{"id":"akitsuki","symbol":"aki","name":"Akitsuki"},{"id":"akoin","symbol":"akn","name":"Akoin"},{"id":"akroma","symbol":"aka","name":"Akroma"},{"id":"akropolis","symbol":"akro","name":"Akropolis"},{"id":"akropolis-delphi","symbol":"adel","name":"Delphi"},{"id":"aktio","symbol":"aktio","name":"Aktio"},{"id":"aladdin-cvxcrv","symbol":"acrv","name":"Aladdin cvxCRV"},{"id":"aladdin-dao","symbol":"ald","name":"Aladdin DAO"},{"id":"alanyaspor-fan-token","symbol":"ala","name":"Alanyaspor Fan Token"},{"id":"alaya","symbol":"atp","name":"Alaya"},{"id":"albedo","symbol":"albedo","name":"ALBEDO"},{"id":"alcazar","symbol":"alcazar","name":"Alcazar"},{"id":"alchemist","symbol":"mist","name":"Alchemist"},{"id":"alchemix","symbol":"alcx","name":"Alchemix"},{"id":"alchemix-eth","symbol":"aleth","name":"Alchemix ETH"},{"id":"alchemix-usd","symbol":"alusd","name":"Alchemix USD"},{"id":"alchemy-pay","symbol":"ach","name":"Alchemy Pay"},{"id":"aldrin","symbol":"rin","name":"Aldrin"},{"id":"aleph","symbol":"aleph","name":"Aleph.im"},{"id":"alephium","symbol":"alph","name":"Alephium"},{"id":"aleph-zero","symbol":"azero","name":"Aleph Zero"},{"id":"alert","symbol":"alert","name":"ALERT"},{"id":"alethea-artificial-liquid-intelligence-token","symbol":"ali","name":"Artificial Liquid Intelligence"},{"id":"alexgo","symbol":"alex","name":"ALEX Lab"},{"id":"alfa-romeo-racing-orlen-fan-token","symbol":"sauber","name":"Alfa Romeo Racing ORLEN Fan Token"},{"id":"alfprotocol","symbol":"alf","name":"AlfProtocol"},{"id":"algebra","symbol":"algb","name":"Algebra"},{"id":"algoblocks","symbol":"algoblk","name":"AlgoBlocks"},{"id":"algodao","symbol":"adao","name":"AlgoDAO"},{"id":"algofund","symbol":"algf","name":"AlgoFund"},{"id":"algomint","symbol":"gomint","name":"Algomint"},{"id":"algorand","symbol":"algo","name":"Algorand"},{"id":"algory","symbol":"alg","name":"Algory"},{"id":"algostable","symbol":"stbl","name":"AlgoStable"},{"id":"algostake","symbol":"stke","name":"AlgoStake"},{"id":"alibabacoin","symbol":"abbc","name":"ABBC"},{"id":"alibaba-tokenized-stock-defichain","symbol":"dbaba","name":"Alibaba Tokenized Stock Defichain"},{"id":"alicenet","symbol":"alca","name":"AliceNet"},{"id":"alien-chicken-farm","symbol":"acf","name":"Alien Chicken Farm"},{"id":"alienfi","symbol":"alien","name":"AlienFi"},{"id":"alien-inu","symbol":"alien","name":"Alien Inu"},{"id":"alienswap","symbol":"alien","name":"AlienSwap"},{"id":"alien-worlds","symbol":"tlm","name":"Alien Worlds"},{"id":"alif-coin","symbol":"alif","name":"AliF Coin"},{"id":"alink-ai","symbol":"alink","name":"ALINK AI"},{"id":"alita","symbol":"ali","name":"Alita"},{"id":"alitas","symbol":"alt","name":"Alitas"},{"id":"alium-finance","symbol":"alm","name":"Alium Finance"},{"id":"alkimi","symbol":"$ads","name":"Alkimi"},{"id":"all-art","symbol":"aart","name":"ALL.ART"},{"id":"allbridge","symbol":"abr","name":"Allbridge"},{"id":"all-coins-yield-capital","symbol":"acyc","name":"All Coins Yield Capital"},{"id":"allianceblock","symbol":"albt","name":"AllianceBlock"},{"id":"allianceblock-nexera","symbol":"nxra","name":"AllianceBlock Nexera"},{"id":"alliance-fan-token","symbol":"all","name":"Alliance Fan Token"},{"id":"alliance-x-trading","symbol":"axt","name":"Alliance X Trading"},{"id":"all-in","symbol":"allin","name":"All In"},{"id":"all-in-ai","symbol":"aiai","name":"ALLINAI"},{"id":"all-in-gpt","symbol":"aigpt","name":"All In GPT"},{"id":"allium-finance","symbol":"alm","name":"Allium Finance"},{"id":"all-me","symbol":"me","name":"All.me"},{"id":"allpaycoin","symbol":"apcg","name":"ALLPAYCOIN"},{"id":"allsafe","symbol":"asafe","name":"AllSafe"},{"id":"all-sports-2","symbol":"soc","name":"All Sports"},{"id":"allstars","symbol":"asx","name":"AllStars"},{"id":"all-time-high","symbol":"ath","name":"All Time High"},{"id":"alluo","symbol":"alluo","name":"Alluo"},{"id":"ally","symbol":"aly","name":"Ally"},{"id":"ally-direct","symbol":"drct","name":"Ally Direct"},{"id":"almira-wallet","symbol":"almr","name":"Almira Wallet"},{"id":"almond","symbol":"alm","name":"Almond"},{"id":"alnair-finance-nika","symbol":"nika","name":"Alnair Finance NIKA"},{"id":"alnassr-fc-fan-token","symbol":"nassr","name":"Alnassr FC Fan Token"},{"id":"aloha","symbol":"aloha","name":"Aloha"},{"id":"alongside-crypto-market-index","symbol":"amkt","name":"Alongside Crypto Market Index"},{"id":"alon-mars","symbol":"alonmars","name":"Alon Mars"},{"id":"alpaca","symbol":"alpa","name":"Alpaca City"},{"id":"alpaca-finance","symbol":"alpaca","name":"Alpaca Finance"},{"id":"alpha5","symbol":"a5t","name":"Alpha5"},{"id":"alpha-brain-capital-2","symbol":"acap","name":"Alpha Capital"},{"id":"alphacoin","symbol":"alpha","name":"Alpha Coin"},{"id":"alpha-dex","symbol":"roar","name":"Alpha DEX"},{"id":"alpha-finance","symbol":"alpha","name":"Alpha Venture DAO"},{"id":"alpha-intelligence","symbol":"$ai","name":"Alpha Intelligence"},{"id":"alpha-quark-token","symbol":"aqt","name":"Alpha Quark"},{"id":"alpharushai","symbol":"rushai","name":"AlphaRushAI"},{"id":"alpha-shares-v2","symbol":"$alpha","name":"Alpha Shares V2"},{"id":"alphatoken","symbol":".alpha","name":".Alpha"},{"id":"alphr","symbol":"alphr","name":"Alphr"},{"id":"alpine-f1-team-fan-token","symbol":"alpine","name":"Alpine F1 Team Fan Token"},{"id":"altair","symbol":"air","name":"Altair"},{"id":"altava","symbol":"tava","name":"ALTAVA"},{"id":"altbase","symbol":"altb","name":"Altbase"},{"id":"altcommunity-coin","symbol":"altom","name":"ALTOM"},{"id":"alter","symbol":"alter","name":"Alter"},{"id":"altered-state-token","symbol":"asto","name":"Altered State Machine"},{"id":"altfins","symbol":"afins","name":"altFINS"},{"id":"altfolio","symbol":"alt","name":"altfolio"},{"id":"alt-markets","symbol":"amx","name":"Alt Markets"},{"id":"altrucoin-2","symbol":"altrucoin","name":"Altrucoin"},{"id":"altswitch","symbol":"alts","name":"AltSwitch"},{"id":"altura","symbol":"alu","name":"Altura"},{"id":"aluna","symbol":"aln","name":"Aluna"},{"id":"alvey-chain","symbol":"walv","name":"Alvey Chain"},{"id":"alyattes","symbol":"alya","name":"Alyattes"},{"id":"amasa","symbol":"amas","name":"Amasa"},{"id":"amateras","symbol":"amt","name":"Amateras"},{"id":"amaterasufi-izanagi","symbol":"iza","name":"AmaterasuFi Izanagi"},{"id":"amaurot","symbol":"ama","name":"AMAUROT"},{"id":"amax-network","symbol":"amax","name":"AMAX Network"},{"id":"amazewallet","symbol":"amt","name":"AmazeToken"},{"id":"amazingdoge","symbol":"adoge","name":"AmazingDoge"},{"id":"amazingteamdao","symbol":"amazingteam","name":"AmazingTeamDAO"},{"id":"amazon-tokenized-stock-defichain","symbol":"damzn","name":"Amazon Tokenized Stock Defichain"},{"id":"amazy","symbol":"azy","name":"Amazy"},{"id":"amazy-move-token","symbol":"amt","name":"Amazy Move Token"},{"id":"amber","symbol":"amb","name":"AirDAO"},{"id":"ambire-wallet","symbol":"wallet","name":"Ambire Wallet"},{"id":"amdg-token","symbol":"amdg","name":"AMDG"},{"id":"amepay","symbol":"ame","name":"AME Chain"},{"id":"american-shiba","symbol":"ushiba","name":"American Shiba"},{"id":"ameta","symbol":"$aplus","name":"AMETA"},{"id":"amgen","symbol":"amg","name":"Amgen"},{"id":"amlp","symbol":"amlp","name":"aMLP"},{"id":"ammf","symbol":"ammf","name":"aMMF"},{"id":"ammyi-coin","symbol":"ami","name":"AMMYI Coin"},{"id":"amo","symbol":"amo","name":"AMO Coin"},{"id":"amon","symbol":"amn","name":"Amon"},{"id":"amond","symbol":"amon","name":"AmonD"},{"id":"ampleforth","symbol":"ampl","name":"Ampleforth"},{"id":"ampleforth-governance-token","symbol":"forth","name":"Ampleforth Governance"},{"id":"ampleswap","symbol":"ample","name":"AmpleSwap"},{"id":"amplifi-dao","symbol":"agg","name":"AmpliFi DAO"},{"id":"ampnet","symbol":"aapx","name":"AMPnet"},{"id":"amp-token","symbol":"amp","name":"Amp"},{"id":"amulet-staked-sol","symbol":"amtsol","name":"Amulet Staked SOL"},{"id":"amz-coin","symbol":"amz","name":"AMZ Coin"},{"id":"anarchy","symbol":"anarchy","name":"Anarchy"},{"id":"a-nation","symbol":"anation","name":"A-Nation"},{"id":"anchor-neural-world-token","symbol":"anw","name":"Anchor Neural World"},{"id":"anchor-protocol","symbol":"anc","name":"Anchor Protocol"},{"id":"anchorswap","symbol":"anchor","name":"AnchorSwap"},{"id":"ancient-kingdom","symbol":"dom","name":"Ancient Kingdom"},{"id":"ancient-raid","symbol":"raid","name":"Ancient Raid"},{"id":"andronodes","symbol":"andro","name":"AndroNodes"},{"id":"anduschain","symbol":"deb","name":"Anduschain"},{"id":"angel-dust","symbol":"ad","name":"Angel Dust"},{"id":"angelscreed","symbol":"angel","name":"AngelsCreed"},{"id":"angle-protocol","symbol":"angle","name":"ANGLE"},{"id":"angola","symbol":"agla","name":"Angola"},{"id":"angryb","symbol":"anb","name":"Angryb"},{"id":"angry-bulls-club","symbol":"abc","name":"Angry Bulls Club"},{"id":"anifi-world","symbol":"anifi","name":"AniFi World"},{"id":"animal-concerts-token","symbol":"anml","name":"Animal Concerts"},{"id":"animal-farm","symbol":"afd","name":"Animal Farm Dogs"},{"id":"animeswap","symbol":"ani","name":"AnimeSwap"},{"id":"anime-token","symbol":"ani","name":"Anime"},{"id":"animverse","symbol":"anm","name":"Animverse"},{"id":"aniverse","symbol":"anv","name":"Aniverse"},{"id":"aniverse-metaverse","symbol":"aniv","name":"Aniverse Metaverse"},{"id":"anji","symbol":"anji","name":"Anji"},{"id":"ankaragucu-fan-token","symbol":"anka","name":"Ankaragücü Fan Token"},{"id":"ankr","symbol":"ankr","name":"Ankr Network"},{"id":"ankreth","symbol":"ankreth","name":"Ankr Staked ETH"},{"id":"ankr-reward-bearing-ftm","symbol":"ankrftm","name":"Ankr Staked FTM"},{"id":"ankr-reward-earning-matic","symbol":"ankrmatic","name":"Ankr Staked MATIC"},{"id":"ankr-staked-avax","symbol":"ankravax","name":"Ankr Staked AVAX"},{"id":"ankr-staked-bnb","symbol":"ankrbnb","name":"Ankr Staked BNB"},{"id":"anomus-coin","symbol":"anom","name":"Anomus Coin"},{"id":"anon","symbol":"anon","name":"ANON"},{"id":"anon-inu","symbol":"ainu","name":"Anon Inu"},{"id":"anonydoxx","symbol":"adxx","name":"AnonyDoxx"},{"id":"anonymous-bsc","symbol":"anon","name":"Anonymous BSC"},{"id":"anonzk","symbol":"azk","name":"AnonZK"},{"id":"anrkey-x","symbol":"$anrx","name":"AnRKey X"},{"id":"answer-governance","symbol":"agov","name":"Answer Governance"},{"id":"answerly","symbol":"ansr","name":"Answerly"},{"id":"antalyaspor","symbol":"akrep","name":"Antalyaspor"},{"id":"antedao","symbol":"ante","name":"AnteDAO"},{"id":"antfarm-governance-token","symbol":"agt","name":"Antfarm Governance Token"},{"id":"antfarm-token","symbol":"atf","name":"Antfarm Token"},{"id":"antgold","symbol":"antg","name":"AntGold"},{"id":"antiample","symbol":"xamp","name":"Antiample"},{"id":"antimatter","symbol":"matter","name":"AntiMatter"},{"id":"antnetworx","symbol":"antx","name":"AntNetworX"},{"id":"anubit","symbol":"anb","name":"Anubit"},{"id":"any-blocknet","symbol":"ablock","name":"ANY Blocknet"},{"id":"anypad","symbol":"apad","name":"Anypad"},{"id":"anyswap","symbol":"any","name":"Anyswap"},{"id":"aok","symbol":"aok","name":"AOK"},{"id":"aonea-coin","symbol":"a1a","name":"Aonea Coin"},{"id":"apass-coin","symbol":"apc","name":"APass Coin"},{"id":"apch","symbol":"apch","name":"APCH"},{"id":"apecoin","symbol":"ape","name":"ApeCoin"},{"id":"apedoge","symbol":"aped","name":"Apedoge"},{"id":"ape-finance","symbol":"apefi","name":"Ape Finance"},{"id":"apefund","symbol":"apefund","name":"ApeFund"},{"id":"ape-fun-token","symbol":"aft","name":"Ape Fun"},{"id":"ape-in","symbol":"apein","name":"Ape In"},{"id":"ape_in_records","symbol":"air","name":"Ape In Records"},{"id":"apejet","symbol":"jet","name":"ApeJet"},{"id":"apemove","symbol":"ape","name":"APEmove"},{"id":"apenft","symbol":"nft","name":"APENFT"},{"id":"apes-token","symbol":"apes","name":"Apes"},{"id":"apeswap-finance","symbol":"banana","name":"ApeSwap"},{"id":"ape-universe","symbol":"apeu","name":"Ape Universe"},{"id":"apexit-finance","symbol":"apex","name":"ApeXit Finance"},{"id":"apex-protocol","symbol":"apxp","name":"APEX Protocol"},{"id":"apex-token-2","symbol":"apex","name":"ApeX"},{"id":"api3","symbol":"api3","name":"API3"},{"id":"apidae","symbol":"apt","name":"Apidae"},{"id":"apix","symbol":"apix","name":"APIX"},{"id":"apm-coin","symbol":"apm","name":"apM Coin"},{"id":"apollo","symbol":"apl","name":"Apollo"},{"id":"apollo-crypto","symbol":"apollo","name":"Apollo Crypto"},{"id":"apollon-limassol","symbol":"apl","name":"Apollon Limassol Fan Token"},{"id":"apollox-2","symbol":"apx","name":"ApolloX"},{"id":"appcoins","symbol":"appc","name":"AppCoins"},{"id":"appics","symbol":"apx","name":"Appics"},{"id":"apple-binemon","symbol":"amb","name":"Apple (Binemon)"},{"id":"apple-tokenized-stock-defichain","symbol":"daapl","name":"Apple Tokenized Stock Defichain"},{"id":"apricot","symbol":"aprt","name":"Apricot"},{"id":"april","symbol":"april","name":"April"},{"id":"apron","symbol":"apn","name":"Apron"},{"id":"aptoge","symbol":"aptoge","name":"Aptoge"},{"id":"aptos","symbol":"apt","name":"Aptos"},{"id":"aptos-launch-token","symbol":"alt","name":"AptosLaunch Token"},{"id":"apwine","symbol":"apw","name":"APWine"},{"id":"apy-finance","symbol":"apy","name":"APY.Finance"},{"id":"apyswap","symbol":"apys","name":"APYSwap"},{"id":"apy-vision","symbol":"vision","name":"APY.vision"},{"id":"aquachain","symbol":"aqua","name":"Aquachain"},{"id":"aquadao","symbol":"$aqua","name":"AquaDAO"},{"id":"aqua-goat","symbol":"aquagoat","name":"Aqua Goat"},{"id":"aquanee","symbol":"aqdc","name":"Aquanee"},{"id":"aquari","symbol":"aquari","name":"Aquari"},{"id":"aquarius","symbol":"aqua","name":"Aquarius"},{"id":"aquariuscoin","symbol":"arco","name":"AquariusCoin"},{"id":"aquarius-fi","symbol":"aqu","name":"Aquarius.Fi"},{"id":"aquatank","symbol":"aqua","name":"AquaTank"},{"id":"aqua-unicorn","symbol":"auni","name":"Aqua Unicorn"},{"id":"arabella","symbol":"ablc","name":"ARABELLA"},{"id":"arabian-doge","symbol":"$adoge","name":"Arabian Doge"},{"id":"arabic","symbol":"abic","name":"Arabic"},{"id":"arable-protocol","symbol":"acre","name":"Arable Protocol"},{"id":"arable-usd","symbol":"arusd","name":"Arable USD"},{"id":"aragon","symbol":"ant","name":"Aragon"},{"id":"ara-token","symbol":"ara","name":"Ara"},{"id":"arbidoge","symbol":"adoge","name":"ArbiDoge"},{"id":"arbigoat","symbol":"agt","name":"ArbiGoat"},{"id":"arbinu","symbol":"arbinu","name":"Arbinu"},{"id":"arbinyan","symbol":"nyan","name":"ArbiNYAN"},{"id":"arbiroul-casino-chip","symbol":"roul","name":"ArbiRoul Casino Chip"},{"id":"arbis-finance","symbol":"arbis","name":"Arbis Finance"},{"id":"arbismart-token","symbol":"rbis","name":"ArbiSmart"},{"id":"arbisphere-launchpad","symbol":"arsh","name":"Arbisphere Launchpad"},{"id":"arbiswap-42983059-37e1-4a8f-b46e-0d908c0d4cc0","symbol":"arbi","name":"ArbiSwap"},{"id":"arbitrum","symbol":"arb","name":"Arbitrum"},{"id":"arbitrum-charts","symbol":"arcs","name":"Arbitrum Charts"},{"id":"arbitrum-exchange","symbol":"arx","name":"Arbidex"},{"id":"arbitrumpad","symbol":"arbpad","name":"ArbitrumPad"},{"id":"arbi-wiz","symbol":"awiz","name":"Arbi Wiz"},{"id":"arb-protocol","symbol":"arb","name":"ARB Protocol"},{"id":"arbucks","symbol":"buck","name":"Arbucks"},{"id":"arbys","symbol":"arbys","name":"Arbys"},{"id":"arbzilla","symbol":"zilla","name":"ArbZilla"},{"id":"arc","symbol":"arc","name":"Arc"},{"id":"arcade2earn","symbol":"arcade","name":"Arcade2Earn"},{"id":"arcade-canine","symbol":"okinu","name":"Arcade Canine"},{"id":"arcade-kingdoms","symbol":"ack","name":"Arcade Kingdoms"},{"id":"arcadeum","symbol":"arc","name":"Arcadeum"},{"id":"arcadium","symbol":"arcadium","name":"Arcadium"},{"id":"arcblock","symbol":"abt","name":"Arcblock"},{"id":"arcc","symbol":"arcc","name":"ARCC"},{"id":"archangel-token","symbol":"archa","name":"ArchAngel"},{"id":"arch-blockchains","symbol":"chain","name":"Arch Blockchains"},{"id":"archer-dao-governance-token","symbol":"arch","name":"Archer DAO Governance"},{"id":"archerswap-bow","symbol":"bow","name":"Archerswap BOW"},{"id":"archerswap-hunter","symbol":"hunt","name":"ArcherSwap Hunter"},{"id":"arch-ethereum-web3","symbol":"web3","name":"Arch Ethereum Web3"},{"id":"archethic","symbol":"uco","name":"Archethic"},{"id":"archie-neko","symbol":"archie","name":"Archie Neko"},{"id":"archimedes","symbol":"arch","name":"Archimedes Finance"},{"id":"archive-ai","symbol":"arcai","name":"Archive AI"},{"id":"archloot","symbol":"alt","name":"ArchLoot"},{"id":"arcona","symbol":"arcona","name":"Arcona"},{"id":"arcs","symbol":"arx","name":"ARCS"},{"id":"ardana","symbol":"dana","name":"Ardana"},{"id":"ardcoin","symbol":"ardx","name":"ArdCoin"},{"id":"ardor","symbol":"ardr","name":"Ardor"},{"id":"arenaplay","symbol":"apc","name":"ArenaPlay"},{"id":"arena-token","symbol":"arena","name":"ArenaSwap"},{"id":"arenum","symbol":"arnm","name":"Arenum"},{"id":"areon-network","symbol":"area","name":"Areon Network"},{"id":"ares3-network","symbol":"ares","name":"Ares3 Network"},{"id":"ares-protocol","symbol":"ares","name":"Ares Protocol"},{"id":"argentine-football-association-fan-token","symbol":"arg","name":"Argentine Football Association Fan Token"},{"id":"argo","symbol":"argo","name":"ArGoApp"},{"id":"argo-finance","symbol":"argo","name":"Argo Finance"},{"id":"argon","symbol":"argon","name":"Argon"},{"id":"argonon-helium","symbol":"arg","name":"Argonon Helium"},{"id":"ari10","symbol":"ari10","name":"Ari10"},{"id":"aria-currency","symbol":"ria","name":"aRIA Currency"},{"id":"ariadne","symbol":"ardn","name":"Ariadne"},{"id":"arianee","symbol":"aria20","name":"Arianee"},{"id":"aries-financial-token","symbol":"afib","name":"Aries Financial"},{"id":"arion","symbol":"arion","name":"Arion"},{"id":"ari-swap","symbol":"ari","name":"Ari Swap"},{"id":"ariva","symbol":"arv","name":"Ariva"},{"id":"arix","symbol":"arix","name":"Arix"},{"id":"arize","symbol":"arz","name":"ARize"},{"id":"ark","symbol":"ark","name":"ARK"},{"id":"arkadiko-protocol","symbol":"diko","name":"Arkadiko"},{"id":"arkadiko-usda","symbol":"usda","name":"Arkadiko USDA"},{"id":"arkania-protocol","symbol":"ania","name":"Arkania Protocol"},{"id":"arker-2","symbol":"arker","name":"Arker"},{"id":"ark-innovation-etf-defichain","symbol":"darkk","name":"ARK Innovation ETF Defichain"},{"id":"ark-rivals","symbol":"arkn","name":"Ark Rivals"},{"id":"armor","symbol":"armor","name":"ARMOR"},{"id":"armor-nxm","symbol":"arnxm","name":"Armor NXM"},{"id":"army-node-finance","symbol":"army","name":"Army Node Finance"},{"id":"arnoya-classic","symbol":"arnc","name":"Arnoya classic"},{"id":"arora","symbol":"aror","name":"Arora"},{"id":"arowana-token","symbol":"arw","name":"Arowana"},{"id":"arpa","symbol":"arpa","name":"ARPA"},{"id":"arqma","symbol":"arq","name":"ArQmA"},{"id":"arrow","symbol":"arw","name":"Arrow"},{"id":"arsenal-fan-token","symbol":"afc","name":"Arsenal Fan Token"},{"id":"artbyte","symbol":"aby","name":"ArtByte"},{"id":"arte","symbol":"arte","name":"ARTE"},{"id":"artem","symbol":"artem","name":"Artem"},{"id":"artemis","symbol":"mis","name":"Artemis"},{"id":"artemis-vision","symbol":"arv","name":"Artemis Vision"},{"id":"arteq-nft-investment-fund","symbol":"arteq","name":"artèQ NFT Investment Fund"},{"id":"artery","symbol":"artr","name":"Artery"},{"id":"art-gobblers-goo","symbol":"goo","name":"Art Gobblers Goo"},{"id":"artgpt","symbol":"agpt","name":"artGPT"},{"id":"arth","symbol":"arth","name":"ARTH"},{"id":"arthswap","symbol":"arsw","name":"ArthSwap"},{"id":"artic-foundation","symbol":"artic","name":"ARTIC Foundation"},{"id":"articoin","symbol":"atc","name":"ArtiCoin"},{"id":"artificial-intelligence","symbol":"ai","name":"Artificial Intelligence"},{"id":"artificial-intelligence-technology-network","symbol":"aitn","name":"Artificial Intelligence Technology Network"},{"id":"artify","symbol":"afy","name":"Artify"},{"id":"artik","symbol":"artk","name":"Artik"},{"id":"arti-project","symbol":"arti","name":"Arti Project"},{"id":"artizen","symbol":"atnt","name":"Artizen"},{"id":"artkit","symbol":"arti","name":"ArtKit"},{"id":"artl","symbol":"artl","name":"ARTL"},{"id":"artm","symbol":"artm","name":"ARTM"},{"id":"artmeta","symbol":"$mart","name":"ArtMeta"},{"id":"artrade","symbol":"atr","name":"Artrade"},{"id":"artube","symbol":"att","name":"Artube"},{"id":"artx","symbol":"artx","name":"ARTX"},{"id":"arweave","symbol":"ar","name":"Arweave"},{"id":"aryacoin","symbol":"aya","name":"Aryacoin"},{"id":"asan-verse","symbol":"asan","name":"ASAN VERSE"},{"id":"ascension","symbol":"asn","name":"Ascension"},{"id":"ascension-protocol","symbol":"ascend","name":"Ascension Protocol"},{"id":"asd","symbol":"asd","name":"AscendEx"},{"id":"asgard-games","symbol":"asg","name":"Asgard Games"},{"id":"asgardian-aereus","symbol":"volt","name":"Asgardian Aereus"},{"id":"asgardx","symbol":"odin","name":"AsgardX"},{"id":"ash","symbol":"ash","name":"ASH"},{"id":"ashera","symbol":"ash","name":"Ashera"},{"id":"ashswap","symbol":"ash","name":"AshSwap"},{"id":"ash-token","symbol":"ash","name":"Ash Token"},{"id":"asia-coin","symbol":"asia","name":"Asia Coin"},{"id":"asic-token","symbol":"asic","name":"ASIC Token"},{"id":"asimi","symbol":"asimi","name":"ASIMI"},{"id":"asix","symbol":"asix","name":"ASIX"},{"id":"asixplus","symbol":"asix+","name":"AsixPlus"},{"id":"ask-chip","symbol":"chip","name":"Ask Chip"},{"id":"askobar-network","symbol":"asko","name":"Asko"},{"id":"as-monaco-fan-token","symbol":"asm","name":"AS Monaco Fan Token"},{"id":"aspire","symbol":"asp","name":"Aspire"},{"id":"aspo-world","symbol":"aspo","name":"ASPO World"},{"id":"as-roma-fan-token","symbol":"asr","name":"AS Roma Fan Token"},{"id":"assangedao","symbol":"justice","name":"AssangeDAO"},{"id":"assaplay","symbol":"assa","name":"AssaPlay"},{"id":"assemble-protocol","symbol":"asm","name":"Assemble Protocol"},{"id":"assent-protocol","symbol":"asnt","name":"Assent Protocol"},{"id":"assetmantle","symbol":"mntl","name":"AssetMantle"},{"id":"asta","symbol":"asta","name":"ASTA"},{"id":"astar","symbol":"astr","name":"Astar"},{"id":"aster","symbol":"atc","name":"Aster"},{"id":"ast-finance","symbol":"ast","name":"AST.finance"},{"id":"aston-martin-cognizant-fan-token","symbol":"am","name":"Aston Martin Cognizant Fan Token"},{"id":"aston-villa-fan-token","symbol":"avl","name":"Aston Villa Fan Token"},{"id":"astra-dao","symbol":"astra","name":"Astra DAO"},{"id":"astrafer","symbol":"astrafer","name":"Astrafer"},{"id":"astra-guild-ventures","symbol":"agv","name":"Astra Guild Ventures"},{"id":"astral-ai","symbol":"astral","name":"Astral AI"},{"id":"astral-credits","symbol":"xac","name":"Astral Credits"},{"id":"astrals-glxy","symbol":"glxy","name":"Astrals GLXY"},{"id":"astra-nova","symbol":"$rvv","name":"Astra Nova"},{"id":"astra-protocol-2","symbol":"astra","name":"Astra Protocol"},{"id":"astrazion","symbol":"aznt","name":"AstraZion"},{"id":"astriddao-token","symbol":"atid","name":"AstridDAO"},{"id":"astroai","symbol":"astroai","name":"AstroAI"},{"id":"astro-babies","symbol":"abb","name":"Astro Babies"},{"id":"astrobirdz","symbol":"abz","name":"AstroBirdz"},{"id":"astro-cash","symbol":"astro","name":"Astro Cash"},{"id":"astroelon","symbol":"elonone","name":"AstroElon"},{"id":"astronaut","symbol":"naut","name":"Astronaut"},{"id":"astroport","symbol":"astroc","name":"Astroport Classic"},{"id":"astroport-fi","symbol":"astro","name":"Astroport"},{"id":"astrospaces-io","symbol":"spaces","name":"AstroSpaces.io"},{"id":"astroswap","symbol":"astro","name":"AstroSwap"},{"id":"astrotools","symbol":"astro","name":"AstroTools"},{"id":"astro-verse","symbol":"asv","name":"Astro Verse"},{"id":"astrox","symbol":"atx","name":"AstroX"},{"id":"asva","symbol":"asva","name":"Asva Labs"},{"id":"asyagro","symbol":"asy","name":"ASYAGRO"},{"id":"atari","symbol":"atri","name":"Atari"},{"id":"athena-returns-olea","symbol":"olea","name":"Athena Returns Olea"},{"id":"athenas","symbol":"athenasv2","name":"Athenas"},{"id":"atheneum","symbol":"aem","name":"Atheneum"},{"id":"athens","symbol":"ath","name":"Athens"},{"id":"athos-finance","symbol":"ath","name":"Athos Finance"},{"id":"athos-finance-usd","symbol":"athusd","name":"Athos Finance USD"},{"id":"atlantis","symbol":"atlas","name":"Atlantis"},{"id":"atlantis-coin","symbol":"atc","name":"Atlantis Coin"},{"id":"atlantis-loans","symbol":"atl","name":"Atlantis Loans"},{"id":"atlantis-loans-polygon","symbol":"atlx","name":"Atlantis Loans Polygon"},{"id":"atlantis-metaverse","symbol":"tau","name":"Atlantis Metaverse"},{"id":"atlantis-universe-money","symbol":"aum","name":"Atlantis Universe Money"},{"id":"atlas-aggregator","symbol":"ata","name":"Atlas Aggregator"},{"id":"atlas-dex","symbol":"ats","name":"Atlas DEX"},{"id":"atlas-fc-fan-token","symbol":"atlas","name":"Atlas FC Fan Token"},{"id":"atlas-navi","symbol":"navi","name":"Atlas Navi"},{"id":"atlas-protocol","symbol":"atp","name":"Atlas Protocol"},{"id":"atlas-usv","symbol":"usv","name":"Atlas USV"},{"id":"atletico-madrid","symbol":"atm","name":"Atletico Madrid Fan Token"},{"id":"atomic-wallet-coin","symbol":"awc","name":"Atomic Wallet Coin"},{"id":"atompad","symbol":"atpad","name":"AtomPad"},{"id":"atpay","symbol":"atpay","name":"AtPay"},{"id":"atrollcity","symbol":"pine","name":"Atrollcity"},{"id":"atromg8","symbol":"ag8","name":"ATROMG8"},{"id":"attack-wagon","symbol":"atk","name":"Attack Wagon"},{"id":"attila","symbol":"att","name":"Attila"},{"id":"attrace","symbol":"attr","name":"Attrace"},{"id":"auction","symbol":"auction","name":"Bounce"},{"id":"auctus","symbol":"auc","name":"Auctus"},{"id":"auditchain","symbol":"audt","name":"Auditchain"},{"id":"audius","symbol":"audio","name":"Audius"},{"id":"audius-wormhole","symbol":"audio","name":"Audius (Wormhole)"},{"id":"augmented-finance","symbol":"agf","name":"Augmented Finance"},{"id":"augur","symbol":"rep","name":"Augur"},{"id":"augury-finance","symbol":"omen","name":"Augury Finance"},{"id":"aura-bal","symbol":"aurabal","name":"Aura BAL"},{"id":"auradx","symbol":"dalle2","name":"AuradX"},{"id":"aura-finance","symbol":"aura","name":"Aura Finance"},{"id":"aura-network","symbol":"aura","name":"Aura Network"},{"id":"aureo","symbol":"aur","name":"AUREO"},{"id":"aureus-nummus-gold","symbol":"ang","name":"Aureus Nummus Gold"},{"id":"auric-network","symbol":"auscm","name":"Auric Network"},{"id":"aurigami","symbol":"ply","name":"Aurigami"},{"id":"aurix","symbol":"aur","name":"Aurix"},{"id":"aurora","symbol":"aoa","name":"Aurora Chain"},{"id":"auroracoin","symbol":"aur","name":"Auroracoin"},{"id":"aurora-dao","symbol":"idex","name":"IDEX"},{"id":"aurora-near","symbol":"aurora","name":"Aurora"},{"id":"auroratoken","symbol":"aurora","name":"AuroraToken"},{"id":"aurora-token","symbol":"$adtx","name":"Aurora Dimension"},{"id":"aurory","symbol":"aury","name":"Aurory"},{"id":"aurusx","symbol":"ax","name":"AurusX"},{"id":"ausdc","symbol":"ausdc","name":"SpaceShipX aUSDC"},{"id":"australian-safe-shepherd","symbol":"ass","name":"Australian Safe Shepherd"},{"id":"authencity","symbol":"auth","name":"Authencity"},{"id":"auto","symbol":"auto","name":"Auto"},{"id":"autobahn-network","symbol":"txl","name":"Autobahn Network"},{"id":"autocrypto","symbol":"au","name":"AutoCrypto"},{"id":"automata","symbol":"ata","name":"Automata"},{"id":"autominingtoken","symbol":"amt","name":"AutoMiningToken"},{"id":"auton","symbol":"atn","name":"Auton"},{"id":"autonio","symbol":"niox","name":"Autonio"},{"id":"autoshark","symbol":"jaws","name":"AutoShark"},{"id":"autosingle","symbol":"autos","name":"AutoSingle"},{"id":"auto-staked-alex","symbol":"atalex","name":"Auto Staked ALEX"},{"id":"autumn","symbol":"autumn","name":"Autumn"},{"id":"aux-coin","symbol":"aux","name":"AUX Coin"},{"id":"auxilium","symbol":"aux","name":"Auxilium"},{"id":"avadex-token","symbol":"avex","name":"AvaDex Token"},{"id":"avalanche-2","symbol":"avax","name":"Avalanche"},{"id":"avalanche-wormhole","symbol":"avax","name":"Avalanche (Wormhole)"},{"id":"avalaunch","symbol":"xava","name":"Avalaunch"},{"id":"avaocado-dao","symbol":"avg","name":"Avocado DAO"},{"id":"avata-network","symbol":"avat","name":"AVATA Network"},{"id":"avatara-nox","symbol":"nox","name":"AVATARA NOX"},{"id":"avaterra","symbol":"terra","name":"Avaterra"},{"id":"avatly","symbol":"ava","name":"Avatly"},{"id":"avaware","symbol":"ave","name":"Avaware"},{"id":"avaxlauncher","symbol":"avxl","name":"Avaxlauncher"},{"id":"avaxtars","symbol":"avxt","name":"Avaxtars"},{"id":"avefarm","symbol":"ave","name":"AveFarm"},{"id":"aventus","symbol":"avt","name":"Aventus"},{"id":"avenue-hamilton-token","symbol":"aht","name":"Avenue Hamilton Token"},{"id":"avenue-university-token","symbol":"aut","name":"Avenue University Token"},{"id":"avian-network","symbol":"avn","name":"AVIAN"},{"id":"avinoc","symbol":"avinoc","name":"AVINOC"},{"id":"avme","symbol":"avme","name":"AVME"},{"id":"avnrich","symbol":"avn","name":"AVNRich"},{"id":"avocado","symbol":"avo","name":"Avocado"},{"id":"avocadocoin","symbol":"avdo","name":"AvocadoCoin"},{"id":"avoteo","symbol":"avo","name":"Avoteo"},{"id":"axe","symbol":"axe","name":"Axe"},{"id":"axel","symbol":"axel","name":"AXEL"},{"id":"axelar","symbol":"axl","name":"Axelar"},{"id":"axia","symbol":"axiav3","name":"Axia"},{"id":"axial-token","symbol":"axial","name":"Axial Token"},{"id":"axie-infinity","symbol":"axs","name":"Axie Infinity"},{"id":"axie-infinity-shard-wormhole","symbol":"axset","name":"Axie Infinity Shard (Wormhole)"},{"id":"axioms","symbol":"axi","name":"Axioms"},{"id":"axion","symbol":"axn","name":"Axion"},{"id":"axis-defi","symbol":"axis","name":"Axis DeFi"},{"id":"axis-token","symbol":"axis","name":"AXIS"},{"id":"axle-games","symbol":"axle","name":"Axle Games"},{"id":"axl-inu","symbol":"axl","name":"AXL INU"},{"id":"axlusdc","symbol":"axlusdc","name":"Axelar USDC"},{"id":"axlwbtc","symbol":"axlwbtc","name":"axlWBTC"},{"id":"axlweth","symbol":"axlweth","name":"axlWETH"},{"id":"axpire","symbol":"axpr","name":"Moola"},{"id":"azacoin","symbol":"aza","name":"AzaCoin"},{"id":"azbit","symbol":"az","name":"Azbit"},{"id":"azit","symbol":"azit","name":"azit"},{"id":"aztec-nodes-sun","symbol":"sun","name":"Aztec Nodes SUN"},{"id":"azuki","symbol":"azuki","name":"Azuki"},{"id":"azuma-coin","symbol":"azum","name":"Azuma Coin"},{"id":"az-world-socialfi","symbol":"azw","name":"AZ World SocialFi"},{"id":"b20","symbol":"b20","name":"B20"},{"id":"b8dex","symbol":"b8t","name":"B8DEX"},{"id":"baanx","symbol":"bxx","name":"Baanx"},{"id":"baasid","symbol":"baas","name":"BaaSid"},{"id":"babacoin","symbol":"bbc","name":"Babacoin"},{"id":"babb","symbol":"bax","name":"BABB"},{"id":"babil-token","symbol":"babil","name":"BABIL TOKEN"},{"id":"baby-alvey","symbol":"balvey","name":"Baby Alvey"},{"id":"babyapefunclub","symbol":"bafc","name":"BabyApeFunClub"},{"id":"baby-arbitrum","symbol":"barb","name":"Baby Arbitrum"},{"id":"baby-bali","symbol":"bb","name":"Baby Bali"},{"id":"baby-bitcoin","symbol":"bbtc","name":"Baby Bitcoin"},{"id":"babybnb","symbol":"babybnb","name":"BabyBNB"},{"id":"babybnbtiger","symbol":"babybnbtig","name":"BabyBNBTiger"},{"id":"babyboo","symbol":"babyboo","name":"BabyBoo"},{"id":"baby-boxer","symbol":"bboxer","name":"Baby Boxer"},{"id":"babybusd","symbol":"babybusd","name":"BabyBUSD"},{"id":"baby-cake","symbol":"babycake","name":"Baby Cake"},{"id":"baby-catcoin","symbol":"babycats","name":"Baby Catcoin"},{"id":"baby-cheems-inu","symbol":"bci","name":"Baby Cheems Inu"},{"id":"babydogecake","symbol":"bdc","name":"BabyDogeCake"},{"id":"baby-doge-cash","symbol":"babydogecash","name":"Baby Doge Cash"},{"id":"baby-doge-ceo","symbol":"babyceo","name":"Baby Doge CEO"},{"id":"babydoge-ceo","symbol":"bceo","name":"BabyDoge CEO"},{"id":"baby-doge-coin","symbol":"babydoge","name":"Baby Doge Coin"},{"id":"babydoge-coin-eth","symbol":"babydoge","name":"BabyDoge ETH"},{"id":"baby-doge-inu","symbol":"$babydogeinu","name":"Baby Doge Inu"},{"id":"babydogezilla","symbol":"babydogezilla","name":"BabyDogeZilla"},{"id":"babydot","symbol":"bdot","name":"BabyDot"},{"id":"baby-doug","symbol":"babydoug","name":"Baby Doug"},{"id":"babyfloki","symbol":"babyfloki","name":"BabyFloki"},{"id":"baby-floki","symbol":"babyfloki","name":"Baby Floki"},{"id":"baby-floki-coin","symbol":"babyflokicoin","name":"Baby Floki Coin"},{"id":"baby-floki-doge","symbol":"babyfd","name":"Baby Floki Doge"},{"id":"baby-floki-inu","symbol":"bfloki","name":"Baby Floki Inu"},{"id":"babyfootball","symbol":"cup","name":"BabyFootball"},{"id":"baby-g","symbol":"babyg","name":"Baby G"},{"id":"baby-kishu","symbol":"babykishu","name":"Baby Kishu"},{"id":"babykitty","symbol":"babykitty","name":"BabyKitty"},{"id":"baby-lambo-inu","symbol":"blinu","name":"Baby Lambo Inu"},{"id":"babylon-finance","symbol":"babl","name":"Babylon Finance"},{"id":"babylons","symbol":"babi","name":"Babylons"},{"id":"baby-lovely-inu","symbol":"blovely","name":"Baby Lovely Inu"},{"id":"baby-moon-floki","symbol":"floki","name":"Baby Moon Floki"},{"id":"babyokx","symbol":"babyokx","name":"BABYOKX (BSC)"},{"id":"babyokx-2","symbol":"babyokx","name":"BABYOKX"},{"id":"baby-pig-token","symbol":"babypig","name":"Baby Pig"},{"id":"baby-pokemoon","symbol":"bpm","name":"Baby Pokemoon"},{"id":"babyrabbit","symbol":"babyrabbit","name":"Babyrabbit"},{"id":"baby-ripple","symbol":"babyxrp","name":"Baby Ripple"},{"id":"baby-saitama","symbol":"babysaitama","name":"Baby Saitama"},{"id":"baby-samo-coin","symbol":"baby","name":"Baby Samo Coin"},{"id":"baby-satoshi","symbol":"sats","name":"Baby Satoshi"},{"id":"baby-shark","symbol":"shark","name":"Baby Shark"},{"id":"baby-shark-tank","symbol":"bashtank","name":"Baby Shark Tank"},{"id":"baby-shiba-coin","symbol":"babyshiba","name":"Baby Shiba Coin"},{"id":"baby-shiba-inu","symbol":"babyshibainu","name":"Baby Shiba Inu"},{"id":"babyshibby-inu","symbol":"babyshib","name":"BabyShibby Inu"},{"id":"babyswap","symbol":"baby","name":"BabySwap"},{"id":"baby-trump","symbol":"babytrump","name":"Baby Trump"},{"id":"babywhale","symbol":"bbw","name":"BabyWhale"},{"id":"baby-woj","symbol":"bwj","name":"Baby WOJ"},{"id":"babyxrp","symbol":"bbyxrp","name":"BabyXrp"},{"id":"backed-protocol","symbol":"bakt","name":"Backed Protocol"},{"id":"bacon-coin","symbol":"bacon","name":"Bacon Coin"},{"id":"bacondao","symbol":"bacon","name":"BaconDAO"},{"id":"bacon-protocol-home","symbol":"home","name":"Home"},{"id":"badger-dao","symbol":"badger","name":"Badger DAO"},{"id":"badger-sett-badger","symbol":"bbadger","name":"Badger Sett Badger"},{"id":"bafi-finance-token","symbol":"bafi","name":"Bafi Finance"},{"id":"bagus-wallet","symbol":"bg","name":"Bagus Wallet"},{"id":"bahtcoin","symbol":"bht","name":"Bahtcoin"},{"id":"bai-stablecoin","symbol":"bai","name":"BAI Stablecoin"},{"id":"baitcoin","symbol":"bait","name":"Baitcoin"},{"id":"baj","symbol":"bjt","name":"BAJ"},{"id":"baked-token","symbol":"baked","name":"Baked"},{"id":"bakerytoken","symbol":"bake","name":"BakerySwap"},{"id":"bakerytools","symbol":"tbake","name":"BakeryTools"},{"id":"baklava","symbol":"bava","name":"Baklava"},{"id":"balance-network","symbol":"bln","name":"Balance Network"},{"id":"balancer","symbol":"bal","name":"Balancer"},{"id":"balancer-80-bal-20-weth","symbol":"b-80bal-20weth","name":"Balancer 80 BAL 20 WETH"},{"id":"balancer-boosted-aave-dai","symbol":"bb-a-dai","name":"Balancer Boosted Aave DAI"},{"id":"balancer-boosted-aave-usdc","symbol":"bb-a-usdc","name":"Balancer Boosted Aave USDC"},{"id":"balancer-boosted-aave-usdt","symbol":"bb-a-usdt","name":"Balancer Boosted Aave USDT"},{"id":"balicoin","symbol":"bali","name":"Bali Coin"},{"id":"balikesirspor-token","symbol":"blks","name":"Balıkesirspor Token"},{"id":"bali-social-integrated","symbol":"bsi","name":"Bali Social Integrated"},{"id":"bali-token","symbol":"bli","name":"Bali Token"},{"id":"bali-united-fc-fan-token","symbol":"bufc","name":"Bali United FC Fan Token"},{"id":"balkari-token","symbol":"bkr","name":"Balkari"},{"id":"ball-coin","symbol":"ball","name":"BALL Coin"},{"id":"balloonsville-air","symbol":"air","name":"Balloonsville AIR"},{"id":"ballswap","symbol":"bsp","name":"BallSwap"},{"id":"ball-token","symbol":"ball","name":"Ball"},{"id":"balpha","symbol":"balpha","name":"bAlpha"},{"id":"balto-token","symbol":"balto","name":"Balto Token"},{"id":"bamboo-coin","symbol":"bmbo","name":"Bamboo Coin"},{"id":"bamboo-defi","symbol":"bamboo","name":"BambooDeFi"},{"id":"bamboo-token-c90b31ff-8355-41d6-a495-2b16418524c2","symbol":"bbo","name":"PandaFarm (BBO)"},{"id":"banana","symbol":"banana","name":"Banana"},{"id":"banana-bucks","symbol":"bab","name":"Banana Bucks"},{"id":"bananaclubtoken","symbol":"bct","name":"BananaClubToken"},{"id":"banana-task-force-ape","symbol":"btfa","name":"Banana Task Force Ape"},{"id":"bananatok","symbol":"bna","name":"BananaTok"},{"id":"banana-token","symbol":"bnana","name":"Chimpion"},{"id":"banano","symbol":"ban","name":"Banano"},{"id":"banca","symbol":"banca","name":"Banca"},{"id":"bancambios-ax","symbol":"bxs","name":"Bancambios AX"},{"id":"bancor","symbol":"bnt","name":"Bancor Network"},{"id":"bancor-governance-token","symbol":"vbnt","name":"Bancor Governance"},{"id":"band-protocol","symbol":"band","name":"Band Protocol"},{"id":"bankera","symbol":"bnk","name":"Bankera"},{"id":"bankers-dream","symbol":"bank$","name":"Bankers Dream"},{"id":"bankless-bed-index","symbol":"bed","name":"Bankless BED Index"},{"id":"bankless-dao","symbol":"bank","name":"Bankless DAO"},{"id":"bankless-defi-innovation-index","symbol":"gmi","name":"Bankless DeFi Innovation Index"},{"id":"bankroll-extended-token","symbol":"bnkrx","name":"Bankroll Extended"},{"id":"bankroll-network","symbol":"bnkr","name":"Bankroll Network"},{"id":"bankroll-vault","symbol":"vlt","name":"Bankroll Vault"},{"id":"banksocial","symbol":"bsl","name":"BankSocial"},{"id":"bantu","symbol":"xbn","name":"Bantu"},{"id":"bao","symbol":"bao","name":"BAO"},{"id":"bao-finance","symbol":"bao","name":"Bao Finance"},{"id":"bao-finance-v2","symbol":"bao","name":"Bao Finance V2"},{"id":"baousd","symbol":"baousd","name":"baoUSD"},{"id":"baptlabs","symbol":"bapt","name":"BaptLabs"},{"id":"barbecueswap","symbol":"bbq","name":"BarbecueSwap"},{"id":"bard-protocol","symbol":"bard","name":"Bard Protocol"},{"id":"bark","symbol":"bark","name":"Bark"},{"id":"barking","symbol":"bark","name":"Barking"},{"id":"barnbridge","symbol":"bond","name":"BarnBridge"},{"id":"barter","symbol":"brtr","name":"Barter"},{"id":"bartertrade","symbol":"bart","name":"BarterTrade"},{"id":"basan","symbol":"basan","name":"Basan"},{"id":"based-ai","symbol":"bai","name":"Based AI"},{"id":"based-finance","symbol":"based","name":"Based Finance"},{"id":"based-shares","symbol":"bshare","name":"BASED Shares"},{"id":"base-protocol","symbol":"base","name":"Base Protocol"},{"id":"basic","symbol":"basic","name":"BASIC"},{"id":"basic-attention-token","symbol":"bat","name":"Basic Attention"},{"id":"basilisk","symbol":"bsx","name":"Basilisk"},{"id":"basis-cash","symbol":"bac","name":"Basis Cash"},{"id":"basis-gold-share-heco","symbol":"bags","name":"Basis Gold Share (Heco)"},{"id":"basis-markets","symbol":"basis","name":"basis.markets"},{"id":"basis-share","symbol":"bas","name":"Basis Share"},{"id":"basketball-legends","symbol":"bbl","name":"Basketball Legends"},{"id":"basketcoin","symbol":"bskt","name":"BasketCoin"},{"id":"baskonia-fan-token","symbol":"bkn","name":"Baskonia Fan Token"},{"id":"bastion-protocol","symbol":"bstn","name":"Bastion Protocol"},{"id":"bata","symbol":"bta","name":"Bata"},{"id":"batorrent","symbol":"ba","name":"BaTorrent"},{"id":"battle-esports-coin","symbol":"bes","name":"battle esports coin"},{"id":"battlefly","symbol":"gfly","name":"BattleFly"},{"id":"battle-for-giostone","symbol":"bfg","name":"Battle For Giostone"},{"id":"battleforten","symbol":"bft","name":"BattleForTEN"},{"id":"battle-hero","symbol":"bath","name":"Battle Hero"},{"id":"battle-infinity","symbol":"ibat","name":"Battle Infinity"},{"id":"battle-of-guardians-share","symbol":"bgs","name":"Battle of Guardians Share"},{"id":"battle-pets","symbol":"pet","name":"Hello Pets"},{"id":"battle-saga","symbol":"btl","name":"Battle Saga"},{"id":"battleverse","symbol":"bvc","name":"BattleVerse"},{"id":"battle-world","symbol":"bwo","name":"Battle World"},{"id":"bayc-vault-nftx","symbol":"bayc","name":"BAYC Vault (NFTX)"},{"id":"baymax-finance","symbol":"bay","name":"BayMax Finance"},{"id":"bazaars","symbol":"bzr","name":"Bazaars"},{"id":"bb-gaming","symbol":"bb","name":"BB Gaming"},{"id":"bbs-network","symbol":"bbs","name":"BBS Network"},{"id":"bcpay-fintech","symbol":"bcpay","name":"BCPAY FinTech"},{"id":"b-cube-ai","symbol":"bcube","name":"B-cube.ai"},{"id":"bdollar","symbol":"bdo","name":"bDollar"},{"id":"beach-token","symbol":"beach","name":"Beach Token"},{"id":"beacon","symbol":"becn","name":"Beacon"},{"id":"beagle-inu","symbol":"bic","name":"Beagle Inu"},{"id":"beam","symbol":"beam","name":"BEAM"},{"id":"beamswap","symbol":"glint","name":"BeamSwap"},{"id":"bean","symbol":"bean","name":"Bean"},{"id":"bean-cash","symbol":"bitb","name":"Bean Cash"},{"id":"bearex","symbol":"brex","name":"BeaRex"},{"id":"bear-inu","symbol":"bear","name":"Bear Inu"},{"id":"beast-masters","symbol":"master","name":"Beast Masters"},{"id":"beatzcoin","symbol":"btzc","name":"BeatzCoin"},{"id":"beauty-bakery-linked-operation-transaction-technology","symbol":"lott","name":"Beauty Bakery Linked Operation Transaction Technology"},{"id":"becoswap-token","symbol":"beco","name":"BecoSwap"},{"id":"bedrock","symbol":"rock","name":"Bedrock"},{"id":"bee-capital","symbol":"bee","name":"Bee Capital"},{"id":"beechat","symbol":"chat","name":"BeeChat"},{"id":"beeco","symbol":"bgc","name":"Bee Token"},{"id":"beefy-escrowed-fantom","symbol":"beftm","name":"Beefy Escrowed Fantom"},{"id":"beefy-finance","symbol":"bifi","name":"Beefy.Finance"},{"id":"beenode","symbol":"bnode","name":"Beenode"},{"id":"beer-money","symbol":"beer","name":"Beer Money"},{"id":"beeruscat","symbol":"bcat","name":"BeerusCat"},{"id":"beethoven-x","symbol":"beets","name":"Beethoven X"},{"id":"beetlecoin","symbol":"beet","name":"Beetlecoin"},{"id":"befasterholdertoken","symbol":"bfht","name":"BeFaster Holder Token"},{"id":"befitter","symbol":"fiu","name":"beFITTER"},{"id":"befitter-health","symbol":"hee","name":"beFITTER Health"},{"id":"be-gaming-coin","symbol":"bgc","name":"Be Gaming Coin"},{"id":"beholder","symbol":"eye","name":"Behodler"},{"id":"bela","symbol":"aqua","name":"Bela Aqua"},{"id":"beldex","symbol":"bdx","name":"Beldex"},{"id":"belecx-protocol","symbol":"bex","name":"BelecX Protocol"},{"id":"belifex","symbol":"befx","name":"Belifex"},{"id":"bella-protocol","symbol":"bel","name":"Bella Protocol"},{"id":"bellcoin","symbol":"bell","name":"Bellcoin"},{"id":"belrium","symbol":"bel","name":"Belrium"},{"id":"belt","symbol":"belt","name":"Belt"},{"id":"beluga-fi","symbol":"beluga","name":"Beluga.fi"},{"id":"bemchain","symbol":"bcn","name":"Bemchain"},{"id":"be-meta-famous","symbol":"bmf","name":"Be Meta Famous"},{"id":"bemil-coin","symbol":"bem","name":"Bemil Coin"},{"id":"benchmark-protocol","symbol":"mark","name":"Benchmark Protocol"},{"id":"benddao","symbol":"bend","name":"BendDAO"},{"id":"bened","symbol":"bnd","name":"Bened"},{"id":"benqi","symbol":"qi","name":"BENQI"},{"id":"benqi-liquid-staked-avax","symbol":"savax","name":"BENQI Liquid Staked AVAX"},{"id":"bent-finance","symbol":"bent","name":"Bent Finance"},{"id":"benzene","symbol":"bzn","name":"Benzene"},{"id":"bep20-leo","symbol":"bleo","name":"BEP20 LEO"},{"id":"bepay","symbol":"becoin","name":"bePAY Finance"},{"id":"bepro-network","symbol":"bepro","name":"BEPRO Network"},{"id":"bergerdoge","symbol":"bergerdoge","name":"BergerDoge"},{"id":"bermuda","symbol":"bmda","name":"Bermuda"},{"id":"berry","symbol":"berry","name":"Berry"},{"id":"berry-data","symbol":"bry","name":"Berry Data"},{"id":"berryswap","symbol":"berry","name":"BerrySwap"},{"id":"berylbit","symbol":"brb","name":"Berylbit Layer-3 Network"},{"id":"beshare-token","symbol":"bst","name":"Beshare"},{"id":"beskar","symbol":"bsk-baa025","name":"Beskar"},{"id":"bestay","symbol":"bsy","name":"Bestay"},{"id":"bet2bank","symbol":"bxb","name":"Bet2Bank"},{"id":"beta-finance","symbol":"beta","name":"Beta Finance"},{"id":"beta-token","symbol":"beta","name":"Beta"},{"id":"betaverse","symbol":"bet","name":"Betaverse"},{"id":"betcoin","symbol":"bet","name":"BETCOIN"},{"id":"betero","symbol":"bte","name":"Betero"},{"id":"betgosu","symbol":"gosu","name":"BetGosu"},{"id":"betswap-gg","symbol":"bsgg","name":"Betswap.gg"},{"id":"betswirl","symbol":"bets","name":"BetSwirl"},{"id":"betted","symbol":"bet","name":"Betted"},{"id":"betterfan","symbol":"bff","name":"BetterFan"},{"id":"betterment-digital","symbol":"bemd","name":"Betterment Digital"},{"id":"better-money","symbol":"better","name":"Better Money"},{"id":"betu","symbol":"betu","name":"Betu"},{"id":"beyondcoin","symbol":"bynd","name":"Beyondcoin"},{"id":"beyond-finance","symbol":"byn","name":"Beyondfi"},{"id":"beyondpay","symbol":"bpay","name":"Beyondpay"},{"id":"beyond-protocol","symbol":"bp","name":"Beyond Protocol"},{"id":"bezant","symbol":"bznt","name":"Bezant"},{"id":"bezoge-earth","symbol":"bezoge","name":"Bezoge Earth"},{"id":"bfg-token","symbol":"bfg","name":"BetFury"},{"id":"bficoin","symbol":"bfic","name":"Bficoin"},{"id":"bfk-warzone","symbol":"bfk","name":"BFK WARZONE"},{"id":"bgan-vault-nftx","symbol":"bgan","name":"BGAN Vault (NFTX)"},{"id":"bhbd","symbol":"bhbd","name":"bHBD"},{"id":"bhive","symbol":"bhive","name":"bHIVE"},{"id":"bhnetwork","symbol":"bhat","name":"BHNetwork"},{"id":"bho-network","symbol":"bho","name":"BHO Network"},{"id":"biblecoin","symbol":"bibl","name":"Biblecoin"},{"id":"biblepay","symbol":"bbp","name":"BiblePay"},{"id":"bibox-token","symbol":"bix","name":"Bibox"},{"id":"bib-token","symbol":"bib","name":"BIB Token"},{"id":"biconomy","symbol":"bico","name":"Biconomy"},{"id":"biconomy-exchange-token","symbol":"bit","name":"Biconomy Exchange"},{"id":"bicyclefi","symbol":"bcf","name":"BicycleFi"},{"id":"bidao","symbol":"bid","name":"Bidao"},{"id":"bidipass","symbol":"bdp","name":"BidiPass"},{"id":"bidz-coin","symbol":"bidz","name":"BIDZ Coin"},{"id":"bifi","symbol":"bifi","name":"BiFi"},{"id":"bifrost","symbol":"bfc","name":"Bifrost"},{"id":"bifrost-native-coin","symbol":"bnc","name":"Bifrost Native Coin"},{"id":"bigcap","symbol":"bigcap","name":"BIGCAP"},{"id":"big-crypto-game","symbol":"crypto","name":"Big Crypto Game"},{"id":"big-data-protocol","symbol":"bdp","name":"Big Data Protocol"},{"id":"big-defi-energy","symbol":"bde","name":"Big Defi Energy"},{"id":"big-digital-shares","symbol":"bds","name":"Big Digital Shares"},{"id":"big-dog-coin","symbol":"bdog","name":"Big Dog Coin"},{"id":"biggerminds","symbol":"mind+","name":"BiggerMINDS"},{"id":"bigo-token","symbol":"bigo","name":"BIGOCOIN"},{"id":"bigshortbets","symbol":"bigsb","name":"BigShortBets"},{"id":"big-turn","symbol":"turn","name":"Big Turn"},{"id":"bikerush","symbol":"brt","name":"Bikerush"},{"id":"bilira","symbol":"tryb","name":"BiLira"},{"id":"billiard-crypto","symbol":"bic","name":"Billiard Crypto"},{"id":"billionaire-plus","symbol":"bplus","name":"Billionaire Plus"},{"id":"billionhappiness","symbol":"bhc","name":"BillionHappiness"},{"id":"bill-murray-inu","symbol":"$bminu","name":"Bill Murray Inu"},{"id":"binamon","symbol":"bmon","name":"Binamon"},{"id":"binance-bitcoin","symbol":"btcb","name":"Binance Bitcoin"},{"id":"binancecoin","symbol":"bnb","name":"BNB"},{"id":"binance-coin-wormhole","symbol":"bnb","name":"Binance Coin (Wormhole)"},{"id":"binance-eth","symbol":"beth","name":"Binance ETH staking"},{"id":"binanceidr","symbol":"bidr","name":"BIDR"},{"id":"binance-multi-chain-capital","symbol":"bmcc","name":"Binance Multi-Chain Capital"},{"id":"binance-peg-avalanche","symbol":"avax","name":"Binance-Peg Avalanche"},{"id":"binance-peg-bitcoin-cash","symbol":"bch","name":"Binance-Peg Bitcoin Cash"},{"id":"binance-peg-cardano","symbol":"ada","name":"Binance-Peg Cardano"},{"id":"binance-peg-dogecoin","symbol":"doge","name":"Binance-Peg Dogecoin"},{"id":"binance-peg-eos","symbol":"eos","name":"Binance-Peg EOS"},{"id":"binance-peg-filecoin","symbol":"fil","name":"Binance-Peg Filecoin"},{"id":"binance-peg-firo","symbol":"firo","name":"Binance-Peg Firo"},{"id":"binance-peg-iotex","symbol":"iotx","name":"Binance-Peg IoTeX"},{"id":"binance-peg-litecoin","symbol":"ltc","name":"Binance-Peg Litecoin"},{"id":"binance-peg-ontology","symbol":"ont","name":"Binance-Peg Ontology"},{"id":"binance-peg-polkadot","symbol":"dot","name":"Binance-Peg Polkadot"},{"id":"binance-peg-xrp","symbol":"xrp","name":"Binance-Peg XRP"},{"id":"binance-smart-chain-girl","symbol":"bscgirl","name":"Binance Smart Chain Girl"},{"id":"binance-usd","symbol":"busd","name":"Binance USD"},{"id":"binance-wrapped-btc","symbol":"bbtc","name":"Binance Wrapped BTC"},{"id":"binance-wrapped-dot","symbol":"bdot","name":"Binance Wrapped DOT"},{"id":"binapet","symbol":"bpet","name":"Binapet"},{"id":"binarydao","symbol":"byte","name":"BinaryDAO"},{"id":"binaryx","symbol":"bnx","name":"BinaryX [OLD]"},{"id":"binaryx-2","symbol":"bnx","name":"BinaryX"},{"id":"bincentive","symbol":"bcnt","name":"Bincentive"},{"id":"binemon","symbol":"bin","name":"Binemon"},{"id":"bingo","symbol":"$bingo","name":"Tomorrowland"},{"id":"binjit-coin","symbol":"bnj","name":"Binjit Coin"},{"id":"binopoly","symbol":"bino","name":"Binopoly"},{"id":"binspirit","symbol":"binspirit","name":"binSPIRIT"},{"id":"binstarter","symbol":"bsr","name":"BinStarter"},{"id":"bintex-futures","symbol":"bntx","name":"Bintex Futures"},{"id":"biometric-financial","symbol":"biofi","name":"BiometricFinancial"},{"id":"biopassport","symbol":"biot","name":"Bio Passport"},{"id":"bios","symbol":"bios","name":"0x_nodes"},{"id":"birake","symbol":"bir","name":"Birake"},{"id":"birb-2","symbol":"birb","name":"Birb"},{"id":"bird-money","symbol":"bird","name":"Bird.Money"},{"id":"birdtoken","symbol":"birdtoken","name":"birdToken"},{"id":"biskit-protocol","symbol":"biskit","name":"Biskit Protocol"},{"id":"bismuth","symbol":"bis","name":"Bismuth"},{"id":"bistroo","symbol":"bist","name":"Bistroo"},{"id":"biswap","symbol":"bsw","name":"Biswap"},{"id":"bit2me","symbol":"b2m","name":"Bit2Me"},{"id":"bitant","symbol":"bitant","name":"BitANT"},{"id":"bitazza","symbol":"btz","name":"Bitazza"},{"id":"bitball","symbol":"btb","name":"Bitball"},{"id":"bitbar","symbol":"btb","name":"Bitbar"},{"id":"bitbase-token","symbol":"btbs","name":"BitBase Token"},{"id":"bitbook-token","symbol":"bbt","name":"BitBook"},{"id":"bitboost","symbol":"bbt","name":"BitBoost"},{"id":"bitcanna","symbol":"bcna","name":"BitCanna"},{"id":"bitcash","symbol":"bitc","name":"BitCash"},{"id":"bitcastle","symbol":"castle","name":"bitcastle"},{"id":"bitcci-cash","symbol":"bitcca","name":"Bitcci Cash"},{"id":"bitcicoin","symbol":"bitci","name":"Bitcicoin"},{"id":"bitci-racing-token","symbol":"brace","name":"Bitci Racing Token"},{"id":"bitcoin","symbol":"btc","name":"Bitcoin"},{"id":"bitcoin-2","symbol":"btc2","name":"Bitcoin 2"},{"id":"bitcoin-and-ethereum-standard-token","symbol":"best","name":"Bitcoin and Ethereum Standard"},{"id":"bitcoin-anonymous","symbol":"btca","name":"Bitcoin Anonymous"},{"id":"bitcoin-asia","symbol":"btca","name":"Bitcoin Asia"},{"id":"bitcoin-asset-2","symbol":"bta","name":"Bitcoin Asset"},{"id":"bitcoin-atom","symbol":"bca","name":"Bitcoin Atom"},{"id":"bitcoin-avalanche-bridged-btc-b","symbol":"btc.b","name":"Bitcoin Avalanche Bridged (BTC.b)"},{"id":"bitcoinbam","symbol":"btcbam","name":"BitcoinBam"},{"id":"bitcoin-bep2","symbol":"btcb","name":"Bitcoin BEP2"},{"id":"bitcoin-br","symbol":"btcbr","name":"Bitcoin BR"},{"id":"bitcoin-cash","symbol":"bch","name":"Bitcoin Cash"},{"id":"bitcoin-cash-sv","symbol":"bsv","name":"Bitcoin SV"},{"id":"bitcoin-city-coin","symbol":"bcity","name":"Bitcoin City Coin"},{"id":"bitcoin-confidential","symbol":"bc","name":"Bitcoin Confidential"},{"id":"bitcoin-diamond","symbol":"bcd","name":"Bitcoin Diamond"},{"id":"bitcoin-e-wallet","symbol":"bitwallet","name":"Bitcoin E-wallet"},{"id":"bitcoin-fast","symbol":"bcf","name":"Bitcoin Fast"},{"id":"bitcoin-free-cash","symbol":"bfc","name":"Bitcoin Free Cash"},{"id":"bitcoin-god","symbol":"god","name":"Bitcoin God"},{"id":"bitcoin-gold","symbol":"btg","name":"Bitcoin Gold"},{"id":"bitcoin-green","symbol":"bitg","name":"Bitcoin Green"},{"id":"bitcoin-hd","symbol":"bhd","name":"Bitcoin HD"},{"id":"bitcoin-international","symbol":"btci","name":"Bitcoin International"},{"id":"bitcoin-latinum","symbol":"ltnm","name":"Bitcoin Latinum"},{"id":"bitcoin-legend","symbol":"bcl","name":"Bitcoin Legend"},{"id":"bitcoin-lightning","symbol":"bltg","name":"Block-Logic"},{"id":"bitcoinmono","symbol":"btcmz","name":"BitcoinMono"},{"id":"bitcoin-pay","symbol":"btcpay","name":"Bitcoin Pay"},{"id":"bitcoin-plus","symbol":"xbc","name":"Bitcoin Plus"},{"id":"bitcoinpos","symbol":"btcs","name":"BitcoinPoS"},{"id":"bitcoin-private","symbol":"btcp","name":"Bitcoin Private"},{"id":"bitcoin-pro","symbol":"btcp","name":"Bitcoin Pro"},{"id":"bitcoin-red","symbol":"btcred","name":"Bitcoin Red"},{"id":"bitcoin-scrypt","symbol":"btcs","name":"Bitcoin Scrypt"},{"id":"bitcoin-subsidium","symbol":"xbtx","name":"Bitcoin Subsidium"},{"id":"bitcoin-trc20","symbol":"btct","name":"Bitcoin TRC20"},{"id":"bitcoin-trust","symbol":"bct","name":"Bitcoin Trust"},{"id":"bitcointry-token","symbol":"btty","name":"Bitcointry Token"},{"id":"bitcoinv","symbol":"btcv","name":"BitcoinV"},{"id":"bitcoin-vault","symbol":"btcv","name":"Bitcoin Vault"},{"id":"bitcoinvb","symbol":"btcvb","name":"BitcoinVB"},{"id":"bitcoinx","symbol":"bcx","name":"BitcoinX"},{"id":"bitcoinx-2","symbol":"btcx","name":"BitcoinXGames"},{"id":"bitcoinz","symbol":"btcz","name":"BitcoinZ"},{"id":"bitcoiva","symbol":"bca","name":"Bitcoiva"},{"id":"bitcomine","symbol":"bme","name":"BitcoMine"},{"id":"bitconey","symbol":"bitconey","name":"BitConey"},{"id":"bitcore","symbol":"btx","name":"BitCore"},{"id":"bitdao","symbol":"bit","name":"BitDAO"},{"id":"bite","symbol":"bite","name":"BITE"},{"id":"bitenium-token","symbol":"bt","name":"Bitenium"},{"id":"bitflowers","symbol":"petal","name":"bitFlowers"},{"id":"bitforex","symbol":"bf","name":"Bitforex"},{"id":"bit-game-verse-token","symbol":"bgvt","name":"Bit Game Verse Token"},{"id":"bitgear","symbol":"gear","name":"Bitgear"},{"id":"bitgem","symbol":"xbtg","name":"Bitgem"},{"id":"bitget-token","symbol":"bgb","name":"Bitget Token"},{"id":"bitguild","symbol":"plat","name":"BitGuild PLAT"},{"id":"bithachi","symbol":"bith","name":"Bithachi"},{"id":"bithash-token","symbol":"bt","name":"BitHash"},{"id":"bit-hotel","symbol":"bth","name":"Bit Hotel"},{"id":"bitica-coin","symbol":"bdcc","name":"BITICA COIN"},{"id":"bitindi-chain","symbol":"bni","name":"Bitindi Chain"},{"id":"bitkub-coin","symbol":"kub","name":"Bitkub Coin"},{"id":"bitlocus","symbol":"btl","name":"Bitlocus"},{"id":"bitmark","symbol":"marks","name":"Bitmark"},{"id":"bitmart-token","symbol":"bmx","name":"BitMart"},{"id":"bitmex-token","symbol":"bmex","name":"BitMEX"},{"id":"bitmon","symbol":"bit","name":"Bitmon"},{"id":"bitnautic","symbol":"btntv2","name":"BitNautic"},{"id":"bito-coin","symbol":"bito","name":"BITO Coin"},{"id":"bitone","symbol":"bio","name":"BITONE"},{"id":"bitorbit","symbol":"bitorb","name":"BitOrbit"},{"id":"bitoreum","symbol":"btrm","name":"Bitoreum"},{"id":"bitpaid-token","symbol":"btp","name":"Bitpaid"},{"id":"bitpanda-ecosystem-token","symbol":"best","name":"Bitpanda Ecosystem"},{"id":"bitrise-token","symbol":"brise","name":"Bitgert"},{"id":"bitrue-token","symbol":"btr","name":"Bitrue Coin"},{"id":"bitscrow","symbol":"btscrw","name":"Bitscrow"},{"id":"bitshares","symbol":"bts","name":"BitShares"},{"id":"bitshiba","symbol":"shiba","name":"BitShiba"},{"id":"bitsong","symbol":"btsg","name":"BitSong"},{"id":"bitspawn","symbol":"spwn","name":"Bitspawn"},{"id":"bitstake","symbol":"xbs","name":"BitStake"},{"id":"bitsten-token","symbol":"bst","name":"Bitsten [OLD]"},{"id":"bit-store-coin","symbol":"store","name":"Bit Store"},{"id":"bitstubs","symbol":"stub","name":"BitStubs"},{"id":"bitsum","symbol":"mat","name":"Matka"},{"id":"bittensor","symbol":"tao","name":"Bittensor"},{"id":"bittoken","symbol":"bitt","name":"BITT"},{"id":"bittorrent","symbol":"btt","name":"BitTorrent"},{"id":"bittorrent-old","symbol":"bttold","name":"BitTorrent [OLD]"},{"id":"bittube","symbol":"tube","name":"BitTube"},{"id":"bittwatt","symbol":"bwt","name":"Bittwatt"},{"id":"bitvalley","symbol":"bitv","name":"BitValley"},{"id":"bitvote","symbol":"btv","name":"Bitvote"},{"id":"bitwhite","symbol":"btw","name":"BitWhite"},{"id":"bitwin24","symbol":"bwi","name":"Bitwin24"},{"id":"bitzen","symbol":"bzen","name":"Bitzen"},{"id":"bitzipp","symbol":"bzp","name":"BitZipp"},{"id":"biu-coin","symbol":"biu","name":"BIU COIN"},{"id":"bixb-coin","symbol":"bixb","name":"BixB Coin"},{"id":"bizzcoin","symbol":"bizz","name":"BIZZCOIN"},{"id":"bkex-token","symbol":"bkk","name":"BKEX Chain"},{"id":"blackcoin","symbol":"blk","name":"BlackCoin"},{"id":"black-diamond","symbol":"diamonds","name":"Black Diamond"},{"id":"black-dragon-society","symbol":"bds","name":"Black Dragon Society"},{"id":"blackdragon-token","symbol":"bdt","name":"BlackDragon"},{"id":"black-eyed-dragon","symbol":"bleyd","name":"Black Eyed Dragon"},{"id":"blackhat-coin","symbol":"blkc","name":"BlackHat Coin"},{"id":"blackhole-protocol","symbol":"black","name":"BlackHole Protocol"},{"id":"blackpearl-chain","symbol":"bplc","name":"BlackPearl"},{"id":"black-phoenix","symbol":"bpx","name":"Black Phoenix"},{"id":"blackpool-token","symbol":"bpt","name":"BlackPool"},{"id":"black-rabbit-ai","symbol":"brain","name":"Black Rabbit AI"},{"id":"black-stallion","symbol":"bs","name":"Black Stallion"},{"id":"black-token","symbol":"black","name":"Black Token"},{"id":"blade","symbol":"blade","name":"BladeWarrior"},{"id":"blanc","symbol":"blanc","name":"Blanc"},{"id":"blank","symbol":"blank","name":"BlockWallet"},{"id":"blaze-network","symbol":"blzn","name":"Blaze Network"},{"id":"blazestake-staked-sol","symbol":"bsol","name":"BlazeStake Staked SOL"},{"id":"bless-global-credit","symbol":"blec","name":"Bless Global Credit"},{"id":"blind-boxes","symbol":"bles","name":"Blind Boxes"},{"id":"blin-metaverse","symbol":"blin","name":"Blin Metaverse"},{"id":"blithe","symbol":"blt","name":"Blithe"},{"id":"blitz-labs","symbol":"blitz","name":"Blitz Labs"},{"id":"blitzpredict","symbol":"xbp","name":"BlitzPick"},{"id":"blizzard","symbol":"xblzd","name":"Blizzard"},{"id":"blizzard-network","symbol":"blizz","name":"Blizzard Network"},{"id":"blizz-finance","symbol":"blzz","name":"Blizz Finance"},{"id":"blocery","symbol":"bly","name":"Blocery"},{"id":"block-ape-scissors","symbol":"bas","name":"Block Ape Scissors"},{"id":"blockasset","symbol":"block","name":"Blockasset"},{"id":"blockaura","symbol":"tbac","name":"BlockAura"},{"id":"blockbank","symbol":"bbank","name":"blockbank"},{"id":"blockbase","symbol":"bbt","name":"BlockBase"},{"id":"blockblend","symbol":"bbl","name":"BlockBlend"},{"id":"blockcdn","symbol":"bcdn","name":"BlockCDN"},{"id":"blockchain-based-distributed-super-computing-platform","symbol":"mbcc","name":"Blockchain-Based Distributed Super Computing Platform"},{"id":"blockchain-bets","symbol":"bcb","name":"Blockchain Bets"},{"id":"blockchain-brawlers","symbol":"brwl","name":"Blockchain Brawlers"},{"id":"blockchain-certified-data-token","symbol":"bcdt","name":"EvidenZ"},{"id":"blockchain-cuties-universe-governance","symbol":"bcug","name":"Blockchain Cuties Universe Governance"},{"id":"blockchain-euro-project","symbol":"bepr","name":"Blockchain Euro Project"},{"id":"blockchain-monster-hunt","symbol":"bcmc","name":"Blockchain Monster Hunt"},{"id":"blockchain-of-hash-power","symbol":"bhp","name":"Blockchain of Hash Power"},{"id":"blockchainpoland","symbol":"bcp","name":"BlockchainPoland"},{"id":"blockchainspace","symbol":"guild","name":"BlockchainSpace"},{"id":"block-commerce-protocol","symbol":"bcp","name":"Block Commerce Protocol"},{"id":"block-creatures","symbol":"moolah","name":"Block Creatures"},{"id":"block-e","symbol":"block-e","name":"BLOCK-E"},{"id":"block-forest","symbol":"bft","name":"Block Forest"},{"id":"blockless","symbol":"bls","name":"Blockless"},{"id":"blockmason-credit-protocol","symbol":"bcpt","name":"Blockmason Credit Protocol"},{"id":"blockmason-link","symbol":"blink","name":"BlockMason Link"},{"id":"blockmax","symbol":"ocb","name":"BLOCKMAX"},{"id":"block-monsters","symbol":"mnstrs","name":"Block Monsters"},{"id":"blocknet","symbol":"block","name":"Blocknet"},{"id":"blockombat","symbol":"bkb","name":"BlocKombat"},{"id":"blockpad","symbol":"blos","name":"Blockius"},{"id":"blockpass","symbol":"pass","name":"Blockpass"},{"id":"blockport","symbol":"bux","name":"BUX"},{"id":"blockportal","symbol":"bptl","name":"BlockPortal"},{"id":"blockrock","symbol":"bro$","name":"BlockRock"},{"id":"blocks","symbol":"blocks","name":"BLOCKS"},{"id":"blocksafu","symbol":"bsafu","name":"BlockSafu"},{"id":"blocksmith-labs-forge","symbol":"$forge","name":"Blocksmith Labs Forge"},{"id":"blockspace-token","symbol":"bls","name":"Blocks Space"},{"id":"blocksport","symbol":"bspt","name":"Blocksport"},{"id":"blocksquare","symbol":"bst","name":"Blocksquare"},{"id":"blockstack","symbol":"stx","name":"Stacks"},{"id":"blockstar","symbol":"bst","name":"BlockStar"},{"id":"blockster","symbol":"bxr","name":"Blockster"},{"id":"blocksworkz","symbol":"blkz","name":"BlocksWorkz"},{"id":"blockton","symbol":"bton","name":"Blockton"},{"id":"blockv","symbol":"vee","name":"BLOCKv"},{"id":"blockx","symbol":"bcx","name":"BlockX"},{"id":"blockxpress","symbol":"bx","name":"BlockXpress"},{"id":"bloc-money","symbol":"bloc","name":"Bloc.Money"},{"id":"blocsport-one","symbol":"bls","name":"Metacourt"},{"id":"blocto-token","symbol":"blt","name":"Blocto"},{"id":"blokpad","symbol":"bpad","name":"BlokPad"},{"id":"bloktopia","symbol":"blok","name":"Bloktopia"},{"id":"bloody-bunny","symbol":"bony","name":"Bloody Bunny"},{"id":"bloom","symbol":"blt","name":"Bloom"},{"id":"blossom","symbol":"sakura","name":"Blossom"},{"id":"blox","symbol":"cdt","name":"Blox"},{"id":"bloxmove-erc20","symbol":"blxm","name":"bloXmove"},{"id":"blox-token","symbol":"blox","name":"Blox SDK"},{"id":"bluca","symbol":"bluc","name":"Bluca"},{"id":"blueart","symbol":"bla","name":"BLUEART TOKEN"},{"id":"blue-baikal","symbol":"bbc","name":"Blue Baikal"},{"id":"bluebenx-2","symbol":"benx","name":"BlueBenx"},{"id":"bluebit","symbol":"bbt","name":"BlueBit"},{"id":"bluejay","symbol":"blu","name":"Bluejay"},{"id":"bluelight","symbol":"kale","name":"Bluelight"},{"id":"bluemove","symbol":"move","name":"BlueMove"},{"id":"blueshift","symbol":"blues","name":"Blueshift"},{"id":"bluesparrow","symbol":"bluesparrow","name":"BlueSparrow"},{"id":"bluesparrow-token","symbol":"bluesparrow","name":"BlueSparrow [OLD]"},{"id":"bluewizard","symbol":"wiz","name":"BlueWizard"},{"id":"blur","symbol":"blur","name":"Blur"},{"id":"blurt","symbol":"blurt","name":"Blurt"},{"id":"bluzelle","symbol":"blz","name":"Bluzelle"},{"id":"bmax","symbol":"bmax","name":"BMAX"},{"id":"bmchain-token","symbol":"bmt","name":"BMCHAIN"},{"id":"bnb48-club-token","symbol":"koge","name":"KOGE"},{"id":"bnbback","symbol":"bnbback","name":"BNBBack"},{"id":"bnb-bank","symbol":"bbk","name":"BNB Bank"},{"id":"bnb-diamond","symbol":"bnbd","name":"BNB Diamond"},{"id":"bnb-dragon","symbol":"bnbdragon","name":"BnB Dragon"},{"id":"bnbpot","symbol":"bnbp","name":"BNBPot"},{"id":"bnbtiger","symbol":"bnbtiger","name":"BNB Tiger Inu"},{"id":"bnext-b3x","symbol":"b3x","name":"Bnext B3X"},{"id":"bnktothefuture","symbol":"bft","name":"BnkToTheFuture"},{"id":"bnpl-pay","symbol":"bnpl","name":"BNPL Pay"},{"id":"bnsd-finance","symbol":"bnsd","name":"BNSD Finance"},{"id":"bns-token","symbol":"bns","name":"BNS"},{"id":"bns-token-old","symbol":"bns","name":"BNS Token [OLD]"},{"id":"board","symbol":"board","name":"Board"},{"id":"bob","symbol":"bob","name":"BOB"},{"id":"boba-brewery","symbol":"bre","name":"Boba Brewery"},{"id":"bobadoge","symbol":"bdoge","name":"BobaDoge"},{"id":"boba-network","symbol":"boba","name":"Boba Network"},{"id":"bobcoin","symbol":"bobc","name":"Bobcoin"},{"id":"bobs_repair","symbol":"bob","name":"Bob's Repair"},{"id":"bocachica","symbol":"chica","name":"BocaChica"},{"id":"bocoin","symbol":"boc","name":"BOCOIN"},{"id":"boda-token","symbol":"bodav2","name":"BODA"},{"id":"bodrumspor-fan-token","symbol":"bdrm","name":"Bodrumspor Fan Token"},{"id":"body-ai","symbol":"bait","name":"Body Ai"},{"id":"bofb","symbol":"bofb","name":"bofb"},{"id":"bogged-finance","symbol":"bog","name":"Bogged Finance"},{"id":"bohr-2","symbol":"br","name":"BoHr"},{"id":"boid","symbol":"boid","name":"Boid"},{"id":"boji","symbol":"boji","name":"BOJI"},{"id":"boku","symbol":"boku","name":"Boryoku Dragonz"},{"id":"bold-point","symbol":"bpt","name":"Bold Point"},{"id":"bole-token","symbol":"bole","name":"Boleld"},{"id":"bolide","symbol":"blid","name":"Bolide"},{"id":"bolivarcoin","symbol":"boli","name":"Bolivarcoin"},{"id":"bollycoin","symbol":"bolly","name":"BollyCoin"},{"id":"bologna-fc-fan-token","symbol":"bfc","name":"Bologna FC Fan Token"},{"id":"bolt","symbol":"bolt","name":"Bolt"},{"id":"bomb","symbol":"bomb","name":"BOMB"},{"id":"bombcrypto-coin","symbol":"bomb","name":"Bombcrypto Coin"},{"id":"bomber-coin","symbol":"bcoin","name":"BombCrypto"},{"id":"bomb-money","symbol":"bomb","name":"Bomb Money"},{"id":"bomb-money-bshare","symbol":"bshare","name":"Bomb Money BShare"},{"id":"bonded-cronos","symbol":"bcro","name":"Bonded Cronos"},{"id":"bondly","symbol":"bondly","name":"Forj"},{"id":"bondly-defi","symbol":"bond","name":"Bondly"},{"id":"bone-2","symbol":"bone","name":"Bone"},{"id":"bonerium-boneswap","symbol":"bswp","name":"Bonerium BoneSwap"},{"id":"bone-shibaswap","symbol":"bone","name":"Bone ShibaSwap"},{"id":"boneswap","symbol":"bone","name":"BoneSwap"},{"id":"bone-token","symbol":"bone","name":"PolyPup Bone"},{"id":"bonfi","symbol":"bnf","name":"BonFi"},{"id":"bonfida","symbol":"fida","name":"Bonfida"},{"id":"bonfire","symbol":"bonfire","name":"Bonfire"},{"id":"bongweedcoin","symbol":"bwc","name":"BongWeedCoin"},{"id":"bonk","symbol":"bonk","name":"Bonk"},{"id":"bonq","symbol":"bnq","name":"Bonq"},{"id":"bonq-euro","symbol":"beur","name":"Bonq Euro"},{"id":"bontecoin","symbol":"bonte","name":"Bontecoin"},{"id":"boo","symbol":"boo","name":"Boo"},{"id":"boo-finance","symbol":"boofi","name":"Boo Finance"},{"id":"boo-mirrorworld","symbol":"xboo","name":"Boo MirrorWorld"},{"id":"boop","symbol":"boop","name":"Boop"},{"id":"boosted-lusd","symbol":"blusd","name":"Boosted LUSD"},{"id":"booster","symbol":"boo","name":"Booster"},{"id":"bora","symbol":"bora","name":"BORA"},{"id":"borderless-money","symbol":"bom","name":"Borderless Money"},{"id":"borealis","symbol":"brl","name":"Borealis"},{"id":"bored","symbol":"$bored","name":"Bored"},{"id":"bored-apemove","symbol":"bape","name":"Bored APEmove"},{"id":"bored-ape-social-club","symbol":"bape","name":"Bored Ape Social Club"},{"id":"bored-candy-city","symbol":"candy","name":"Bored Candy City"},{"id":"boredmemes","symbol":"boredm","name":"BoredMemes"},{"id":"boringdao","symbol":"boring","name":"BoringDAO"},{"id":"boringdao-btc","symbol":"obtc","name":"BoringDAO BTC"},{"id":"boringdao-[old]","symbol":"bor","name":"BoringDAO [OLD]"},{"id":"boring-protocol","symbol":"bop","name":"Boring Protocol"},{"id":"bork","symbol":"bork","name":"Bork"},{"id":"bosagora","symbol":"boa","name":"BOSagora"},{"id":"boson-protocol","symbol":"boson","name":"Boson Protocol"},{"id":"boss","symbol":"boss","name":"Boss"},{"id":"bossdao","symbol":"boss","name":"BossDao"},{"id":"bossswap","symbol":"boss","name":"Boss Swap"},{"id":"bostrom","symbol":"boot","name":"Bostrom"},{"id":"botopiafinance","symbol":"btop","name":"BotopiaFinance"},{"id":"bot-planet","symbol":"bot","name":"Bot Planet"},{"id":"botto","symbol":"botto","name":"Botto"},{"id":"bottos","symbol":"bto","name":"Bottos"},{"id":"botxcoin","symbol":"botx","name":"BOTXCOIN"},{"id":"boulpik-token","symbol":"boulpik","name":"Boulpik Token"},{"id":"bountie-hunter","symbol":"bountie","name":"Bountie Hunter"},{"id":"bounty","symbol":"bnty","name":"Bounty"},{"id":"bounty0x","symbol":"bnty","name":"Bounty0x"},{"id":"bountymarketcap","symbol":"bmc","name":"BountyMarketCap"},{"id":"bovineverse-bvt","symbol":"bvt","name":"Bovineverse BVT"},{"id":"bowl-shibarium","symbol":"bowl","name":"BOWL SHIBARIUM"},{"id":"bowscoin","symbol":"bsc","name":"BowsCoin"},{"id":"boxa","symbol":"boxa","name":"BOXA"},{"id":"boxaxis","symbol":"baxs","name":"BoxAxis"},{"id":"boxcasino","symbol":"boxc","name":"BOXCASINO"},{"id":"boxch","symbol":"boxch","name":"Utility Token Boxch"},{"id":"boxerdoge","symbol":"boxerdoge","name":"BoxerDOGE"},{"id":"boxer-inu","symbol":"boxer","name":"Boxer Inu"},{"id":"bpegd","symbol":"bpeg","name":"BPEGd"},{"id":"bpm","symbol":"bpm","name":"BPM"},{"id":"b-protocol","symbol":"bpro","name":"B.Protocol"},{"id":"bracelet","symbol":"brc","name":"Bracelet"},{"id":"brain-sync","symbol":"syncbrain","name":"Brain Sync"},{"id":"braintrust","symbol":"btrst","name":"Braintrust"},{"id":"brandpad-finance","symbol":"brand","name":"BrandPad Finance"},{"id":"brave-power-crystal","symbol":"bpc","name":"Brave Power Crystal"},{"id":"brazil-fan-token","symbol":"bft","name":"Brazil National Football Team Fan Token"},{"id":"brcp-token","symbol":"brcp","name":"BRCP"},{"id":"bread","symbol":"brd","name":"Bread"},{"id":"breederdao","symbol":"breed","name":"BreederDAO"},{"id":"brewlabs","symbol":"brewlabs","name":"Brewlabs"},{"id":"brick","symbol":"brick","name":"r/FortNiteBR Bricks"},{"id":"brick-token","symbol":"brick","name":"Brick"},{"id":"bridge-mutual","symbol":"bmi","name":"Bridge Mutual"},{"id":"bridge-network","symbol":"brdg","name":"Bridge Network"},{"id":"bridge-oracle","symbol":"brg","name":"Bridge Oracle"},{"id":"bright-token","symbol":"bright","name":"BrightID"},{"id":"bright-union","symbol":"bright","name":"Bright Union"},{"id":"brightypad","symbol":"byp","name":"BrightyPad"},{"id":"brise-paradise","symbol":"prds","name":"Brise Paradise"},{"id":"britto","symbol":"brt","name":"Britto"},{"id":"brmv-token","symbol":"brmv","name":"BRMV"},{"id":"brn-metaverse","symbol":"brn","name":"BRN Metaverse"},{"id":"brokkr","symbol":"bro","name":"Brokkr"},{"id":"brokoli","symbol":"brkl","name":"Brokoli"},{"id":"broovs-projects","symbol":"brs","name":"Broovs Projects"},{"id":"brother-music-platform","symbol":"bmp","name":"Brother Music Platform"},{"id":"bro-token","symbol":"bro","name":"Bro"},{"id":"brr-protocol","symbol":"brr","name":"Brr Protocol"},{"id":"brz","symbol":"brz","name":"Brazilian Digital"},{"id":"bscarmy","symbol":"barmy","name":"BscArmy"},{"id":"bscbets","symbol":"bets","name":"BSCBETS"},{"id":"bscex","symbol":"bscx","name":"BSCEX"},{"id":"bsclaunch","symbol":"bsl","name":"BSClaunch"},{"id":"bscpad","symbol":"bscpad","name":"BSCPAD"},{"id":"bscstarter","symbol":"start","name":"Starter.xyz"},{"id":"bsc-station","symbol":"bscs","name":"BSC Station"},{"id":"bscview","symbol":"bscv","name":"Bscview"},{"id":"bsdium","symbol":"bscd","name":"BSDium"},{"id":"bsocial","symbol":"bins","name":"BSocial"},{"id":"btaf-token","symbol":"btaf","name":"BTAF token"},{"id":"btc-2x-flexible-leverage-index","symbol":"btc2x-fli","name":"BTC 2x Flexible Leverage Index"},{"id":"btcmoon","symbol":"btcm","name":"BTCMoon"},{"id":"btc-proxy","symbol":"btcpx","name":"BTC Proxy"},{"id":"btc-standard-hashrate-token","symbol":"btcst","name":"BTC Standard Hashrate Token"},{"id":"btf","symbol":"btf","name":"Bitcoin Faith"},{"id":"bt-finance","symbol":"bt","name":"BT.Finance"},{"id":"btour-chain","symbol":"msot","name":"BTour Chain"},{"id":"btrips","symbol":"btr","name":"BTRIPS"},{"id":"bts-chain","symbol":"btsc","name":"BTS Chain"},{"id":"btse-token","symbol":"btse","name":"BTSE Token"},{"id":"btu-protocol","symbol":"btu","name":"BTU Protocol"},{"id":"bubblefong","symbol":"bbf","name":"Bubblefong"},{"id":"buckhath-coin","symbol":"bhig","name":"BuckHath Coin"},{"id":"bucky-badger","symbol":"bucky","name":"Bucky Badger"},{"id":"buddy-dao","symbol":"bdy","name":"Buddy DAO"},{"id":"buff-coin","symbol":"buff","name":"Buff Coin"},{"id":"buff-doge-coin","symbol":"dogecoin","name":"Buff Doge Coin"},{"id":"buffedshiba","symbol":"bshib","name":"BuffedShiba"},{"id":"buff-floki","symbol":"bufloki","name":"Buff Floki"},{"id":"buff-samo","symbol":"bsamo","name":"Buff Samo"},{"id":"buff-shiba-inu","symbol":"buffshiba","name":"Buff Shiba Inu"},{"id":"buffswap","symbol":"buffs","name":"BuffSwap"},{"id":"bugg-finance","symbol":"bugg","name":"BUGG Finance"},{"id":"buhund","symbol":"buh","name":"Buhund"},{"id":"buidl-acadamey","symbol":"$bdgv","name":"BUIDL Acadamey"},{"id":"build","symbol":"build","name":"BUILD"},{"id":"buildup","symbol":"bup","name":"BuildUp"},{"id":"bulk-network","symbol":"bulk","name":"Bulk Network"},{"id":"bull-btc-club","symbol":"bbc","name":"Bull BTC Club"},{"id":"bull-coin","symbol":"bull","name":"Bull Coin"},{"id":"bulldog-inu","symbol":"bull","name":"BullDog Inu"},{"id":"bullet-2","symbol":"blt","name":"Bullet"},{"id":"bull-game","symbol":"bgt","name":"Bull Game ToKens"},{"id":"bullieverse","symbol":"bull","name":"Bullieverse"},{"id":"bullion","symbol":"cbx","name":"Bullion"},{"id":"bullishapes","symbol":"bullish","name":"BullishApes"},{"id":"bullperks","symbol":"blp","name":"BullPerks"},{"id":"bumblebot","symbol":"bumble","name":"Bumblebot"},{"id":"bumoon","symbol":"bumn","name":"BUMooN"},{"id":"bumper","symbol":"bump","name":"Bumper"},{"id":"bundles","symbol":"bund","name":"Bund V2"},{"id":"bunicorn","symbol":"buni","name":"Bunicorn"},{"id":"bunnycoin","symbol":"bun","name":"Bunnycoin"},{"id":"bunnyducky","symbol":"bud","name":"BunnyDucky"},{"id":"bunny-king-metaverse","symbol":"bkm","name":"Bunny King Metaverse"},{"id":"bunnypark","symbol":"bp","name":"BunnyPark"},{"id":"bunnypark-game","symbol":"bg","name":"BunnyPark Game"},{"id":"bunny-token-polygon","symbol":"polybunny","name":"Pancake Bunny Polygon"},{"id":"bunnyverse","symbol":"bnv","name":"BunnyVerse"},{"id":"bunscake","symbol":"bscake","name":"Bunscake"},{"id":"burency","symbol":"buy","name":"Burency"},{"id":"burger-swap","symbol":"burger","name":"BurgerCities"},{"id":"burnfloki","symbol":"bfloki","name":"BurnFloki"},{"id":"burp","symbol":"burp","name":"Burp"},{"id":"burrito-boyz-floor-index","symbol":"burr","name":"Burrito Boyz Floor Index"},{"id":"burrow","symbol":"brrr","name":"Burrow"},{"id":"bursaspor-fan-token","symbol":"tmsh","name":"Bursaspor Fan Token"},{"id":"busd-plenty-bridge","symbol":"busd.e","name":"BUSD (Plenty Bridge)"},{"id":"busdx","symbol":"busdx","name":"BUSDX"},{"id":"busdx-fuel","symbol":"xfuel","name":"BUSDX Fuel"},{"id":"business-universe","symbol":"buun","name":"Business Universe"},{"id":"busy-dao","symbol":"busy","name":"Busy"},{"id":"butterfly-protocol-2","symbol":"bfly","name":"Butterfly Protocol"},{"id":"buying","symbol":"buy","name":"Buying.com"},{"id":"buymainstreet","symbol":"mainst","name":"Main Street"},{"id":"buymore","symbol":"more","name":"BuyMORE"},{"id":"buzz","symbol":"buzz","name":"BUZZ"},{"id":"b-watch-box","symbol":"box","name":"B.watch Box"},{"id":"bxh","symbol":"bxh","name":"BXH"},{"id":"byepix","symbol":"epix","name":"Byepix"},{"id":"byteball","symbol":"gbyte","name":"Obyte"},{"id":"bytecoin","symbol":"bcn","name":"Bytecoin"},{"id":"byteex","symbol":"bx","name":"ByteEx"},{"id":"bytenext","symbol":"bnu","name":"ByteNext"},{"id":"bytom","symbol":"btm","name":"Bytom"},{"id":"bytz","symbol":"bytz","name":"BYTZ"},{"id":"bzetcoin","symbol":"bzet","name":"BzetCoin"},{"id":"bzx-protocol","symbol":"bzrx","name":"bZx Protocol"},{"id":"bzzone","symbol":"bzzone","name":"Bzzone"},{"id":"caashcow","symbol":"cow","name":"CaashCow"},{"id":"caave","symbol":"caave","name":"cAAVE"},{"id":"cache-gold","symbol":"cgt","name":"CACHE Gold"},{"id":"cad-coin","symbol":"cadc","name":"CAD Coin"},{"id":"caduceus","symbol":"cmp","name":"Caduceus"},{"id":"caica-coin","symbol":"cicc","name":"CAICA Coin"},{"id":"cairo-finance-2","symbol":"caf","name":"CAIRO"},{"id":"cajutel","symbol":"caj","name":"Cajutel"},{"id":"cake-bank","symbol":"cakebank","name":"Cake Bank"},{"id":"cake-monster","symbol":"monsta","name":"Cake Monster"},{"id":"cakepad","symbol":"ckp","name":"CakePad"},{"id":"cakeswap","symbol":"cakeswap","name":"CakeSwap"},{"id":"caketools","symbol":"ckt","name":"Caketools"},{"id":"cakewswap","symbol":"cakew","name":"CakeWSwap"},{"id":"calamari-network","symbol":"kma","name":"Calamari Network"},{"id":"calaxy","symbol":"clxy","name":"Calaxy"},{"id":"calicoin","symbol":"cali","name":"CaliCoin"},{"id":"callisto","symbol":"clo","name":"Callisto Network"},{"id":"calo-app","symbol":"calo","name":"Calo"},{"id":"calo-fit","symbol":"fit","name":"Calo FIT"},{"id":"calo-indoor","symbol":"ifit","name":"Calo Indoor"},{"id":"calvaria-doe","symbol":"ria","name":"Calvaria: DoE"},{"id":"camelcoin","symbol":"cml","name":"Camelcoin"},{"id":"camelot-token","symbol":"grail","name":"Camelot Token"},{"id":"camp","symbol":"camp","name":"Camp"},{"id":"canadian-inuit-dog-2","symbol":"cadinu","name":"Canadian Inuit Dog"},{"id":"canary","symbol":"cnr","name":"Canary"},{"id":"canary-dollar","symbol":"cand","name":"Canary Dollar"},{"id":"canaryx","symbol":"cnyx","name":"CanaryX"},{"id":"candylad","symbol":"candylad","name":"Candylad"},{"id":"cannabiscoin","symbol":"cann","name":"CannabisCoin"},{"id":"cannumo","symbol":"canu","name":"Cannumo"},{"id":"cantina-royale","symbol":"crt","name":"Cantina Royale"},{"id":"canto","symbol":"canto","name":"CANTO"},{"id":"cantobelieve","symbol":"believe","name":"CantoBelieve"},{"id":"cantobonk","symbol":"cbonk","name":"CantoBonk"},{"id":"canto-crabs-chip","symbol":"crab","name":"Canto Crabs Chip"},{"id":"cantohm","symbol":"cohm","name":"CantOHM"},{"id":"canto-inu","symbol":"cinu","name":"Canto Inu"},{"id":"canto-shib","symbol":"cshib","name":"Canto Shib"},{"id":"cantosino-com-profit-pass","symbol":"cpp","name":"Cantosino.com Profit Pass"},{"id":"canvas-n-glr","symbol":"glr","name":"GalleryCoin"},{"id":"cap","symbol":"cap","name":"Cap"},{"id":"capapult","symbol":"capa","name":"Capapult"},{"id":"capital-dao-starter-token","symbol":"cds","name":"Capital DAO Starter"},{"id":"capital-x-cell","symbol":"cxc","name":"CAPITAL X CELL"},{"id":"cappasity","symbol":"capp","name":"Cappasity"},{"id":"capricorn","symbol":"corn","name":"Capricorn"},{"id":"captain-inu","symbol":"cptinu","name":"Captain Inu"},{"id":"captain-planet","symbol":"ctp","name":"Captain Planet"},{"id":"capybara","symbol":"capy","name":"Capybara"},{"id":"capybara-bsc","symbol":"capy","name":"Capybara BSC"},{"id":"carbon","symbol":"crbn","name":"Carbon"},{"id":"carbon-browser","symbol":"csix","name":"Carbon Browser"},{"id":"carboncoin","symbol":"carbon","name":"Carboncoin"},{"id":"carbon-credit","symbol":"cct","name":"Carbon Credit"},{"id":"carbon-labs","symbol":"carb","name":"Carbon Labs"},{"id":"carbon-usd","symbol":"usc","name":"Carbon USD"},{"id":"cardano","symbol":"ada","name":"Cardano"},{"id":"cardanum","symbol":"carda","name":"Cardanum"},{"id":"cardence","symbol":"$crdn","name":"Cardence"},{"id":"cardiocoin","symbol":"crdc","name":"Cardiocoin"},{"id":"cardstack","symbol":"card","name":"Cardstack"},{"id":"cardstarter","symbol":"cards","name":"Cardstarter"},{"id":"cardwallet","symbol":"cw","name":"CardWallet"},{"id":"carecoin","symbol":"care","name":"CareCoin"},{"id":"cargolink","symbol":"clx","name":"CargoLink"},{"id":"cargox","symbol":"cxo","name":"CargoX"},{"id":"carmin","symbol":"carmin","name":"Carmin"},{"id":"carnomaly","symbol":"carr","name":"Carnomaly"},{"id":"carrieverse","symbol":"cvtx","name":"CarrieVerse"},{"id":"carrot-stable-coin","symbol":"carrot","name":"Carrot Stable Coin"},{"id":"carry","symbol":"cre","name":"Carry"},{"id":"cartesi","symbol":"ctsi","name":"Cartesi"},{"id":"carvertical","symbol":"cv","name":"carVertical"},{"id":"cascadia","symbol":"cc","name":"Cascadia"},{"id":"cashaa","symbol":"cas","name":"Cashaa"},{"id":"cashbackpro","symbol":"cbp","name":"CashBackPro"},{"id":"cashbet-coin","symbol":"cbc","name":"CBC.network"},{"id":"cashcats","symbol":"$cats","name":"CashCats"},{"id":"cashcow","symbol":"cow","name":"CashCow"},{"id":"cash-driver","symbol":"cd","name":"Cash Driver"},{"id":"cashera","symbol":"csr","name":"Cashera"},{"id":"cash-tech","symbol":"cate","name":"Cash Tech"},{"id":"cashzone","symbol":"cashz","name":"CashZone"},{"id":"casinocoin","symbol":"csc","name":"Casinocoin"},{"id":"casper-network","symbol":"cspr","name":"Casper Network"},{"id":"casperpad","symbol":"cspd","name":"CasperPad"},{"id":"castello-coin","symbol":"cast","name":"Castello Coin"},{"id":"cat","symbol":"cat","name":"Kitty Finance CAT"},{"id":"catalina-whales-index","symbol":"whales","name":"Catalina Whales Index"},{"id":"catapult","symbol":"atd","name":"A2DAO"},{"id":"catapult-ac","symbol":"cplt","name":"Catapult.ac"},{"id":"catbonk","symbol":"cabo","name":"Catbonk"},{"id":"catboy-2","symbol":"catboy","name":"CatBoy"},{"id":"cat-cat-token","symbol":"cat","name":"Cat"},{"id":"catceo","symbol":"catceo","name":"CATCEO"},{"id":"catch-up","symbol":"cu","name":"Catch Up"},{"id":"catchy","symbol":"catchy","name":"Catchy"},{"id":"catcoin-bsc","symbol":"cat","name":"Catcoin BSC"},{"id":"catcoin-cash","symbol":"catcoin","name":"Catcoin"},{"id":"catcoin-token","symbol":"cats","name":"CatCoin Token"},{"id":"catecoin","symbol":"cate","name":"CateCoin"},{"id":"catex-token","symbol":"catt","name":"Catex"},{"id":"catge-coin","symbol":"catge","name":"Catge Coin"},{"id":"catgirl","symbol":"catgirl","name":"Catgirl"},{"id":"catgirl-ai","symbol":"catai","name":"Catgirl AI"},{"id":"catgirl-optimus","symbol":"optig","name":"Catgirl Optimus"},{"id":"catheon-gaming","symbol":"catheon","name":"Catheon Gaming"},{"id":"catking","symbol":"cking","name":"CatKing"},{"id":"cato","symbol":"cato","name":"CATO"},{"id":"catocoin","symbol":"cato","name":"CatoCoin"},{"id":"catpay","symbol":"catpay","name":"CATpay"},{"id":"cats","symbol":"cats","name":"Cats"},{"id":"catscoin","symbol":"cats","name":"Catscoin"},{"id":"cats-coin-1722f9f2-68f8-4ad8-a123-2835ea18abc5","symbol":"cts","name":"Cats Coin (BSC)"},{"id":"cat-sphynx","symbol":"cpx","name":"Cat Sphynx"},{"id":"cat-token","symbol":"cat","name":"Mooncat CAT"},{"id":"catvills-coin","symbol":"catvills","name":"Catvills Coin"},{"id":"catzcoin","symbol":"catz","name":"CatzCoin"},{"id":"cavapoo","symbol":"cava","name":"Cavapoo"},{"id":"cave","symbol":"cave","name":"CaveWorld"},{"id":"cbet-token","symbol":"cbet","name":"CBET"},{"id":"cbfinu","symbol":"cbfinu","name":"CBFINU"},{"id":"cbomber","symbol":"cbomber","name":"CBOMBER"},{"id":"cbyte-network","symbol":"cbyte","name":"CBYTE Network"},{"id":"cca","symbol":"cca","name":"CCA"},{"id":"c-charge","symbol":"cchg","name":"C+Charge"},{"id":"ccomp","symbol":"ccomp","name":"cCOMP"},{"id":"ccore","symbol":"cco","name":"Ccore"},{"id":"cdai","symbol":"cdai","name":"cDAI"},{"id":"cdbio","symbol":"mcd","name":"CDbio"},{"id":"cdzexchange","symbol":"cdz","name":"CDzExchange"},{"id":"ceasports","symbol":"cspt","name":"CEASports"},{"id":"cebiolabs","symbol":"cbsl","name":"CeBioLabs"},{"id":"ceek","symbol":"ceek","name":"CEEK Smart VR"},{"id":"ceji","symbol":"ceji","name":"Ceji"},{"id":"celeb","symbol":"celeb","name":"Celeb"},{"id":"celer-network","symbol":"celr","name":"Celer Network"},{"id":"celery","symbol":"cly","name":"Celery"},{"id":"celestial","symbol":"celt","name":"Celestial"},{"id":"celletf","symbol":"ecell","name":"Consensus Cell Network"},{"id":"cellframe","symbol":"cell","name":"Cellframe"},{"id":"celo","symbol":"celo","name":"Celo"},{"id":"celo-dollar","symbol":"cusd","name":"Celo Dollar"},{"id":"celo-euro","symbol":"ceur","name":"Celo Euro"},{"id":"celo-euro-wormhole","symbol":"ceur","name":"Celo Euro (Wormhole)"},{"id":"celolaunch","symbol":"cla","name":"CeloLaunch"},{"id":"celo-real-creal","symbol":"creal","name":"Celo Real (cREAL)"},{"id":"celostarter","symbol":"cstar","name":"CeloStarter"},{"id":"celsius-degree-token","symbol":"cel","name":"Celsius Network"},{"id":"celsiusx-wrapped-doge","symbol":"cxdoge","name":"CelsiusX Wrapped DOGE"},{"id":"celsiusx-wrapped-eth","symbol":"cxeth","name":"CelsiusX Wrapped ETH"},{"id":"cens-world","symbol":"cens","name":"Cens World"},{"id":"centaur","symbol":"cntr","name":"Centaur"},{"id":"centaurify","symbol":"cent","name":"Centaurify"},{"id":"centcex","symbol":"cenx","name":"Centcex"},{"id":"centrality","symbol":"cennz","name":"CENNZnet"},{"id":"centric-cash","symbol":"cns","name":"Centric Swap"},{"id":"centrifuge","symbol":"cfg","name":"Centrifuge"},{"id":"centrofi","symbol":"centro","name":"CentroFi"},{"id":"centurion-invest","symbol":"cix","name":"Centurion Invest"},{"id":"ceo","symbol":"ceo","name":"CEO"},{"id":"cerberus-2","symbol":"crbrus","name":"Cerberus"},{"id":"cere-network","symbol":"cere","name":"Cere Network"},{"id":"ceres","symbol":"ceres","name":"Ceres"},{"id":"certik","symbol":"ctk","name":"Shentu"},{"id":"cetf","symbol":"cetf","name":"Cell ETF"},{"id":"cex-ai","symbol":"cex-ai","name":"CEX AI"},{"id":"cex-trade","symbol":"cexd","name":"Cex-Trade"},{"id":"cfl365-finance","symbol":"cfl365","name":"CFL365 Finance"},{"id":"cfx-quantum","symbol":"cfxq","name":"CFX Quantum"},{"id":"chain-2","symbol":"xcn","name":"Onyxcoin"},{"id":"chain4energy","symbol":"c4e","name":"Chain4Energy"},{"id":"chainbing","symbol":"cbg","name":"Chainbing"},{"id":"chaincade","symbol":"chaincade","name":"ChainCade"},{"id":"chainflix","symbol":"cfxt","name":"Chainflix"},{"id":"chain-games","symbol":"chain","name":"Chain Games"},{"id":"chainge-finance","symbol":"chng","name":"Chainge Finance"},{"id":"chaingpt","symbol":"cgpt","name":"ChainGPT"},{"id":"chain-guardians","symbol":"cgg","name":"Chain Guardians"},{"id":"chainium","symbol":"chx","name":"WeOwn"},{"id":"chainlink","symbol":"link","name":"Chainlink"},{"id":"chainlink-plenty-bridge","symbol":"link.e","name":"Chainlink (Plenty Bridge)"},{"id":"chainlist","symbol":"clist","name":"Chainlist"},{"id":"chain-of-legends","symbol":"cleg","name":"Chain of Legends"},{"id":"chainpay","symbol":"cpay","name":"Chainpay"},{"id":"chain-pet","symbol":"cpet","name":"Chain Pet"},{"id":"chainport","symbol":"portx","name":"ChainPort"},{"id":"chains","symbol":"cha","name":"Chains"},{"id":"chains-of-war","symbol":"mira","name":"Chains of War"},{"id":"chainsquare","symbol":"chs","name":"Chainsquare"},{"id":"chainswap","symbol":"asap","name":"Chainswap"},{"id":"chain-wars-essence","symbol":"cwe","name":"Chain Wars"},{"id":"chainx","symbol":"pcx","name":"ChainX"},{"id":"challenge-coin","symbol":"hero","name":"Challenge Coin"},{"id":"champion","symbol":"cham","name":"Champion"},{"id":"change","symbol":"cag","name":"Change"},{"id":"changenow","symbol":"now","name":"ChangeNOW"},{"id":"changer","symbol":"cng","name":"Changer"},{"id":"changex","symbol":"change","name":"ChangeX"},{"id":"channels","symbol":"can","name":"Channels"},{"id":"chaotic-finance","symbol":"chaos","name":"Chaotic Finance"},{"id":"charactbit","symbol":"chb","name":"Charactbit"},{"id":"characterai","symbol":"chai","name":"CharacterAI"},{"id":"charg-coin","symbol":"chg","name":"Charg Coin"},{"id":"chargedefi-charge","symbol":"charge","name":"ChargeDeFi Charge"},{"id":"chargedefi-static","symbol":"static","name":"ChargeDeFi Static"},{"id":"charged-particles","symbol":"ionx","name":"Charged Particles"},{"id":"charity-alfa","symbol":"mich","name":"Charity Alfa"},{"id":"charitydao","symbol":"chd","name":"CharityDAO"},{"id":"charity-dao-token","symbol":"chdao","name":"Charity DAO Token"},{"id":"charli3","symbol":"c3","name":"Charli3"},{"id":"charlie-finance","symbol":"cht","name":"Charlie Finance"},{"id":"charm","symbol":"charm","name":"Charm"},{"id":"chartex","symbol":"chart","name":"ChartEx"},{"id":"chat-ai","symbol":"ai","name":"Chat AI"},{"id":"checkdot","symbol":"cdt","name":"CheckDot"},{"id":"checkerchain","symbol":"checkr","name":"CheckerChain"},{"id":"checkmate-token","symbol":"cmt","name":"CheckMate Token"},{"id":"checoin","symbol":"checoin","name":"CheCoin"},{"id":"chedda","symbol":"chedda","name":"Chedda"},{"id":"cheelee","symbol":"cheel","name":"Cheelee"},{"id":"cheems","symbol":"cheems","name":"Cheems"},{"id":"cheems-inu","symbol":"cinu","name":"CHEEMS INU [OLD]"},{"id":"cheems-inu-new","symbol":"cinu","name":"Cheems Inu [NEW]"},{"id":"cheersland","symbol":"cheers","name":"CheersLand"},{"id":"cheese","symbol":"cheese","name":"Cheese"},{"id":"cheesecakeswap","symbol":"ccake","name":"CheesecakeSwap"},{"id":"cheesedao","symbol":"cheez","name":"CheeseDAO"},{"id":"cheese-swap","symbol":"cheese","name":"Cheese Swap"},{"id":"cheesus","symbol":"cheesus","name":"Cheesus"},{"id":"chellitcoin","symbol":"chlt","name":"Chellitcoin"},{"id":"cheqd-network","symbol":"cheq","name":"CHEQD Network"},{"id":"cherish","symbol":"chc","name":"Cherish"},{"id":"cherry-network","symbol":"cher","name":"Cherry Network"},{"id":"cherryswap","symbol":"che","name":"CherrySwap"},{"id":"chesscoin-0-32","symbol":"chess","name":"ChessCoin 0.32%"},{"id":"chew","symbol":"chew","name":"CHEW"},{"id":"chex-token","symbol":"chex","name":"CHEX Token"},{"id":"chia","symbol":"xch","name":"Chia"},{"id":"chiba-inu","symbol":"chiba","name":"Chiba Inu"},{"id":"chicken","symbol":"kfc","name":"Chicken"},{"id":"chicken-town","symbol":"chickentown","name":"Chicken Town"},{"id":"chi-coin","symbol":"chi","name":"CHI Coin"},{"id":"chi-gastoken","symbol":"chi","name":"Chi Gas"},{"id":"chihiro-inu","symbol":"chiro","name":"Chihiro Inu"},{"id":"chihuahua","symbol":"hua","name":"Chihuahua"},{"id":"chihuahua-in-space","symbol":"cis","name":"Chihuahua In Space"},{"id":"chihuahuasol","symbol":"chih","name":"ChihuahuaSol"},{"id":"chihuahua-token","symbol":"huahua","name":"Chihuahua Chain"},{"id":"chihuahua-token-19fcd0de-eb4d-4fd7-bc4a-a202247dfdbb","symbol":"chh","name":"Chihuahua Token"},{"id":"chikincoin","symbol":"ckc","name":"ChikinCoin"},{"id":"chikn-egg","symbol":"egg","name":"Chikn Egg"},{"id":"chikn-feed","symbol":"feed","name":"chikn feed"},{"id":"chikn-fert","symbol":"fert","name":"Chikn Fert"},{"id":"chikn-worm","symbol":"worm","name":"Chikn Worm"},{"id":"childhoods-end","symbol":"o","name":"Childhoods End"},{"id":"chili","symbol":"chili","name":"CHILI"},{"id":"chiliz","symbol":"chz","name":"Chiliz"},{"id":"chillpill","symbol":"$chill","name":"ChillPill"},{"id":"chimaera","symbol":"wchi","name":"XAYA"},{"id":"chimeras","symbol":"chim","name":"Chimeras"},{"id":"chimp-fight","symbol":"nana","name":"Nana"},{"id":"chipstars","symbol":"chips","name":"Chipstars"},{"id":"chirpley","symbol":"chrp","name":"Chirpley"},{"id":"chives-coin","symbol":"xcc","name":"Chives Coin"},{"id":"choccyswap","symbol":"ccy","name":"ChoccySwap"},{"id":"chocolate-like-butterfly","symbol":"clb","name":"Chocolate Like Butterfly"},{"id":"choice-coin","symbol":"choice","name":"Choice Coin"},{"id":"choise","symbol":"cho","name":"Choise.com"},{"id":"chonk","symbol":"chonk","name":"Chonk"},{"id":"chooky-inu","symbol":"$choo","name":"Chooky Inu"},{"id":"chorusx","symbol":"cx1","name":"ChorusX"},{"id":"christmas-floki","symbol":"floc","name":"Christmas Floki"},{"id":"christmas-shiba","symbol":"xshib","name":"Christmas Shiba"},{"id":"chromaway","symbol":"chr","name":"Chromia"},{"id":"chromium-dollar","symbol":"cr","name":"Chromium Dollar"},{"id":"chronicle","symbol":"xnl","name":"Chronicle"},{"id":"chronicum","symbol":"chro","name":"Chronicum"},{"id":"chronobank","symbol":"time","name":"chrono.tech"},{"id":"chronoly","symbol":"crno","name":"Chronoly"},{"id":"chronos-finance","symbol":"chr","name":"Chronos Finance"},{"id":"chubbies","symbol":"chubbies20","name":"Chubbies"},{"id":"chubbyakita","symbol":"cakita","name":"ChubbyAkita"},{"id":"chumbai-valley","symbol":"chmb","name":"Chumbi Valley"},{"id":"cia","symbol":"cia","name":"CIA"},{"id":"ciento-exchange","symbol":"cnto","name":"Ciento Exchange"},{"id":"cigarette-token","symbol":"cig","name":"Cigarette"},{"id":"cindicator","symbol":"cnd","name":"Cindicator"},{"id":"cindrum","symbol":"cind","name":"Cindrum"},{"id":"cinnamoon","symbol":"cimo","name":"Cinnamoon"},{"id":"cino-games","symbol":"cino","name":"Cino Token"},{"id":"cipher-2","symbol":"cpr","name":"CIPHER"},{"id":"circlepod","symbol":"cpx","name":"Circlepod"},{"id":"circleswap","symbol":"cir","name":"CircleSwap"},{"id":"circuits-of-value","symbol":"coval","name":"Circuits of Value"},{"id":"cirquity","symbol":"cirq","name":"Cirquity"},{"id":"cirus","symbol":"cirus","name":"Cirus"},{"id":"citadao","symbol":"knight","name":"CitaDAO"},{"id":"citadel","symbol":"ctl","name":"Citadel"},{"id":"citadel-one","symbol":"xct","name":"Citadel.one"},{"id":"citizen-finance","symbol":"cifi","name":"Citizen Finance"},{"id":"citrus","symbol":"cts","name":"Citrus"},{"id":"city-of-dream","symbol":"cod","name":"City of Dream"},{"id":"citystates-medieval","symbol":"csm","name":"CityStates Medieval"},{"id":"city-tycoon-games","symbol":"ctg","name":"City Tycoon Games"},{"id":"civfund-stone","symbol":"0ne","name":"Civfund Stone"},{"id":"civic","symbol":"cvc","name":"Civic"},{"id":"civilization","symbol":"civ","name":"Civilization"},{"id":"civilization-network","symbol":"cvl","name":"Civilization Network"},{"id":"claimswap","symbol":"cla","name":"ClaimSwap"},{"id":"clams","symbol":"clam","name":"Clams"},{"id":"clash","symbol":"clh","name":"Clash"},{"id":"clash-of-lilliput","symbol":"col","name":"Clash of Lilliput"},{"id":"class-coin","symbol":"class","name":"Class Coin"},{"id":"classicbitcoin","symbol":"cbtc","name":"ClassicBitcoin"},{"id":"classicdoge","symbol":"xdoge","name":"ClassicDoge"},{"id":"classzz","symbol":"czz","name":"ClassZZ"},{"id":"claw","symbol":"claw","name":"Claw [OLD]"},{"id":"claw-2","symbol":"claw","name":"Claw"},{"id":"clay-nation","symbol":"clay","name":"Clay Nation"},{"id":"claystack-staked-matic","symbol":"csmatic","name":"ClayStack Staked MATIC"},{"id":"cleancarbon","symbol":"carbo","name":"CleanCarbon"},{"id":"cleanocean","symbol":"clean","name":"CleanOcean"},{"id":"clearcryptos","symbol":"ccx","name":"ClearCryptos"},{"id":"cleardao","symbol":"clh","name":"ClearDAO"},{"id":"clearpoll","symbol":"poll","name":"ClearPoll"},{"id":"clearpool","symbol":"cpool","name":"Clearpool"},{"id":"clear-water","symbol":"$clear","name":"Clear Water"},{"id":"clever-cvx","symbol":"clevcvx","name":"CLever CVX"},{"id":"clever-token","symbol":"clev","name":"CLever"},{"id":"climb-token-finance","symbol":"climb","name":"Climb Token Finance"},{"id":"clintex-cti","symbol":"cti","name":"ClinTex CTi"},{"id":"cliq","symbol":"ct","name":"CLIQ"},{"id":"cloakcoin","symbol":"cloak","name":"Cloakcoin"},{"id":"cloak-coin","symbol":"cloak","name":"Cloak Coin"},{"id":"clock-24","symbol":"c24","name":"Clock 24"},{"id":"cloudbric","symbol":"clbk","name":"Cloudbric"},{"id":"cloudchat","symbol":"cc","name":"CloudChat"},{"id":"cloudname","symbol":"cname","name":"Cloudname"},{"id":"cloud-pet","symbol":"cpet","name":"Cloud Pet"},{"id":"cloudtx","symbol":"cloud","name":"CloudTx"},{"id":"cloutcontracts","symbol":"ccs","name":"CloutContracts"},{"id":"clover-finance","symbol":"clv","name":"Clover Finance"},{"id":"club-atletico-independiente","symbol":"cai","name":"Club Atletico Independiente Fan Token"},{"id":"clube-atletico-mineiro-fan-token","symbol":"galo","name":"Clube Atlético Mineiro Fan Token"},{"id":"clubrare-empower","symbol":"mpwr","name":"Empower"},{"id":"club-santos-laguna-fan-token","symbol":"san","name":"Club Santos Laguna Fan Token"},{"id":"clucoin","symbol":"clu","name":"CluCoin"},{"id":"cmc-coin","symbol":"cmcc","name":"CMC Coin"},{"id":"cneta","symbol":"cneta","name":"cNETA"},{"id":"cng-casino","symbol":"cng","name":"CNG Casino"},{"id":"cnh-tether","symbol":"cnht","name":"CNH Tether"},{"id":"cnns","symbol":"cnns","name":"CNNS"},{"id":"coalculus","symbol":"coal","name":"Coalculus"},{"id":"cobak-token","symbol":"cbk","name":"Cobak"},{"id":"coban","symbol":"coban","name":"COBAN"},{"id":"coca-network","symbol":"cocn","name":"Coca Network"},{"id":"cockapoo","symbol":"cpoo","name":"Cockapoo"},{"id":"cocktailbar","symbol":"coc","name":"cocktailbar.finance"},{"id":"cocos-bcx","symbol":"cocos","name":"COCOS BCX"},{"id":"codex","symbol":"cdex","name":"Codex"},{"id":"coffin-dollar","symbol":"cousd","name":"Coffin Dollar"},{"id":"coffin-finance","symbol":"coffin","name":"Coffin Finance"},{"id":"cofix","symbol":"cofi","name":"CoFiX"},{"id":"cogecoin","symbol":"coge","name":"Cogecoin"},{"id":"cogent-sol","symbol":"cgntsol","name":"Cogent SOL"},{"id":"cogiverse","symbol":"cogi","name":"9D NFT"},{"id":"coil","symbol":"coil","name":"Coil"},{"id":"coin","symbol":"coin","name":"Coin"},{"id":"coin98","symbol":"c98","name":"Coin98"},{"id":"coin98-dollar","symbol":"cusd","name":"Coin98 Dollar"},{"id":"coinalpha","symbol":"alp","name":"CoinAlpha"},{"id":"coin-artist","symbol":"coin","name":"Coin Artist"},{"id":"coinary-token","symbol":"cyt","name":"Coinary"},{"id":"coinbase-tokenized-stock-defichain","symbol":"dcoin","name":"Coinbase Tokenized Stock Defichain"},{"id":"coinbase-wrapped-staked-eth","symbol":"cbeth","name":"Coinbase Wrapped Staked ETH"},{"id":"coinbet-finance","symbol":"cfi","name":"Coinbet Finance"},{"id":"coinbond","symbol":"cbd","name":"Coinbond"},{"id":"coin-capsule","symbol":"caps","name":"Ternoa"},{"id":"coinclaim","symbol":"clm","name":"CoinClaim"},{"id":"coindom","symbol":"scc","name":"Stem Cell Coin"},{"id":"coin-edelweis","symbol":"edel","name":"Coin Edelweis"},{"id":"coinerr","symbol":"err","name":"Coinerr"},{"id":"coinex-token","symbol":"cet","name":"CoinEx"},{"id":"coinfarm","symbol":"cfarm","name":"CoinFarm"},{"id":"coinfi","symbol":"cofi","name":"CoinFi"},{"id":"coinfirm-amlt","symbol":"amlt","name":"AMLT Network"},{"id":"coinflect","symbol":"wcflt","name":"Coinflect"},{"id":"coin-gabbar-token","symbol":"cgt","name":"Coin Gabbar Token"},{"id":"coinloan","symbol":"clt","name":"CoinLoan"},{"id":"coinlocally","symbol":"clyc","name":"Coinlocally"},{"id":"coinmatch-ai","symbol":"cmai","name":"CoinMatch AI"},{"id":"coinmerge-os","symbol":"cmos","name":"CoinMerge OS"},{"id":"coinmetro","symbol":"xcm","name":"Coinmetro"},{"id":"coinmooner","symbol":"mooner","name":"CoinMooner"},{"id":"coin-of-nature","symbol":"con","name":"Coin of Nature"},{"id":"coin-of-the-champions","symbol":"coc","name":"Coin of the champions"},{"id":"coinpoker","symbol":"chp","name":"CoinPoker"},{"id":"coinracer","symbol":"crace","name":"Coinracer"},{"id":"coinradr","symbol":"radr","name":"CoinRadr"},{"id":"coinsbit-token","symbol":"cnb","name":"Coinsbit Token"},{"id":"coinscan","symbol":"scan","name":"CoinScan"},{"id":"coinscope","symbol":"coinscope","name":"Coinscope"},{"id":"coinspaid","symbol":"cpd","name":"CoinsPaid"},{"id":"coinstox","symbol":"csx","name":"Coinstox"},{"id":"coinwealth","symbol":"cnw","name":"CoinWealth"},{"id":"coinweb","symbol":"cweb","name":"Coinweb"},{"id":"coinwind","symbol":"cow","name":"CoinWind"},{"id":"coinxpad","symbol":"cxpad","name":"CoinxPad"},{"id":"coinzix-token","symbol":"zix","name":"Coinzix Token"},{"id":"cola-token","symbol":"cola","name":"Cola"},{"id":"cold-finance","symbol":"cold","name":"Cold Finance"},{"id":"coldstack","symbol":"cls","name":"Coldstack"},{"id":"colizeum","symbol":"zeum","name":"Colizeum"},{"id":"collab-land","symbol":"collab","name":"Collab.Land"},{"id":"collar","symbol":"collar","name":"Collar"},{"id":"collarswap","symbol":"collar","name":"CollarSwap"},{"id":"collateral-pay","symbol":"coll","name":"Collateral Pay"},{"id":"collectcoin-2","symbol":"clct","name":"CollectCoin"},{"id":"collector-coin","symbol":"ags","name":"Collector Coin"},{"id":"collie-inu","symbol":"collie","name":"COLLIE INU"},{"id":"colony","symbol":"cly","name":"Colony"},{"id":"colony-avalanche-index","symbol":"cai","name":"Colony Avalanche Index"},{"id":"colony-network-token","symbol":"clny","name":"Colony Network"},{"id":"colossuscoinxt","symbol":"colx","name":"ColossusXT"},{"id":"colr-coin","symbol":"$colr","name":"colR Coin"},{"id":"comb-finance","symbol":"comb","name":"Comb Finance"},{"id":"comdex","symbol":"cmdx","name":"COMDEX"},{"id":"communique","symbol":"cmq","name":"Communique"},{"id":"communis","symbol":"com","name":"Communis"},{"id":"community-business-token","symbol":"cbt","name":"Community Business Token"},{"id":"community-doge-coin","symbol":"ccdoge","name":"Community Doge Coin"},{"id":"communitytoken","symbol":"ct","name":"Cojam"},{"id":"comodo-coin","symbol":"cmd","name":"Comodo Coin"},{"id":"companion","symbol":"cmpn","name":"Companion"},{"id":"compendium-fi","symbol":"cmfi","name":"Compendium"},{"id":"complifi","symbol":"comfi","name":"CompliFi"},{"id":"composite","symbol":"cmst","name":"Composite"},{"id":"compound-0x","symbol":"czrx","name":"c0x"},{"id":"compound-basic-attention-token","symbol":"cbat","name":"cBAT"},{"id":"compound-chainlink-token","symbol":"clink","name":"cLINK"},{"id":"compound-coin","symbol":"comp","name":"Compound Coin"},{"id":"compounded-marinated-umami","symbol":"cmumami","name":"Compounded Marinated UMAMI"},{"id":"compound-ether","symbol":"ceth","name":"cETH"},{"id":"compound-governance-token","symbol":"comp","name":"Compound"},{"id":"compound-maker","symbol":"cmkr","name":"cMKR"},{"id":"compound-meta","symbol":"coma","name":"Compound Meta"},{"id":"compound-sushi","symbol":"csushi","name":"cSUSHI"},{"id":"compound-uniswap","symbol":"cuni","name":"cUNI"},{"id":"compound-usd-coin","symbol":"cusdc","name":"cUSDC"},{"id":"compound-usdt","symbol":"cusdt","name":"cUSDT"},{"id":"compound-wrapped-btc","symbol":"cwbtc","name":"cWBTC"},{"id":"compound-yearn-finance","symbol":"cyfi","name":"cYFI"},{"id":"comp-yvault","symbol":"yvcomp","name":"COMP yVault"},{"id":"comsa","symbol":"cms","name":"COMSA"},{"id":"comtech-gold","symbol":"cgo","name":"Comtech Gold"},{"id":"concave","symbol":"cnv","name":"Concave"},{"id":"conceal","symbol":"ccx","name":"Conceal"},{"id":"concentrated-voting-power","symbol":"cvp","name":"PowerPool Concentrated Voting Power"},{"id":"concentrator","symbol":"ctr","name":"Concentrator"},{"id":"concertvr","symbol":"cvt","name":"concertVR"},{"id":"concierge-io","symbol":"ava","name":"Travala.com"},{"id":"concordium","symbol":"ccd","name":"Concordium"},{"id":"condorchain","symbol":"cdr","name":"CondorChain"},{"id":"conflux-token","symbol":"cfx","name":"Conflux"},{"id":"conic-finance","symbol":"cnc","name":"Conic"},{"id":"connect-financial","symbol":"cnfi","name":"Connect Financial"},{"id":"connectico","symbol":"con","name":"Connectico"},{"id":"connectome","symbol":"cntm","name":"Connectome"},{"id":"connect-token","symbol":"cnt","name":"Connect Stela"},{"id":"constellation-labs","symbol":"dag","name":"Constellation"},{"id":"constitutiondao","symbol":"people","name":"ConstitutionDAO"},{"id":"constitutiondao-wormhole","symbol":"people","name":"ConstitutionDAO (Wormhole)"},{"id":"contentbox","symbol":"box","name":"ContentBox"},{"id":"contentos","symbol":"cos","name":"Contentos"},{"id":"contents-shopper-token","symbol":"cst","name":"Contents Shopper Token"},{"id":"content-value-network","symbol":"cvnt","name":"Conscious Value Network"},{"id":"continuum-finance","symbol":"ctn","name":"Continuum Finance"},{"id":"continuum-world","symbol":"um","name":"Continuum World"},{"id":"contracoin","symbol":"ctcn","name":"Contracoin"},{"id":"contracto","symbol":"lock","name":"Contracto"},{"id":"conun","symbol":"con","name":"CONUN"},{"id":"convergence","symbol":"conv","name":"Convergence"},{"id":"converter-finance","symbol":"con","name":"Converter Finance"},{"id":"convex-crv","symbol":"cvxcrv","name":"Convex CRV"},{"id":"convex-finance","symbol":"cvx","name":"Convex Finance"},{"id":"convex-fpis","symbol":"cvxfpis","name":"Convex FPIS"},{"id":"convex-fxs","symbol":"cvxfxs","name":"Convex FXS"},{"id":"cook","symbol":"cook","name":"Cook"},{"id":"cookiesale","symbol":"cookie","name":"CookieSale"},{"id":"cookies-protocol","symbol":"cp","name":"Cookies Protocol"},{"id":"coolmining","symbol":"cooha","name":"CoolMining"},{"id":"cool-vault-nftx","symbol":"cool","name":"COOL Vault (NFTX)"},{"id":"cope","symbol":"cope","name":"Cope"},{"id":"cope-token","symbol":"cope","name":"Cope Token"},{"id":"copiosa","symbol":"cop","name":"Copiosa"},{"id":"copuppy","symbol":"cp","name":"CoPuppy"},{"id":"copycat-finance","symbol":"copycat","name":"Copycat Finance"},{"id":"coral-swap","symbol":"coral","name":"Coral Swap"},{"id":"cordium","symbol":"cord","name":"Cordium"},{"id":"core","symbol":"cmcx","name":"CORE MultiChain"},{"id":"coredao","symbol":"coredao","name":"coreDAO"},{"id":"coredaoorg","symbol":"core","name":"Core"},{"id":"coredaoswap","symbol":"cdao","name":"CoreDaoSwap"},{"id":"core-id","symbol":"cid","name":"CORE ID"},{"id":"corestarter","symbol":"cstr","name":"CoreStarter"},{"id":"coreto","symbol":"cor","name":"COR Token"},{"id":"coreum","symbol":"core","name":"Coreum"},{"id":"corgi-ceo","symbol":"corgiceo","name":"CORGI CEO"},{"id":"corgicoin","symbol":"corgi","name":"CorgiCoin"},{"id":"corgidoge","symbol":"corgi","name":"Corgidoge"},{"id":"corginftgame","symbol":"cor","name":"CorgiNFTGame"},{"id":"corionx","symbol":"corx","name":"CorionX"},{"id":"corite","symbol":"co","name":"Corite"},{"id":"coritiba-f-c-fan-token","symbol":"crtb","name":"Coritiba F.C. Fan Token"},{"id":"corn","symbol":"corn","name":"CORN"},{"id":"cornatto","symbol":"cnc","name":"Cornatto"},{"id":"corni","symbol":"corni","name":"Corni"},{"id":"cornichon","symbol":"corn","name":"Cornichon"},{"id":"cornucopias","symbol":"copi","name":"Cornucopias"},{"id":"cortex","symbol":"ctxc","name":"Cortex"},{"id":"cortexdao","symbol":"cxd","name":"CortexDAO"},{"id":"cosanta","symbol":"cosa","name":"Cosanta"},{"id":"coshi-inu","symbol":"coshi","name":"CoShi Inu"},{"id":"cosmic-champs","symbol":"cosg","name":"Cosmic Champs"},{"id":"cosmicswap","symbol":"cosmic","name":"CosmicSwap"},{"id":"cosmic-universe-magic-token","symbol":"magic","name":"Cosmic Universe Magic"},{"id":"cosmos","symbol":"atom","name":"Cosmos Hub"},{"id":"cosplay-token-2","symbol":"cot","name":"Cosplay Token"},{"id":"coti","symbol":"coti","name":"COTI"},{"id":"cotrader","symbol":"cot","name":"CoTrader"},{"id":"cougar-token","symbol":"cgs","name":"CougarSwap"},{"id":"counosx","symbol":"ccxx","name":"CounosX"},{"id":"counterparty","symbol":"xcp","name":"Counterparty"},{"id":"couponbay","symbol":"cup","name":"CouponBay"},{"id":"covalent","symbol":"cqt","name":"Covalent"},{"id":"covalent-cova","symbol":"cova","name":"Cova Unity"},{"id":"covenant-child","symbol":"covn","name":"Covenant"},{"id":"covercompared","symbol":"cvr","name":"CoverCompared"},{"id":"cover-protocol","symbol":"cover","name":"Cover Protocol"},{"id":"covesting","symbol":"cov","name":"Covesting"},{"id":"covicoin","symbol":"cvc","name":"CoviCoin"},{"id":"cowboy-snake","symbol":"cows","name":"Cowboy Snake"},{"id":"cowcoin","symbol":"cc","name":"CowCoin"},{"id":"cow-protocol","symbol":"cow","name":"CoW Protocol"},{"id":"cowrie","symbol":"cowrie","name":"Cowrie"},{"id":"cowry","symbol":"cow","name":"COWRY"},{"id":"coxswap","symbol":"cox","name":"Coxswap [OLD]"},{"id":"coxswap-2","symbol":"cox","name":"Coxswap"},{"id":"cpchain","symbol":"cpc","name":"CPChain"},{"id":"cpos-cloud-payment","symbol":"cpos","name":"CPOS Cloud Payment"},{"id":"cpucoin","symbol":"cpu","name":"CPUcoin"},{"id":"crabada","symbol":"cra","name":"Crabada"},{"id":"crab-market","symbol":"crab","name":"Crab Market"},{"id":"cracle","symbol":"cra","name":"Cracle"},{"id":"crafting-finance","symbol":"crf","name":"Crafting Finance"},{"id":"cramer-coin","symbol":"$cramer","name":"Cramer Coin"},{"id":"cranx-chain","symbol":"granx","name":"GranX Chain"},{"id":"cratos","symbol":"crts","name":"Cratos"},{"id":"crave","symbol":"crave","name":"Crave"},{"id":"crazy-bunny","symbol":"crazybunny","name":"Crazy Bunny"},{"id":"crazy-bunny-equity-token","symbol":"cbunny","name":"Crazy Bunny Equity"},{"id":"crazy-internet-coin","symbol":"cic","name":"Crazy Internet Coin"},{"id":"crazyminer","symbol":"pwr","name":"CrazyMiner"},{"id":"crazysharo","symbol":"sharo","name":"CrazySharo"},{"id":"crazy-treasure-token","symbol":"ctt","name":"Crazy Treasure Token"},{"id":"crb-coin","symbol":"crb","name":"CRB Coin"},{"id":"cre8r-dao","symbol":"cre8r","name":"CRE8R DAO"},{"id":"cream","symbol":"crm","name":"Creamcoin"},{"id":"cream-2","symbol":"cream","name":"Cream"},{"id":"creama","symbol":"creama","name":"Creama"},{"id":"cream-eth2","symbol":"creth2","name":"Cream ETH 2"},{"id":"creamlands","symbol":"cream","name":"Creamlands"},{"id":"creamy","symbol":"creamy","name":"Creamy"},{"id":"create","symbol":"ct","name":"Create"},{"id":"creaticles","symbol":"cre8","name":"Creaticles"},{"id":"creator-platform","symbol":"ctr","name":"Creator Platform"},{"id":"creature_hunters","symbol":"chts","name":"Puzzle Hunters"},{"id":"creda","symbol":"creda","name":"CreDA"},{"id":"cred-coin-pay","symbol":"cred","name":"CRED COIN PAY"},{"id":"credefi","symbol":"credi","name":"Credefi"},{"id":"credit","symbol":"credit","name":"Credit"},{"id":"credit-2","symbol":"credit","name":"PROXI DeFi"},{"id":"creditcoin-2","symbol":"ctc","name":"Creditcoin"},{"id":"credits","symbol":"cs","name":"CREDITS"},{"id":"creditum","symbol":"credit","name":"Creditum"},{"id":"creds","symbol":"creds","name":"Creds"},{"id":"creo-engine","symbol":"creo","name":"Creo Engine"},{"id":"crescent-network","symbol":"cre","name":"Crescent Network"},{"id":"cresio","symbol":"xcre","name":"Cresio"},{"id":"creta-world","symbol":"creta","name":"Creta World"},{"id":"cricket-foundation","symbol":"cric","name":"Cricket Foundation"},{"id":"cricket-star-manager","symbol":"csm","name":"Cricket Star Manager"},{"id":"crime-gold","symbol":"crime","name":"Crime Gold"},{"id":"crimson-network","symbol":"crimson","name":"Crimson Network"},{"id":"cripco","symbol":"ip3","name":"Cripco"},{"id":"criptoville-coins-2","symbol":"cvlc2","name":"CriptoVille Coins 2"},{"id":"crir-msh","symbol":"msh","name":"CRIR MSH"},{"id":"croatian-ff-fan-token","symbol":"vatreni","name":"Croatian FF Fan Token"},{"id":"crodex","symbol":"crx","name":"Crodex"},{"id":"crogecoin","symbol":"croge","name":"Crogecoin"},{"id":"croissant-games","symbol":"croissant","name":"Croissant Games"},{"id":"croking","symbol":"crk","name":"Croking"},{"id":"crolend","symbol":"crd","name":"Crolend"},{"id":"crolon-mars","symbol":"clmrs","name":"Crolon Mars"},{"id":"cronaswap","symbol":"crona","name":"CronaSwap"},{"id":"cronodes","symbol":"crn","name":"CroNodes"},{"id":"cronos-id","symbol":"croid","name":"Cronos ID"},{"id":"cronospad","symbol":"cpad","name":"Cronospad"},{"id":"cronosphere","symbol":"sphere","name":"Cronosphere"},{"id":"cronosverse","symbol":"vrse","name":"CronosVerse"},{"id":"cronus-finance","symbol":"crn","name":"Cronus Finance"},{"id":"cropbytes","symbol":"cbx","name":"CropBytes"},{"id":"cropperfinance","symbol":"crp","name":"CropperFinance"},{"id":"cross-chain-bch","symbol":"ccbch","name":"Cross-Chain BCH"},{"id":"cross-chain-bridge","symbol":"bridge","name":"Cross-Chain Bridge"},{"id":"cross-chain-farming","symbol":"ccf","name":"Cross Chain Farming"},{"id":"crosschain-iotx","symbol":"ciotx","name":"Crosschain IOTX"},{"id":"crossfi","symbol":"crfi","name":"CrossFi"},{"id":"crossswap","symbol":"cswap","name":"CrossSwap"},{"id":"crosswallet","symbol":"cwt","name":"CrossWallet"},{"id":"crossx","symbol":"crx","name":"CrossX"},{"id":"croswap","symbol":"cros","name":"CroSwap"},{"id":"crowd","symbol":"cwd","name":"CROWD"},{"id":"crowdswap","symbol":"crowd","name":"CrowdSwap"},{"id":"crown","symbol":"crw","name":"Crown"},{"id":"crowns","symbol":"cws","name":"Seascape Crowns"},{"id":"crown-sovereign","symbol":"csov","name":"Crown Sovereign"},{"id":"crownsterling","symbol":"wcsov","name":"CrownSterling"},{"id":"crown-token","symbol":"cwt","name":"Crown CWT"},{"id":"crowny-token","symbol":"crwny","name":"Crowny"},{"id":"crunchy-dao","symbol":"crdao","name":"Crunchy DAO"},{"id":"crunchy-network","symbol":"crnchy","name":"Crunchy Network"},{"id":"crusaders-of-crypto","symbol":"crusader","name":"Crusaders of Crypto"},{"id":"crust-exchange","symbol":"crust","name":"Crust Exchange"},{"id":"crust-network","symbol":"cru","name":"Crust Network"},{"id":"crust-storage-market","symbol":"csm","name":"Crust Shadow"},{"id":"cry-coin","symbol":"cryy","name":"Cry Cat Coin"},{"id":"cryn","symbol":"cryn","name":"CRYN"},{"id":"cryowar-token","symbol":"cwar","name":"Cryowar"},{"id":"cryptaur","symbol":"cpt","name":"Cryptaur"},{"id":"cryptegrity-dao","symbol":"escrow","name":"Cryptegrity Dao"},{"id":"crypterium","symbol":"crpt","name":"Crypterium"},{"id":"crypteriumcoin","symbol":"ccoin","name":"Crypteriumcoin"},{"id":"cryptex","symbol":"crx","name":"CryptEx"},{"id":"cryptex-finance","symbol":"ctx","name":"Cryptex Finance"},{"id":"cryptia","symbol":"crypt","name":"Cryptia"},{"id":"cryption-network","symbol":"cnt","name":"Cryption Network"},{"id":"cryptmi","symbol":"cymi","name":"CryptMi"},{"id":"cryptoai","symbol":"cai","name":"CryptoAI"},{"id":"crypto-arc","symbol":"arc","name":"CryptoArc"},{"id":"cryptoart-ai","symbol":"cart","name":"CryptoArt.Ai"},{"id":"cryptobank","symbol":"cbex","name":"CryptoBank"},{"id":"crypto-bank","symbol":"cbank","name":"Crypto Bank"},{"id":"cryptobill","symbol":"crb","name":"CryptoBill"},{"id":"crypto-birds","symbol":"xcb","name":"Crypto Birds"},{"id":"cryptoblades","symbol":"skill","name":"CryptoBlades"},{"id":"cryptoblades-kingdoms","symbol":"king","name":"CryptoBlades Kingdoms"},{"id":"cryptoblast","symbol":"cbt","name":"CryptoBlast"},{"id":"cryptobonusmiles","symbol":"cbm","name":"CryptoBonusMiles"},{"id":"cryptobosscoin","symbol":"cbc","name":"CryptoBossCoin"},{"id":"crypto-carbon-energy","symbol":"cyce","name":"Crypto Carbon Energy"},{"id":"cryptocart","symbol":"ccv2","name":"CryptoCart V2"},{"id":"cryptocean","symbol":"cron","name":"Cryptocean"},{"id":"cryptochrome","symbol":"chm","name":"Cryptochrome"},{"id":"crypto-classic","symbol":"crc","name":"Crypto Classic"},{"id":"cryptocoinpay","symbol":"ccp","name":"CryptoCoinPay"},{"id":"crypto-com-chain","symbol":"cro","name":"Cronos"},{"id":"cryptocurrency-market-index","symbol":"cmi","name":"Cryptocurrency Market Index"},{"id":"crypto-development-services","symbol":"cds","name":"Crypto Development Services"},{"id":"cryptodrop","symbol":"juice","name":"CryptoDrop"},{"id":"crypto-emergency","symbol":"cem","name":"Crypto Emergency"},{"id":"crypto-excellence","symbol":"ce","name":"Crypto Excellence"},{"id":"cryptoexpress","symbol":"xpress","name":"CryptoXpress"},{"id":"crypto-fantasy-coin","symbol":"cfc","name":"Crypto Fantasy Coin"},{"id":"cryptofi","symbol":"cfi","name":"Cryptofi"},{"id":"crypto-fight-club","symbol":"fight","name":"Crypto Fight Club"},{"id":"cryptoflow","symbol":"cfl","name":"Cryptoflow"},{"id":"cryptofranc","symbol":"xchf","name":"CryptoFranc"},{"id":"cryptogcoin","symbol":"crg","name":"Cryptogcoin"},{"id":"crypto-gladiator-shards","symbol":"cgl","name":"Crypto Gladiator League"},{"id":"crypto-global-united","symbol":"cgu","name":"Crypto Global United"},{"id":"cryptogpt-token","symbol":"gpt","name":"CryptoGPT Token"},{"id":"crypto-holding-frank-token","symbol":"chft","name":"Crypto Holding Frank"},{"id":"cryptoindex-io","symbol":"cix100","name":"Cryptoindex.com 100"},{"id":"crypto-international","symbol":"cri","name":"Crypto International"},{"id":"crypto-island","symbol":"cisla","name":"Crypto Island"},{"id":"cryptojetski","symbol":"cjet","name":"CryptoJetski"},{"id":"crypto-kart-racing","symbol":"ckracing","name":"Crypto Kart Racing"},{"id":"cryptokenz","symbol":"cyt","name":"Cryptokenz"},{"id":"cryptokki","symbol":"tokki","name":"CRYPTOKKI"},{"id":"crypto-klash","symbol":"klh","name":"Crypto Klash"},{"id":"crypto-kombat","symbol":"kombat","name":"Crypto Kombat"},{"id":"cryptoku","symbol":"cku","name":"Cryptoku"},{"id":"crypto-legions-v3","symbol":"blv3","name":"Crypto Legions V3"},{"id":"cryptolic","symbol":"cptlc","name":"Cryptolic"},{"id":"crypto-makers-foundation","symbol":"cmf","name":"Crypto Makers Foundation"},{"id":"cryptomeda","symbol":"tech","name":"Cryptomeda"},{"id":"cryptomines-eternal","symbol":"eternal","name":"CryptoMines Eternal"},{"id":"cryptomines-reborn","symbol":"crux","name":"CryptoMines Reborn"},{"id":"cryptomoonshots","symbol":"cms","name":"CryptoMoonShots"},{"id":"cryptoneur-network-foundation","symbol":"cnf","name":"CryptoNeur Network foundation"},{"id":"cryptonits","symbol":"crt","name":"Cryptonits"},{"id":"cryptonovae","symbol":"yae","name":"Cryptonovae"},{"id":"cryptopay","symbol":"cpay","name":"Cryptopay"},{"id":"cryptoperformance-coin","symbol":"cpc","name":"CryptoPerformance Coin"},{"id":"crypto-perx","symbol":"cprx","name":"Crypto Perx"},{"id":"crypto-pitch","symbol":"cpi","name":"Crypto Pitch"},{"id":"cryptoplanes","symbol":"cpan","name":"CryptoPlanes"},{"id":"cryptopolis","symbol":"cpo","name":"Cryptopolis"},{"id":"crypto-price-index","symbol":"cpi","name":"Crypto Price Index"},{"id":"crypto-puffs","symbol":"puffs","name":"Crypto Puffs"},{"id":"cryptopunk-7171-hoodie","symbol":"hoodie","name":"CryptoPunk #7171"},{"id":"cryptopunks-fraction-toke","symbol":"ipunks","name":"CryptoPunks Fraction Token"},{"id":"cryptopunt","symbol":"pun","name":"CryptoPunt"},{"id":"crypto-raiders","symbol":"raider","name":"Crypto Raiders"},{"id":"crypto-real-estate","symbol":"cre","name":"Crypto Real Estate"},{"id":"cryptorg-token","symbol":"ctg","name":"Cryptorg"},{"id":"crypto-royale","symbol":"roy","name":"Crypto Royale"},{"id":"cryptosaga","symbol":"saga","name":"CryptoSaga"},{"id":"cryptoshares","symbol":"shares","name":"Cryptoshares"},{"id":"crypto-shield","symbol":"shield","name":"Crypto Shield"},{"id":"cryptoships","symbol":"cship","name":"CryptoShips"},{"id":"cryptoskates","symbol":"cst","name":"CryptoSkates"},{"id":"crypto-snack","symbol":"snack","name":"Crypto Snack"},{"id":"crypto-soccer","symbol":"csc","name":"Crypto Soccer"},{"id":"cryptosroom","symbol":"croom","name":"Cryptosroom"},{"id":"cryptostone","symbol":"cps","name":"Cryptostone"},{"id":"cryptostribe","symbol":"cstc","name":"CryptosTribe"},{"id":"cryptotanks","symbol":"tank","name":"CryptoTanks"},{"id":"cryptotask-2","symbol":"ctask","name":"CryptoTask"},{"id":"cryptotem","symbol":"totem","name":"Cryptotem"},{"id":"crypto-tex","symbol":"ctex","name":"CRYPTO TEX"},{"id":"cryptotycoon","symbol":"ctt","name":"CryptoTycoon"},{"id":"cryptounit","symbol":"cru","name":"Cryptounit"},{"id":"crypto-vault","symbol":"cvt","name":"Crypto Vault"},{"id":"crypto-village-accelerator","symbol":"cva","name":"Crypto Village Accelerator"},{"id":"crypto-village-accelerator-cvag","symbol":"cvag","name":"Crypto Village Accelerator CVAG"},{"id":"crypto-volatility-token","symbol":"cvi","name":"Crypto Volatility"},{"id":"cryptowar-xblade","symbol":"open","name":"OpenWorld"},{"id":"crypto-warz","symbol":"warz","name":"Crypto Warz"},{"id":"crypto-wrestling-inu","symbol":"$cwi","name":"Crypto Wrestling Inu"},{"id":"cryptozerofi","symbol":"zeri_v2","name":"CryptoZerofi V2"},{"id":"cryptozoo","symbol":"zoo","name":"CryptoZoo"},{"id":"cryptozoon","symbol":"zoon","name":"CryptoZoon"},{"id":"cryptyk","symbol":"ctk","name":"Cryptyk"},{"id":"crypworld","symbol":"cwc","name":"CrypWorld"},{"id":"crystal","symbol":"crystal","name":"Crystal"},{"id":"crystal-clear","symbol":"cct","name":"Crystal Clear"},{"id":"crystal-dust","symbol":"csd","name":"Crystal Dust"},{"id":"crystal-palace-fan-token","symbol":"cpfc","name":"Crystal Palace FC Fan Token"},{"id":"crystal-token","symbol":"cyl","name":"Crystal CYL"},{"id":"crystl-finance","symbol":"crystl","name":"Crystl Finance"},{"id":"csp-dao-network","symbol":"nebo","name":"CSP DAO Network"},{"id":"csr","symbol":"csr","name":"CSR"},{"id":"ctez","symbol":"ctez","name":"Ctez"},{"id":"cthulhu-finance","symbol":"cth","name":"Cthulhu Finance"},{"id":"ctomorrow-platform","symbol":"ctp","name":"Ctomorrow Platform"},{"id":"ctrl-x","symbol":"cut","name":"Ctrl-X"},{"id":"cube","symbol":"itamcube","name":"CUBE"},{"id":"cube-intelligence","symbol":"auto","name":"Cube Intelligence"},{"id":"cub-finance","symbol":"cub","name":"Cub Finance"},{"id":"cubiex-power","symbol":"cbix-p","name":"Cubiex Power"},{"id":"cubix","symbol":"cubix","name":"CUBIX"},{"id":"cubtoken","symbol":"cubt","name":"CubToken"},{"id":"cudos","symbol":"cudos","name":"Cudos"},{"id":"cue-protocol","symbol":"cue","name":"CUE Protocol"},{"id":"cuex","symbol":"ccap","name":"CUEX Capital"},{"id":"cult-dao","symbol":"cult","name":"Cult DAO"},{"id":"cultiplan","symbol":"ctpl","name":"Cultiplan"},{"id":"cuminu","symbol":"cuminu","name":"Cuminu"},{"id":"cumrocket","symbol":"cummies","name":"CumRocket"},{"id":"cuprum-coin","symbol":"cuc","name":"Cuprum Coin"},{"id":"curate","symbol":"xcur","name":"Curate"},{"id":"curecoin","symbol":"cure","name":"Curecoin"},{"id":"cure-token-v2","symbol":"cure","name":"CURE V2"},{"id":"curio-governance","symbol":"cgt","name":"Curio Governance"},{"id":"curve-dao-token","symbol":"crv","name":"Curve DAO"},{"id":"curve-fi-amdai-amusdc-amusdt","symbol":"am3crv","name":"Curve.fi amDAI/amUSDC/amUSDT"},{"id":"curve-fi-dai-usdc","symbol":"dai+usdc","name":"Curve.fi DAI/USDC"},{"id":"curve-fi-frax-usdc","symbol":"crvfrax","name":"Curve.fi FRAX/USDC"},{"id":"curve-fi-gdai-gusdc-gusdt","symbol":"g3crv","name":"Curve.fi gDAI/gUSDC/gUSDT"},{"id":"curve-fi-renbtc-wbtc-sbtc","symbol":"crvrenwsbtc","name":"Curve.fi renBTC/wBTC/sBTC"},{"id":"curve-fi-usdc-usdt","symbol":"2crv","name":"Curve.fi USDC/USDT"},{"id":"curve-fi-ydai-yusdc-yusdt-ytusd","symbol":"ycurve","name":"LP-yCurve"},{"id":"custodiy","symbol":"cty","name":"CUSTODIY"},{"id":"cvault-finance","symbol":"core","name":"cVault.finance"},{"id":"cvnx","symbol":"cvnx","name":"CVNX"},{"id":"cvshots","symbol":"cvshot","name":"CVSHOTS"},{"id":"cyber-city","symbol":"cybr","name":"Cyber City"},{"id":"cyber-crystal","symbol":"crystal","name":"Cyber Crystal"},{"id":"cyberdoge","symbol":"cybrrrdoge","name":"CyberDoge"},{"id":"cyberdragon-gold","symbol":"gold","name":"CyberDragon Gold"},{"id":"cyberfi","symbol":"cfi","name":"CyberFi"},{"id":"cyberfm","symbol":"cyfm","name":"CyberFM"},{"id":"cyberharbor","symbol":"cht","name":"CyberHarbor"},{"id":"cybermiles","symbol":"cmt","name":"CyberMiles"},{"id":"cyberpop-metaverse","symbol":"cyt","name":"Cyberpop Metaverse"},{"id":"cyberpunk-city","symbol":"cyber","name":"Cyberpunk City"},{"id":"cyber-soccer","symbol":"coca","name":"CYBER SOCCER"},{"id":"cybertronchain","symbol":"ctc","name":"CyberTronchain"},{"id":"cybervein","symbol":"cvt","name":"CyberVein"},{"id":"cyberyen","symbol":"cy","name":"Cyberyen"},{"id":"cybloc-battery-token","symbol":"cbt","name":"CyBall CyBloc Battery"},{"id":"cyborg-apes","symbol":"borg","name":"Cyborg Apes"},{"id":"cycle-token","symbol":"cycle","name":"Cycle"},{"id":"cyc-lock","symbol":"cyc","name":"CYC'Lock"},{"id":"cyclone-protocol","symbol":"cyc","name":"Cyclone Protocol"},{"id":"cyclos","symbol":"cys","name":"Cykura"},{"id":"cydotori","symbol":"dotr","name":"Cydotori"},{"id":"cylum-finance","symbol":"cym","name":"Cylum Finance"},{"id":"cyop","symbol":"cyop","name":"CyOp"},{"id":"cyop-protocol","symbol":"cyop","name":"CyOp Protocol"},{"id":"cypherdog-token","symbol":"cdog","name":"Cypherdog Token"},{"id":"cypherium","symbol":"cph","name":"Cypherium"},{"id":"cyptobit-network","symbol":"cbi","name":"Cyptobit Network"},{"id":"czbnb","symbol":"czbnb","name":"CZbnb"},{"id":"czred","symbol":"czr","name":"CZRed"},{"id":"czusd","symbol":"czusd","name":"CZUSD"},{"id":"d3","symbol":"defi","name":"D3"},{"id":"d3d-social","symbol":"d3d","name":"D3D Social"},{"id":"dabb-doge","symbol":"ddoge","name":"Dabb Doge"},{"id":"dab-coin","symbol":"dab","name":"DAB Coin"},{"id":"dachshund","symbol":"dsd","name":"Dachshund"},{"id":"dacxi","symbol":"dacxi","name":"Dacxi"},{"id":"daddy-doge","symbol":"daddydoge","name":"Daddy Doge"},{"id":"daefrom","symbol":"dae","name":"Daefrom"},{"id":"daex","symbol":"dax","name":"DAEX"},{"id":"dafin","symbol":"daf","name":"DaFIN"},{"id":"dafi-protocol","symbol":"dafi","name":"Dafi Protocol"},{"id":"dagger","symbol":"xdag","name":"Dagger"},{"id":"dai","symbol":"dai","name":"Dai"},{"id":"daikicoin","symbol":"dic","name":"Daikicoin"},{"id":"daikokuten-sama","symbol":"dkks","name":"Daikokuten Sama"},{"id":"dain-token","symbol":"dain","name":"Dain"},{"id":"daisy","symbol":"daisy","name":"Daisy Protocol"},{"id":"dalecoin","symbol":"dalc","name":"Dalecoin"},{"id":"dali","symbol":"dali","name":"DALI"},{"id":"dama","symbol":"dama","name":"DAMA"},{"id":"dam-finance","symbol":"d2o","name":"Deuterium"},{"id":"damm","symbol":"damm","name":"dAMM"},{"id":"danat-coin","symbol":"dnc","name":"Danat Coin"},{"id":"dangermoon","symbol":"dangermoon","name":"DangerMoon"},{"id":"danketsu","symbol":"ninjaz","name":"Danketsu"},{"id":"dao-farmer-dfg","symbol":"dfg","name":"DAO Farmer DFG"},{"id":"daohaus","symbol":"haus","name":"DAOhaus"},{"id":"dao-invest","symbol":"vest","name":"DAO Invest"},{"id":"daoland","symbol":"dld","name":"Daoland"},{"id":"daolaunch","symbol":"dal","name":"DAOLaunch"},{"id":"dao-maker","symbol":"dao","name":"DAO Maker"},{"id":"daosol","symbol":"daosol","name":"daoSOL"},{"id":"daosquare","symbol":"rice","name":"DAOSquare"},{"id":"daostack","symbol":"gen","name":"DAOstack"},{"id":"daovc","symbol":"daovc","name":"DAOvc"},{"id":"daoventures","symbol":"dvd","name":"DAOventures"},{"id":"daoverse","symbol":"dvrs","name":"DaoVerse"},{"id":"dapp","symbol":"dapp","name":"LiquidApps"},{"id":"dapp-com","symbol":"dappt","name":"Dapp.com"},{"id":"dappnode","symbol":"node","name":"DAppNode"},{"id":"dappradar","symbol":"radar","name":"DappRadar"},{"id":"dappstore","symbol":"dappx","name":"dAppstore"},{"id":"dappsy","symbol":"app","name":"Dappsy"},{"id":"darcmatter-coin","symbol":"darc","name":"Konstellation"},{"id":"dar-dex-token","symbol":"dut","name":"Dar Dex"},{"id":"darenft","symbol":"dnft","name":"DareNFT"},{"id":"darkcrypto","symbol":"dark","name":"DarkCrypto"},{"id":"darkcrypto-share","symbol":"sky","name":"DarkCrypto Share"},{"id":"darkcrystl","symbol":"darkcrystl","name":"DarkCrystl"},{"id":"dark-energy-crystals","symbol":"dec","name":"Dark Energy Crystals"},{"id":"dark-frontiers","symbol":"dark","name":"Dark Frontiers"},{"id":"darkgang-finance","symbol":"darkg","name":"DarkGang Finance"},{"id":"darkknight","symbol":"dknight","name":"Dark Knight"},{"id":"dark-land-survival","symbol":"big","name":"Dark Land Survival"},{"id":"dark-magic","symbol":"dmagic","name":"Dark Magic"},{"id":"darkmatter","symbol":"dmt","name":"DarkMatter"},{"id":"dark-matter","symbol":"dmt","name":"Dark Matter"},{"id":"dark-matter-defi","symbol":"dmd","name":"Dark Matter Defi"},{"id":"darkness-dollar","symbol":"dusd","name":"Darkness Dollar"},{"id":"darkness-share","symbol":"ness","name":"Darkness Share"},{"id":"darkshield","symbol":"dks","name":"DarkShield"},{"id":"darleygo-essence","symbol":"dge","name":"DarleyGo Essence"},{"id":"daruma","symbol":"daruma","name":"Daruma"},{"id":"darussafaka-sports-club","symbol":"dsk","name":"Darüşşafaka Sports Club"},{"id":"darwinia-commitment-token","symbol":"kton","name":"Darwinia Commitment"},{"id":"darwinia-network-native-token","symbol":"ring","name":"Darwinia Network"},{"id":"dascoin","symbol":"grn","name":"GreenPower"},{"id":"dash","symbol":"dash","name":"Dash"},{"id":"dash-2-trade","symbol":"d2t","name":"Dash 2 Trade"},{"id":"dash-diamond","symbol":"dashd","name":"Dash Diamond"},{"id":"dashsports","symbol":"dass","name":"DashSports"},{"id":"data","symbol":"dta","name":"DATA"},{"id":"databroker-dao","symbol":"dtx","name":"DaTa eXchange DTX"},{"id":"datachain-foundation","symbol":"dc","name":"DATACHAIN FOUNDATION"},{"id":"data-economy-index","symbol":"data","name":"Data Economy Index"},{"id":"datahighway","symbol":"dhx","name":"DataHighway"},{"id":"datakyc","symbol":"dkyc","name":"DataKYC"},{"id":"data-lake","symbol":"lake","name":"Data Lake"},{"id":"datamine","symbol":"dam","name":"Datamine"},{"id":"datx","symbol":"datx","name":"DATx"},{"id":"dav","symbol":"dav","name":"DAV Network"},{"id":"davidcoin","symbol":"dc","name":"DavidCoin"},{"id":"davis-cup-fan-token","symbol":"davis","name":"Davis Cup Fan Token"},{"id":"davos-protocol","symbol":"davos","name":"Davos"},{"id":"dawg","symbol":"dawg","name":"DAWG"},{"id":"dawin-token","symbol":"dwt","name":"DaWin Token"},{"id":"dawn-protocol","symbol":"dawn","name":"Dawn Protocol"},{"id":"dawn-star-share","symbol":"solar","name":"Dawn Star Share"},{"id":"dawn-star-token","symbol":"dsf","name":"Dawn Star Token"},{"id":"day-by-day","symbol":"dbd","name":"Day By Day"},{"id":"daylight-protocol","symbol":"dayl","name":"Daylight Protocol"},{"id":"day-of-defeat","symbol":"dod","name":"Day of Defeat 2.0"},{"id":"daystarter","symbol":"dst","name":"DAYSTARTER"},{"id":"dbx-2","symbol":"dbx","name":"DBX"},{"id":"dchess-king","symbol":"king","name":"DChess King"},{"id":"dcoin-token","symbol":"dt","name":"Dcoin"},{"id":"d-community","symbol":"dili","name":"D Community"},{"id":"dcomy","symbol":"dco","name":"DCOMY"},{"id":"dcoreum","symbol":"dco","name":"DCOREUM"},{"id":"d-drops","symbol":"dop","name":"D-Drops"},{"id":"dead-knight","symbol":"dkm","name":"Dead Knight"},{"id":"deadpxlz","symbol":"ding","name":"DEADPXLZ"},{"id":"deapcoin","symbol":"dep","name":"DEAPCOIN"},{"id":"deathroad","symbol":"drace","name":"DeathRoad"},{"id":"death-token","symbol":"death","name":"Death"},{"id":"deathwolf","symbol":"dth","name":"DeathWolf"},{"id":"deblox","symbol":"dgs","name":"Deblox"},{"id":"decanect","symbol":"dcnt","name":"Decanect"},{"id":"decaswap","symbol":"deca","name":"DecaSwap"},{"id":"decaswap-corn","symbol":"corn","name":"DecaSwap CORN"},{"id":"decentbet","symbol":"dbet","name":"DecentBet"},{"id":"decent-database","symbol":"decent","name":"DECENT Database"},{"id":"decentr","symbol":"dec","name":"Decentr"},{"id":"decentrabnb","symbol":"dbnb","name":"DecentraBNB"},{"id":"decentra-box","symbol":"dbox","name":"Decentra Box"},{"id":"decentraland","symbol":"mana","name":"Decentraland"},{"id":"decentraland-wormhole","symbol":"mana","name":"Decentraland (Wormhole)"},{"id":"decentral-art","symbol":"art","name":"Decentral ART"},{"id":"decentral-games","symbol":"dg","name":"Decentral Games"},{"id":"decentral-games-governance","symbol":"xdg","name":"Decentral Games Governance"},{"id":"decentral-games-ice","symbol":"ice","name":"Decentral Games ICE"},{"id":"decentral-games-old","symbol":"dg","name":"Decentral Games (Old)"},{"id":"decentralized-activism","symbol":"dact","name":"Decentralized Activism"},{"id":"decentralized-advertising","symbol":"dad","name":"DAD"},{"id":"decentralized-autonomous-organization","symbol":"dao","name":"Decentralized Autonomous Organization"},{"id":"decentralized-community-investment-protocol","symbol":"dcip","name":"Decentralized Community Investment Protocol"},{"id":"decentralized-liquidity-program","symbol":"dlp","name":"Decentralized Liquidity Program"},{"id":"decentralized-mining-exchange","symbol":"dmc","name":"Decentralized Mining Exchange"},{"id":"decentralized-nations","symbol":"dena","name":"Decentralized Nations"},{"id":"decentralized-pirates","symbol":"depi","name":"Decentralized Pirates"},{"id":"decentralized-united","symbol":"dcu","name":"Decentralized United"},{"id":"decentralized-universal-basic-income","symbol":"dubi","name":"Decentralized Universal Basic Income"},{"id":"decentralized-usd","symbol":"dusd","name":"Decentralized USD"},{"id":"decentralized-vulnerability-platform","symbol":"dvp","name":"Decentralized Vulnerability Platform"},{"id":"decentraweb","symbol":"dweb","name":"DecentraWeb"},{"id":"decentrawood","symbol":"deod","name":"Decentrawood"},{"id":"decimal","symbol":"del","name":"Decimal"},{"id":"decimated","symbol":"dio","name":"Decimated"},{"id":"decode-coin","symbol":"decode","name":"Decode Coin"},{"id":"decred","symbol":"dcr","name":"Decred"},{"id":"decredit","symbol":"cdtc","name":"DeCredit"},{"id":"decred-next","symbol":"dcrn","name":"Decred-Next"},{"id":"decubate","symbol":"dcb","name":"Decubate"},{"id":"decurian","symbol":"ecu","name":"Decurian"},{"id":"deep-blue-sea","symbol":"dbea","name":"Deep Blue Sea"},{"id":"deepbrain-chain","symbol":"dbc","name":"DeepBrain Chain"},{"id":"deeper-network","symbol":"dpr","name":"Deeper Network"},{"id":"deeponion","symbol":"onion","name":"DeepOnion"},{"id":"deepspace","symbol":"dps","name":"DEEPSPACE"},{"id":"deepwaters","symbol":"wtr","name":"Deepwaters"},{"id":"deesse","symbol":"love","name":"Deesse"},{"id":"deez-nuts","symbol":"deeznuts","name":"Deez Nuts"},{"id":"defactor","symbol":"factr","name":"Defactor"},{"id":"defhold","symbol":"defo","name":"DefHold"},{"id":"defi-04ab07ad-43a9-4d63-a379-2c6a2499f748","symbol":"dfx","name":"DeFi²"},{"id":"defi11","symbol":"d11","name":"DeFi11"},{"id":"defiai","symbol":"dfai","name":"DeFiAI"},{"id":"defiato","symbol":"dfiat","name":"DeFiato"},{"id":"defibay","symbol":"dbay","name":"DefiBay"},{"id":"defi-bids","symbol":"bid","name":"DeFi Bids"},{"id":"defi-bomb","symbol":"dbomb","name":"Defi Bomb"},{"id":"defibox","symbol":"box","name":"DefiBox"},{"id":"defi-ch","symbol":"dfch","name":"DeFi.ch"},{"id":"defichain","symbol":"dfi","name":"DeFiChain"},{"id":"deficliq","symbol":"cliq","name":"DefiCliq"},{"id":"defi-coin","symbol":"defc","name":"DeFi Coin"},{"id":"deficonnect","symbol":"dfc","name":"DefiConnect V1"},{"id":"deficonnect-v2","symbol":"dfc","name":"DefiConnect V2"},{"id":"defi-degen-land","symbol":"ddl","name":"DeFi Degen Land"},{"id":"defido","symbol":"defido","name":"DeFido"},{"id":"defidollar-dao","symbol":"dfd","name":"DefiDollar DAO"},{"id":"defi-forge","symbol":"forge","name":"DeFi Forge"},{"id":"defi-for-you","symbol":"dfy","name":"Defi For You"},{"id":"defi-franc","symbol":"dchf","name":"DeFi Franc"},{"id":"defi-franc-moneta","symbol":"mon","name":"Moneta DAO"},{"id":"defi-gold","symbol":"dfgl","name":"DeFi Gold"},{"id":"defigram","symbol":"dfg","name":"Defigram"},{"id":"defihorse","symbol":"dfh","name":"DeFiHorse"},{"id":"defi-hunters-dao","symbol":"ddao","name":"DDAO Hunters"},{"id":"defi-kingdoms","symbol":"jewel","name":"DeFi Kingdoms"},{"id":"defi-kingdoms-crystal","symbol":"crystal","name":"DeFi Kingdoms Crystal"},{"id":"defil","symbol":"dfl","name":"DeFIL"},{"id":"defilancer","symbol":"defilancer","name":"Defilancer"},{"id":"defi-land","symbol":"dfl","name":"DeFi Land"},{"id":"defi-land-gold","symbol":"goldy","name":"DeFi Land Gold"},{"id":"defily","symbol":"dfl","name":"Defily"},{"id":"defina-finance","symbol":"fina","name":"Defina Finance"},{"id":"define","symbol":"dfa","name":"DeFine"},{"id":"definer","symbol":"fin","name":"DeFiner"},{"id":"definet","symbol":"net","name":"Definet"},{"id":"definity","symbol":"defx","name":"DeFinity"},{"id":"defi-or-die","symbol":"dord","name":"DeFi Or Die"},{"id":"defipie","symbol":"pie","name":"DeFiPie"},{"id":"defiplaza","symbol":"dfp2","name":"DefiPlaza"},{"id":"defipulse-index","symbol":"dpi","name":"DeFi Pulse Index"},{"id":"defire","symbol":"cwap","name":"DeFIRE"},{"id":"defi-shopping-stake","symbol":"dss","name":"Defi Shopping Stake"},{"id":"defiskeletons","symbol":"skeleton","name":"Defiskeletons"},{"id":"defis-network","symbol":"dfs","name":"Defis Network"},{"id":"defistarter","symbol":"dfi","name":"DfiStarter"},{"id":"defi-stoa","symbol":"sta","name":"STOA Network"},{"id":"defit","symbol":"defit","name":"Digital Fitness"},{"id":"defitankland","symbol":"dftl","name":"DefiTankLand"},{"id":"defi-tiger","symbol":"dtg","name":"Defi Tiger"},{"id":"defiville-island","symbol":"isla","name":"DefiVille Island"},{"id":"defiwall","symbol":"fiwa","name":"DeFiWall"},{"id":"defi-warrior","symbol":"fiwa","name":"Defi Warrior"},{"id":"defi-yield-protocol","symbol":"dyp","name":"Dypius"},{"id":"defly","symbol":"defly","name":"Defly"},{"id":"deflyball","symbol":"defly","name":"Deflyball"},{"id":"defrost-finance","symbol":"melt","name":"Defrost Finance"},{"id":"defun-gaming","symbol":"defun","name":"Defun Gaming"},{"id":"defy","symbol":"defy","name":"DEFY"},{"id":"degate","symbol":"dg","name":"DeGate"},{"id":"degen","symbol":"degn","name":"Degen"},{"id":"degenerator","symbol":"meme","name":"Meme"},{"id":"degen-index","symbol":"degen","name":"DEGEN Index"},{"id":"degenreborn","symbol":"degen","name":"DegenReborn"},{"id":"degenvc","symbol":"dgvc","name":"DegenVC"},{"id":"degenx","symbol":"dgnx","name":"DegenX"},{"id":"degen-zoo","symbol":"dzoo","name":"Degen Zoo"},{"id":"degis","symbol":"deg","name":"Degis"},{"id":"dego-finance","symbol":"dego","name":"Dego Finance"},{"id":"degrain","symbol":"dgrn","name":"Degrain"},{"id":"degree-crypto-token","symbol":"dct","name":"Degree Crypto"},{"id":"dehealth","symbol":"dhlt","name":"DeHealth"},{"id":"dehero-community-token","symbol":"heroes","name":"Dehero Community"},{"id":"deherogame-amazing-token","symbol":"amg","name":"DeHeroGame Amazing Token"},{"id":"dehive","symbol":"dhv","name":"DeHive"},{"id":"dehorizon","symbol":"devt","name":"DeHorizon"},{"id":"dehr-network","symbol":"dhr","name":"DeHR Network"},{"id":"dehub","symbol":"dhb","name":"DeHub"},{"id":"dei-token","symbol":"dei","name":"DEI"},{"id":"dejitaru-kaida","symbol":"kaida","name":"Dejitaru Kaida"},{"id":"dejitaru-shirudo","symbol":"shield","name":"Dejitaru Shirudo"},{"id":"dejitaru-tsuka","symbol":"tsuka","name":"Dejitaru Tsuka"},{"id":"dekbox","symbol":"dek","name":"DekBox"},{"id":"delio-dsp","symbol":"dsp","name":"Delio DSP"},{"id":"delion","symbol":"dln","name":"Delion"},{"id":"deliq","symbol":"dlq","name":"Deliq"},{"id":"delot-io","symbol":"delot","name":"DELOT.IO"},{"id":"delphy","symbol":"dpy","name":"Delphy"},{"id":"delrey-inu","symbol":"delrey","name":"Delrey Inu"},{"id":"delta-exchange-token","symbol":"deto","name":"Delta Exchange"},{"id":"deltafi","symbol":"delfi","name":"DeltaFi"},{"id":"delta-financial","symbol":"delta","name":"Delta Financial"},{"id":"deltaflare","symbol":"honr","name":"DeltaFlare"},{"id":"deltahub-community","symbol":"dhc","name":"DeltaHub Community"},{"id":"delta-theta","symbol":"dlta","name":"delta.theta"},{"id":"delysium","symbol":"agi","name":"Delysium"},{"id":"demeter","symbol":"deo","name":"Demeter"},{"id":"demeter-usd","symbol":"dusd","name":"Demeter USD"},{"id":"demodyfi","symbol":"dmod","name":"Demodyfi"},{"id":"demole","symbol":"dmlg","name":"Demole"},{"id":"demx","symbol":"demx","name":"DemX"},{"id":"denarius","symbol":"d","name":"Denarius"},{"id":"dendomains","symbol":"ddn","name":"Den Domains"},{"id":"denizlispor-fan-token","symbol":"dnz","name":"Denizlispor Fan Token"},{"id":"dent","symbol":"dent","name":"Dent"},{"id":"dentacoin","symbol":"dcn","name":"Dentacoin"},{"id":"depay","symbol":"depay","name":"DePay"},{"id":"deploying-more-capital","symbol":"dmc","name":"Deploying More Capital"},{"id":"depocket","symbol":"depo","name":"DePocket"},{"id":"deportivo-alaves-fan-token","symbol":"daft","name":"Deportivo Alavés Fan Token"},{"id":"dequant","symbol":"deq","name":"Dequant"},{"id":"derace","symbol":"derc","name":"DeRace"},{"id":"deracoin","symbol":"drc","name":"Deracoin"},{"id":"derify-protocol","symbol":"drf","name":"Derify Protocol"},{"id":"deri-protocol","symbol":"deri","name":"Deri Protocol"},{"id":"derivadao","symbol":"ddx","name":"DerivaDAO"},{"id":"derived","symbol":"dvdx","name":"Derived"},{"id":"dero","symbol":"dero","name":"Dero"},{"id":"desmos","symbol":"dsm","name":"Desmos"},{"id":"deso","symbol":"deso","name":"Decentralized Social"},{"id":"despace-protocol","symbol":"des","name":"DeSpace Protocol"},{"id":"destorage","symbol":"ds","name":"DeStorage"},{"id":"deusdc","symbol":"deusdc","name":"deUSDC"},{"id":"deus-finance-2","symbol":"deus","name":"DEUS Finance"},{"id":"deutsche-emark","symbol":"dem","name":"Deutsche eMark"},{"id":"deuxpad","symbol":"deux","name":"DeuxPad"},{"id":"devault","symbol":"dvt","name":"DeVault"},{"id":"developer-dao","symbol":"code","name":"Developer DAO"},{"id":"devikins","symbol":"dvk","name":"Devikins"},{"id":"devil-finance","symbol":"devil","name":"Devil Finance"},{"id":"devita-global","symbol":"life","name":"DEVITA"},{"id":"devolution","symbol":"devo","name":"DeVolution"},{"id":"devour-2","symbol":"dpay","name":"Devour"},{"id":"dev-protocol","symbol":"dev","name":"Dev Protocol"},{"id":"devvio","symbol":"devve","name":"Devvio"},{"id":"dexa-coin","symbol":"dexa","name":"DEXA COIN"},{"id":"dexalot","symbol":"alot","name":"Dexalot"},{"id":"dexbrowser","symbol":"bro","name":"DexBrowser"},{"id":"dexe","symbol":"dexe","name":"DeXe"},{"id":"dexfin","symbol":"dxf","name":"Dexfin"},{"id":"dexfolio","symbol":"dexf","name":"Dexfolio"},{"id":"dex-game","symbol":"dxgm","name":"DexGame"},{"id":"dexioprotocol-v1","symbol":"dexi","name":"Dexioprotocol V1"},{"id":"dexioprotocol-v2","symbol":"dexi","name":"Dexioprotocol"},{"id":"dexira","symbol":"dex","name":"dexIRA"},{"id":"dexit-finance","symbol":"dxt","name":"Dexit Network"},{"id":"dexkit","symbol":"kit","name":"DexKit"},{"id":"dexlab","symbol":"dxl","name":"Dexlab"},{"id":"dexo","symbol":"dexo","name":"DEXO"},{"id":"dexpad","symbol":"dxp","name":"DexPad"},{"id":"dexpools","symbol":"dxp","name":"Vela Exchange"},{"id":"dexshare","symbol":"dexshare","name":"dexSHARE"},{"id":"dexsport","symbol":"desu","name":"Dexsport"},{"id":"dextf","symbol":"dextf","name":"Domani Protocol"},{"id":"dextools","symbol":"dext","name":"DexTools"},{"id":"dex-trade-coin","symbol":"dxc","name":"Dex-Trade Coin"},{"id":"dextro","symbol":"dxo","name":"Dextro"},{"id":"dexwallet","symbol":"dwt","name":"DexWallet"},{"id":"dfe-finance","symbol":"dfe","name":"DFE.Finance"},{"id":"dfohub","symbol":"buidl","name":"dfohub"},{"id":"dforce-token","symbol":"df","name":"dForce"},{"id":"dfs-mafia","symbol":"dfsm","name":"DFS Mafia V2"},{"id":"dfund","symbol":"dfnd","name":"dFund"},{"id":"dfx-finance","symbol":"dfx","name":"DFX Finance"},{"id":"dfyn-network","symbol":"dfyn","name":"Dfyn Network"},{"id":"dgpayment","symbol":"dgp","name":"DGPayment"},{"id":"dhabicoin","symbol":"dbc","name":"Dhabicoin"},{"id":"dhealth","symbol":"dhp","name":"dHealth"},{"id":"dhedge-dao","symbol":"dht","name":"dHEDGE DAO"},{"id":"dia-data","symbol":"dia","name":"DIA"},{"id":"diamond","symbol":"dmd","name":"Diamond"},{"id":"diamond-boyz-coin","symbol":"dbz","name":"Diamond Boyz Coin"},{"id":"diamond-coin","symbol":"diamond","name":"Diamond Coin"},{"id":"diamond-launch","symbol":"dlc","name":"Diamond Launch"},{"id":"diamond-love","symbol":"love","name":"Diamond Love"},{"id":"diamond-xrpl","symbol":"diamond","name":"Diamond XRPL"},{"id":"dibs-money","symbol":"dibs","name":"Dibs Money"},{"id":"dibs-share","symbol":"dshare","name":"Dibs Share"},{"id":"dice-kingdom","symbol":"dk","name":"Dice Kingdom"},{"id":"die-protocol","symbol":"die","name":"Die Protocol"},{"id":"diffusion","symbol":"diff","name":"Diffusion"},{"id":"dify-finance","symbol":"yfiii","name":"Dify.Finance"},{"id":"dig-chain","symbol":"dig","name":"Dig Chain"},{"id":"digg","symbol":"digg","name":"DIGG"},{"id":"digible","symbol":"digi","name":"Digible"},{"id":"digibyte","symbol":"dgb","name":"DigiByte"},{"id":"digichain","symbol":"digichain","name":"Digichain Coin"},{"id":"digicol-token","symbol":"dgcl","name":"DigiCol"},{"id":"digidinar-token","symbol":"ddrt","name":"DigiDinar Token"},{"id":"digifinextoken","symbol":"dft","name":"DigiFinex"},{"id":"digihealth","symbol":"dgh","name":"Digihealth"},{"id":"digimetaverse","symbol":"dgmv","name":"DigiMetaverse"},{"id":"digimon-rabbit","symbol":"drb","name":"Digimon Rabbit"},{"id":"digiswap","symbol":"digis","name":"DigiSwap"},{"id":"digital-bank-of-africa","symbol":"dba","name":"Digital Bank of Africa"},{"id":"digitalbits","symbol":"xdb","name":"DigitalBits"},{"id":"digitalcoin","symbol":"dgc","name":"Digitalcoin"},{"id":"digitaldollar","symbol":"dusd","name":"DigitalDollar"},{"id":"digital-files","symbol":"difi","name":"Digital Files"},{"id":"digital-financial-exchange","symbol":"difx","name":"Digital Financial Exchange"},{"id":"digitalnote","symbol":"xdn","name":"DigitalNote"},{"id":"digital-rand","symbol":"dzar","name":"Digital Rand"},{"id":"digital-reserve-currency","symbol":"drc","name":"Digital Reserve Currency"},{"id":"digital-standard-unit","symbol":"dsu","name":"Digital Standard Unit"},{"id":"digital-swis-franc","symbol":"dsfr","name":"Digital Swiss Franc"},{"id":"digital-ticks","symbol":"dtx","name":"Digital Ticks"},{"id":"digital-trip-advisor","symbol":"dta","name":"Digital Trip Advisor"},{"id":"digitex-futures-exchange","symbol":"dgtx","name":"Digitex"},{"id":"digits-dao","symbol":"digits","name":"Digits DAO"},{"id":"digixdao","symbol":"dgd","name":"DigixDAO"},{"id":"digix-gold","symbol":"dgx","name":"Digix Gold"},{"id":"dignity-gold","symbol":"digau","name":"Dignity Gold"},{"id":"dike","symbol":"dike","name":"Dike"},{"id":"diminutive-coin","symbol":"dimi","name":"Diminutive Coin"},{"id":"dimitra","symbol":"dmtr","name":"Dimitra"},{"id":"dimo","symbol":"dimo","name":"DIMO"},{"id":"dina","symbol":"dina","name":"Dina"},{"id":"dinamo-zagreb-fan-token","symbol":"dzg","name":"Dinamo Zagreb Fan Token"},{"id":"dinastycoin","symbol":"dcy","name":"Dinastycoin"},{"id":"dinero","symbol":"din","name":"Dinero"},{"id":"dinerobet","symbol":"dinero","name":"Dinerobet"},{"id":"dinger-token","symbol":"dinger","name":"Dinger"},{"id":"dingocoin","symbol":"dingo","name":"Dingocoin"},{"id":"dingo-token","symbol":"dingo","name":"Dingo"},{"id":"dink-donk","symbol":"dink","name":"Dink Doink"},{"id":"dino","symbol":"dino","name":"Dino"},{"id":"dinoegg","symbol":"dinoegg","name":"DINOEGG"},{"id":"dinoland","symbol":"dnl","name":"Dinoland"},{"id":"dinolfg","symbol":"dino","name":"DinoLFG"},{"id":"dinostep","symbol":"dns","name":"DinoStep"},{"id":"dinoswap","symbol":"dino","name":"DinoSwap"},{"id":"dinox","symbol":"dnxc","name":"DinoX"},{"id":"diolaunch","symbol":"dla","name":"Diolaunch"},{"id":"dione","symbol":"dione","name":"Dione"},{"id":"dionpay","symbol":"dion","name":"Dionpay"},{"id":"disbalancer","symbol":"ddos","name":"disBalancer"},{"id":"district0x","symbol":"dnt","name":"district0x"},{"id":"ditto-staked-aptos","symbol":"stapt","name":"Ditto Staked Aptos"},{"id":"divergence-protocol","symbol":"diver","name":"Divergence Protocol"},{"id":"diversified-staked-eth","symbol":"dseth","name":"Diversified Staked ETH"},{"id":"divi","symbol":"divi","name":"Divi"},{"id":"diviner-protocol","symbol":"dpt","name":"Diviner Protocol"},{"id":"diyarbekirspor","symbol":"diyar","name":"Diyarbekirspor"},{"id":"djed","symbol":"djed","name":"Djed"},{"id":"dkargo","symbol":"dka","name":"dKargo"},{"id":"dkey-bank","symbol":"dkey","name":"DKEY Bank"},{"id":"dlp-duck-token","symbol":"duck","name":"DLP Duck"},{"id":"dmarket","symbol":"dmt","name":"DMarket"},{"id":"dmd","symbol":"dmd","name":"DMD"},{"id":"dmm-governance","symbol":"dmg","name":"DMM: Governance"},{"id":"dmt-token","symbol":"dmt","name":"DMT"},{"id":"dmz-token","symbol":"dmz","name":"DMZ"},{"id":"dnaxcat","symbol":"dxct","name":"DNAxCAT"},{"id":"dobermann","symbol":"dobe","name":"Dobermann"},{"id":"dock","symbol":"dock","name":"Dock"},{"id":"docuchain","symbol":"dcct","name":"DocuChain"},{"id":"documentchain","symbol":"dms","name":"Documentchain"},{"id":"dodo","symbol":"dodo","name":"DODO"},{"id":"dodreamchain","symbol":"drm","name":"DoDreamChain"},{"id":"doex","symbol":"doex","name":"DOEX"},{"id":"dog","symbol":"dog","name":"Dog [OLD]"},{"id":"dogami","symbol":"doga","name":"Dogami"},{"id":"dog-boss","symbol":"dogboss","name":"Dog Boss"},{"id":"dogcoin","symbol":"dogs","name":"Dogcoin"},{"id":"dog-collar","symbol":"collar","name":"Dog Collar"},{"id":"doge-1-mission-to-the-moon","symbol":"doge-1","name":"Doge-1 Mission to the moon"},{"id":"doge-alliance","symbol":"dogeally","name":"Doge Alliance"},{"id":"doge-ape","symbol":"dogeape","name":"Doge Ape"},{"id":"dogearmy","symbol":"dogrmy","name":"DogeArmy"},{"id":"doge-army-token","symbol":"dgat","name":"Doge Army"},{"id":"dogebonk","symbol":"dobo","name":"DogeBonk"},{"id":"dogecash","symbol":"dogec","name":"DogeCash"},{"id":"doge-ceo","symbol":"dogeceo","name":"Doge CEO"},{"id":"dogeceomeme","symbol":"dogeceo","name":"DOGE CEO AI"},{"id":"dogechain","symbol":"dc","name":"Dogechain"},{"id":"dogecoin","symbol":"doge","name":"Dogecoin"},{"id":"dogecoin-2","symbol":"doge2","name":"Dogecoin 2.0"},{"id":"dogecola","symbol":"dogecola","name":"DOGECOLA"},{"id":"dogecube","symbol":"dogecube","name":"DogeCube"},{"id":"dogedi","symbol":"dogedi","name":"DOGEDI"},{"id":"doge-digger","symbol":"dogedigger","name":"Doge Digger"},{"id":"dogedragon","symbol":"dd","name":"DogeDragon"},{"id":"doge-eat-doge","symbol":"omnom","name":"Doge Eat Doge"},{"id":"doge-farm","symbol":"dof","name":"Doge Farm"},{"id":"dogefi","symbol":"dogefi","name":"DogeFi"},{"id":"doge-floki-coin","symbol":"dofi","name":"Doge Floki Coin"},{"id":"dogefood","symbol":"dogefood","name":"DogeFood"},{"id":"dogegayson","symbol":"goge","name":"DogeGaySon"},{"id":"dogegf","symbol":"dogegf","name":"DogeGF"},{"id":"dogegrow","symbol":"dgr","name":"DogeGrow"},{"id":"doge-inu","symbol":"dinu","name":"Doge Inu"},{"id":"doge-kaki","symbol":"kaki","name":"Doge KaKi"},{"id":"dogeking","symbol":"dogeking","name":"DogeKing"},{"id":"dogelana","symbol":"dgln","name":"Dogelana"},{"id":"dogelon-classic","symbol":"elonc","name":"Dogelon Classic"},{"id":"dogelon-mars","symbol":"elon","name":"Dogelon Mars"},{"id":"dogelon-mars-wormhole","symbol":"elon","name":"Dogelon Mars (Wormhole)"},{"id":"doge-lumens","symbol":"dxlm","name":"DogeLumens"},{"id":"dogemon-go","symbol":"dogo","name":"DogemonGo"},{"id":"dogemoon","symbol":"dogemoon","name":"Dogemoon"},{"id":"dogens","symbol":"dogens","name":"Dogens"},{"id":"dogeon","symbol":"don","name":"Dogeon"},{"id":"dogepad-finance","symbol":"dpf","name":"Dogepad Finance"},{"id":"doge-protocol","symbol":"dogep","name":"Doge Protocol"},{"id":"doge-pup-token","symbol":"dogepup","name":"Doge Pup"},{"id":"doge-rise-up","symbol":"dogeriseup","name":"Doge Rise Up"},{"id":"dogeshiba","symbol":"doshib","name":"DogeShiba"},{"id":"dogeshrek","symbol":"dogeshrek","name":"DogeShrek"},{"id":"dogestribute","symbol":"dgstb","name":"Dogestribute"},{"id":"dogeswap","symbol":"doges","name":"Dogeswap"},{"id":"doge-token","symbol":"doget","name":"Doge Token"},{"id":"dogetools","symbol":"dtools","name":"DogeTools"},{"id":"dogetrend","symbol":"dogetrend","name":"DogeTrend"},{"id":"doge-tv","symbol":"$dgtv","name":"Doge-TV"},{"id":"dogewhale","symbol":"dogewhale","name":"Dogewhale"},{"id":"doge-yellow-coin","symbol":"dogey","name":"Doge Yellow Coin"},{"id":"dogeyield","symbol":"dogy","name":"DogeYield"},{"id":"dogey-inu","symbol":"dinu","name":"Dogey-Inu"},{"id":"doge-zilla","symbol":"dogez","name":"DogeZilla Token"},{"id":"dogezilla-ai","symbol":"dai","name":"DogeZilla Ai"},{"id":"dogezone","symbol":"dgz","name":"Dogezone"},{"id":"dogger","symbol":"dogger","name":"Dogger"},{"id":"doggo","symbol":"doggo","name":"DOGGO"},{"id":"doggy","symbol":"doggy","name":"Doggy"},{"id":"doggystyle-coin","symbol":"dsc","name":"DoggyStyle Coin"},{"id":"dogira","symbol":"dogira","name":"Dogira"},{"id":"doglaikacoin","symbol":"dlc","name":"Doglaikacoin"},{"id":"dog-landing-on-the-moon","symbol":"dogmoon","name":"Dog Landing On The Moon"},{"id":"dogpad-finance","symbol":"dogpad","name":"DogPad Finance"},{"id":"dogs-kombat","symbol":"dk","name":"Dogs Kombat"},{"id":"dogsofelon","symbol":"doe","name":"Dogs Of Elon"},{"id":"dogswap-token","symbol":"dog","name":"Dogeswap (HECO)"},{"id":"dog-tag","symbol":"tag","name":"Dog Tag"},{"id":"dogu-inu","symbol":"dogu","name":"Dogu Inu"},{"id":"dogyrace","symbol":"dor","name":"DogyRace"},{"id":"dogz","symbol":"dogz","name":"Dogz"},{"id":"dohrnii","symbol":"dhn","name":"Dohrnii"},{"id":"doichain","symbol":"doi","name":"Doichain"},{"id":"dojo","symbol":"dojo","name":"DOJO"},{"id":"dojocoin","symbol":"dojo","name":"Dojocoin"},{"id":"doke-inu","symbol":"doke","name":"Doke Inu"},{"id":"doken","symbol":"dkn","name":"DoKEN V2"},{"id":"doki-doki-finance","symbol":"doki","name":"Doki Doki"},{"id":"dola-borrowing-right","symbol":"dbr","name":"DOLA Borrowing Right"},{"id":"dola-usd","symbol":"dola","name":"Dola"},{"id":"dollarback","symbol":"back","name":"DollarBack"},{"id":"dollarmoon","symbol":"dmoon","name":"DollarMoon"},{"id":"dollars","symbol":"usdx","name":"Dollars"},{"id":"domestic-collectors","symbol":"dmc","name":"Domestic Collectors"},{"id":"domi","symbol":"domi","name":"Domi"},{"id":"dominica-coin","symbol":"dmc","name":"Dominica Coin"},{"id":"dominium-2","symbol":"dom","name":"Dominium"},{"id":"domraider","symbol":"drt","name":"DomRaider"},{"id":"don-key","symbol":"don","name":"Don-key"},{"id":"don-t-buy-inu","symbol":"dbi","name":"Don't Buy Inu"},{"id":"donut","symbol":"donut","name":"Donut"},{"id":"doogee","symbol":"doogee","name":"Doogee"},{"id":"doom-hero-dao","symbol":"dhd","name":"Doom Hero Dao"},{"id":"doom-hero-game","symbol":"dhg","name":"Doom Hero Game"},{"id":"doont-buy","symbol":"dbuy","name":"Doont Buy"},{"id":"dope-wars-paper","symbol":"paper","name":"Dope Wars Paper"},{"id":"dopewarz","symbol":"dwz","name":"DopeWarz"},{"id":"dopex","symbol":"dpx","name":"Dopex"},{"id":"dopex-rebate-token","symbol":"rdpx","name":"Dopex Rebate"},{"id":"dopple-finance","symbol":"dop","name":"Dopple Finance"},{"id":"dora-factory","symbol":"dora","name":"Dora Factory"},{"id":"doragonland","symbol":"dor","name":"DoragonLand"},{"id":"doren","symbol":"dre","name":"DoRen"},{"id":"dosa","symbol":"$dosa","name":"Dosa"},{"id":"dos-chain","symbol":"dos","name":"DOS Chain"},{"id":"dose-token","symbol":"dose","name":"DOSE"},{"id":"dos-network","symbol":"dos","name":"DOS Network"},{"id":"dotarcade","symbol":"adt","name":"DotArcade"},{"id":"dot-dot-finance","symbol":"ddd","name":"Dot Dot Finance"},{"id":"dot-finance","symbol":"pink","name":"Dot Finance"},{"id":"dotlab","symbol":"dtl","name":"Dotlab"},{"id":"dotmoovs","symbol":"moov","name":"dotmoovs"},{"id":"dot-names","symbol":"dns","name":"Dot Names"},{"id":"dotoracle","symbol":"dto","name":"DotOracle"},{"id":"dotori","symbol":"dtr","name":"Dotori"},{"id":"doubledice-token","symbol":"dodi","name":"DoubleDice"},{"id":"double-swap-token","symbol":"dst","name":"Double Swap Token"},{"id":"doubloon","symbol":"dbl","name":"Doubloon"},{"id":"douge","symbol":"douge","name":"Douge"},{"id":"dough","symbol":"dough","name":"Dough"},{"id":"dovu","symbol":"dov","name":"Dovu"},{"id":"dpad-finance","symbol":"dpad","name":"Dpad Finance"},{"id":"dprating","symbol":"rating","name":"DPRating"},{"id":"dps-doubloon","symbol":"dbl","name":"DPS Doubloon"},{"id":"dps-rum","symbol":"rum","name":"DPS Rum"},{"id":"dps-treasuremaps","symbol":"tmap","name":"DPS TreasureMaps"},{"id":"dr1ver","symbol":"dr1$","name":"Dr1ver"},{"id":"drac-network","symbol":"drac","name":"DRAC Network"},{"id":"dracoomaster","symbol":"bas","name":"DracooMaster"},{"id":"dracula","symbol":"drc","name":"Dracula"},{"id":"draggable-aktionariat-ag","symbol":"daks","name":"Draggable Aktionariat AG"},{"id":"dragoma","symbol":"dma","name":"Dragoma"},{"id":"dragonbite","symbol":"bite","name":"DragonBite"},{"id":"dragonchain","symbol":"drgn","name":"Dragonchain"},{"id":"dragon-crypto-argenti","symbol":"dcar","name":"Dragon Crypto Argenti"},{"id":"dragon-crypto-aurum","symbol":"dcau","name":"Dragon Crypto Aurum"},{"id":"dragon-mainland-shards","symbol":"dms","name":"Dragon Mainland Shards"},{"id":"dragonmaster-token","symbol":"dmt","name":"DragonMaster"},{"id":"dragonmaster-totem","symbol":"totem","name":"DragonMaster Totem"},{"id":"dragonmoon","symbol":"dmoon","name":"DragonMoon"},{"id":"dragonrace","symbol":"dragace","name":"Dragonrace"},{"id":"dragons-quick","symbol":"dquick","name":"Dragon's Quick"},{"id":"dragonvein","symbol":"dvc","name":"DragonVein"},{"id":"dragon-verse","symbol":"drv","name":"Dragon Verse"},{"id":"dragon-war","symbol":"draw","name":"Dragon War"},{"id":"draken","symbol":"drk","name":"Draken"},{"id":"drawshop-kingdom-reverse-joystick","symbol":"joy","name":"Drawshop Kingdom Reverse Joystick"},{"id":"drc-mobility","symbol":"drc","name":"DRC Mobility"},{"id":"dreamdao","symbol":"dream","name":"DreamDAO"},{"id":"dreamr-platform-token","symbol":"dmr","name":"Dreamr Platform"},{"id":"dreamscoin","symbol":"dream","name":"DreamsCoin"},{"id":"dream-soccer","symbol":"dsoccer","name":"Dream Soccer"},{"id":"dreams-quest","symbol":"dreams","name":"Dreams Quest"},{"id":"dream-token","symbol":"dream","name":"Dream"},{"id":"dreamverse","symbol":"dv","name":"Dreamverse"},{"id":"dreamy-undersea-world","symbol":"duw","name":"Dreamy Undersea World"},{"id":"drep-new","symbol":"drep","name":"Drep"},{"id":"drife","symbol":"drf","name":"Drife"},{"id":"dripdropz","symbol":"drip","name":"DripDropz"},{"id":"drip-network","symbol":"drip","name":"Drip Network"},{"id":"dripto","symbol":"dryp","name":"Dripto"},{"id":"drive-crypto","symbol":"drivecrypto","name":"Drive Crypto"},{"id":"drivenx","symbol":"dvx","name":"DRIVENx"},{"id":"drivez","symbol":"driv","name":"Drivez"},{"id":"drops-ownership-power","symbol":"dop","name":"Drops Ownership Power"},{"id":"drunk-robots","symbol":"metal","name":"Drunk Robots"},{"id":"drunk-skunks-drinking-club","symbol":"stink","name":"Drunk Skunks Drinking Club"},{"id":"d-runt","symbol":"drnt","name":"D-RUNT"},{"id":"dsc-mix","symbol":"mix","name":"DSC Mix"},{"id":"dshares","symbol":"dshare","name":"DShares"},{"id":"dsquared-finance","symbol":"dsq","name":"Dsquared.finance"},{"id":"dtng","symbol":"dtng","name":"DTNG"},{"id":"dtravel","symbol":"trvl","name":"TRVL"},{"id":"dtsla","symbol":"dtsla","name":"Tesla Tokenized Stock Defichain"},{"id":"dtube-coin","symbol":"dtube","name":"Dtube Coin"},{"id":"dual-finance","symbol":"dual","name":"Dual Finance"},{"id":"dua-token","symbol":"dua","name":"DUA Token"},{"id":"dubbz","symbol":"dubbz","name":"Dubbz"},{"id":"ducatus","symbol":"ducx","name":"Ducatus"},{"id":"duckdaodime","symbol":"ddim","name":"DuckDaoDime"},{"id":"duckduck-token","symbol":"duck","name":"DuckDuck"},{"id":"duckereum","symbol":"ducker","name":"Duckereum"},{"id":"duckie-land-multi-metaverse","symbol":"mmeta","name":"Duckie Land Multi Metaverse"},{"id":"duckies","symbol":"duckies","name":"Yellow Duckies"},{"id":"duck-punkz-universe-floor-index","symbol":"dpunkz","name":"Duck Punkz Universe Floor Index"},{"id":"duckrocket","symbol":"duck","name":"DuckRocket"},{"id":"duckydefi","symbol":"degg","name":"DuckyDefi"},{"id":"dude","symbol":"dude","name":"DuDe"},{"id":"duelist-king","symbol":"dkt","name":"Duelist King"},{"id":"duel-network","symbol":"duel","name":"Duel Network"},{"id":"duet-protocol","symbol":"duet","name":"Duet Protocol"},{"id":"duke-inu-token","symbol":"duke","name":"Duke Inu"},{"id":"dumpbuster","symbol":"gtfo","name":"DumpBuster"},{"id":"dungeonswap","symbol":"dnd","name":"DungeonSwap"},{"id":"dungeon-token","symbol":"geon","name":"Triathon"},{"id":"dusk-network","symbol":"dusk","name":"DUSK Network"},{"id":"dust-protocol","symbol":"dust","name":"DUST Protocol"},{"id":"dux","symbol":"dux","name":"DUX"},{"id":"duzce","symbol":"duzce","name":"Duzce"},{"id":"dvision-network","symbol":"dvi","name":"Dvision Network"},{"id":"dwagon","symbol":"$dwagon","name":"Dwagon"},{"id":"dxbpay","symbol":"dxb","name":"DXBPay"},{"id":"dxcad","symbol":"dxcad","name":"dXCAD"},{"id":"dxchain","symbol":"dx","name":"DxChain"},{"id":"dxdao","symbol":"dxd","name":"DXdao"},{"id":"dxsale-network","symbol":"sale","name":"DxSale Network"},{"id":"dx-spot","symbol":"dxs","name":"Dx Spot"},{"id":"dxy-finance","symbol":"dxy","name":"DXY Finance"},{"id":"dyakon","symbol":"dyn","name":"DYAKON"},{"id":"dydx","symbol":"dydx","name":"dYdX"},{"id":"dydx-wormhole","symbol":"dydx","name":"dYdX (Wormhole)"},{"id":"dymmax","symbol":"dmx","name":"Dymmax"},{"id":"dynamic-finance","symbol":"dyna","name":"Dynamic Finance"},{"id":"dynamic-set-dollar","symbol":"dsd","name":"Dynamic Set Dollar"},{"id":"dynamite-token","symbol":"dynmt","name":"Dynamite"},{"id":"dynamix","symbol":"dyna","name":"Dynamix"},{"id":"dynamo-coin","symbol":"dynamo","name":"Dynamo Coin"},{"id":"dynex","symbol":"dnx","name":"Dynex"},{"id":"dynochain","symbol":"dnd","name":"DynoChain"},{"id":"dyor","symbol":"dyor","name":"DYOR"},{"id":"dystopia","symbol":"dyst","name":"Dystopia"},{"id":"dyzilla","symbol":"dyzilla","name":"DYZilla"},{"id":"eaglecoin-2","symbol":"elc","name":"EagleCoin"},{"id":"eagle-mining-network","symbol":"egon","name":"EAGLE MINING NETWORK"},{"id":"eagonswap-token","symbol":"eagon","name":"EagonSwap"},{"id":"early-bird","symbol":"ebird","name":"Early Bird"},{"id":"earnbusd","symbol":"ebusd","name":"EarnBUSD"},{"id":"earncraft","symbol":"plot","name":"Earncraft"},{"id":"earndefi","symbol":"edc","name":"EarnDeFi"},{"id":"earnguild","symbol":"earn","name":"EarnGuild"},{"id":"earnx-v2","symbol":"earnx","name":"EarnX V2"},{"id":"earnzcoin","symbol":"erz","name":"EarnzCoin"},{"id":"earthbyt","symbol":"ebyt","name":"EarthByt"},{"id":"earthfund","symbol":"1earth","name":"EarthFund"},{"id":"ease","symbol":"ease","name":"EASE"},{"id":"easter-floki","symbol":"efloki","name":"Easter Floki"},{"id":"easyfi","symbol":"ez","name":"EasyFi V2"},{"id":"easymine","symbol":"emt","name":"easyMine"},{"id":"eat-to-earn","symbol":"eater","name":"Eat to Earn"},{"id":"eblockstock","symbol":"ebso","name":"eBlockStock"},{"id":"ebox","symbol":"ebox","name":"Ebox"},{"id":"ecash","symbol":"xec","name":"eCash"},{"id":"eceltron","symbol":"ectr","name":"eCeltron"},{"id":"echain-network","symbol":"ect","name":"Echain Network"},{"id":"echelon-prime","symbol":"prime","name":"Echelon Prime"},{"id":"echidna","symbol":"ecd","name":"Echidna"},{"id":"echoin","symbol":"ec","name":"Echoin"},{"id":"echolink","symbol":"eko","name":"EchoLink"},{"id":"echosoracoin","symbol":"esrc","name":"EchoSoraCoin"},{"id":"eclat","symbol":"elt","name":"ECLAT"},{"id":"eco","symbol":"eco","name":"ECO"},{"id":"ecobit","symbol":"ecob","name":"Ecobit"},{"id":"ecochain-token","symbol":"ect","name":"Ecochain Finance"},{"id":"ecocredit","symbol":"eco","name":"EcoCREDIT"},{"id":"eco-defi","symbol":"ecop","name":"Eco DeFi"},{"id":"ecog9coin","symbol":"egc","name":"EcoG9coin"},{"id":"ecoin-2","symbol":"ecoin","name":"Ecoin"},{"id":"ecoin-finance","symbol":"ecoin","name":"Ecoin Finance"},{"id":"ecomi","symbol":"omi","name":"ECOMI"},{"id":"ecoreal-estate","symbol":"ecoreal","name":"Ecoreal Estate"},{"id":"ecoscu","symbol":"ecu","name":"ECOSC"},{"id":"ecosystem-coin-network","symbol":"ecn","name":"Ecosystem Coin Network"},{"id":"eco-value-coin","symbol":"evc","name":"Eco Value Coin"},{"id":"ecowatt","symbol":"ewt","name":"Ecowatt"},{"id":"ecox","symbol":"ecox","name":"ECOx"},{"id":"ecs-gold","symbol":"ecg","name":"ECS Gold"},{"id":"e-c-vitoria-fan-token","symbol":"vtra","name":"E.C. Vitoria Fan Token"},{"id":"edac","symbol":"edac","name":"EDAC"},{"id":"edain","symbol":"eai","name":"Edain"},{"id":"eddaswap","symbol":"edda","name":"EDDASwap"},{"id":"eden","symbol":"eden","name":"EDEN"},{"id":"edenloop","symbol":"elt","name":"EdenLoop"},{"id":"edexa-service-token","symbol":"edx","name":"edeXa Service Token"},{"id":"edge","symbol":"edge","name":"Edge"},{"id":"edge-activity","symbol":"eat","name":"EDGE Activity"},{"id":"edgecoin-2","symbol":"edgt","name":"Edgecoin"},{"id":"edgeless","symbol":"edg","name":"Edgeless"},{"id":"edgeswap","symbol":"egs","name":"EdgeSwap"},{"id":"edgeware","symbol":"edg","name":"Edgeware"},{"id":"edoverse-zeni","symbol":"zeni","name":"Edoverse Zeni"},{"id":"education-assessment-cult","symbol":"eac","name":"Education Assessment Cult"},{"id":"edufex","symbol":"edux","name":"Edufex"},{"id":"effect-network","symbol":"efx","name":"Effect Network"},{"id":"efin-decentralized","symbol":"wefin","name":"eFin Decentralized"},{"id":"efinity","symbol":"efi","name":"Efinity"},{"id":"efk-token","symbol":"efk","name":"EFK Token"},{"id":"efun","symbol":"efun","name":"EFUN"},{"id":"egg-n-partners","symbol":"eggt","name":"Egg N Partners"},{"id":"eggplant-finance","symbol":"eggp","name":"Eggplant Finance"},{"id":"eggplus","symbol":"eggplus","name":"EggPlus"},{"id":"eggs","symbol":"eggs","name":"Eggs"},{"id":"egod-the-savior","symbol":"$savior","name":"Egod The Savior"},{"id":"egoplatform","symbol":"ego","name":"EGO"},{"id":"egoras-credit","symbol":"egc","name":"Egoras Credit"},{"id":"egretia","symbol":"egt","name":"Egretia"},{"id":"eg-token","symbol":"eg","name":"EG Token"},{"id":"ehash","symbol":"ehash","name":"EHash"},{"id":"ehive","symbol":"ehive","name":"eHive"},{"id":"eifi-finance","symbol":"eifi","name":"EIFI Finance"},{"id":"eiichiro-oda","symbol":"oda","name":"Odasea"},{"id":"eiichiro-oda-inu","symbol":"oda","name":"Eiichiro Oda Inu"},{"id":"einsteinium","symbol":"emc2","name":"Einsteinium"},{"id":"ekta-2","symbol":"ekta","name":"Ekta"},{"id":"elamachain","symbol":"elama","name":"Elamachain"},{"id":"elan","symbol":"elan","name":"Elan"},{"id":"elasticswap","symbol":"tic","name":"ElasticSwap"},{"id":"elastos","symbol":"ela","name":"Elastos"},{"id":"el-dorado-exchange","symbol":"ede","name":"El Dorado Exchange"},{"id":"el-dorado-exchange-arb","symbol":"ede","name":"El Dorado Exchange (Arb)"},{"id":"electra","symbol":"eca","name":"Electra"},{"id":"electra-protocol","symbol":"xep","name":"Electra Protocol"},{"id":"electric-cash","symbol":"elcash","name":"Electric Cash"},{"id":"electric-vehicle-direct-currency","symbol":"evdc","name":"Electric Vehicle Direct Currency"},{"id":"electric-vehicle-zone","symbol":"evz","name":"Electric Vehicle Zone"},{"id":"electrify-asia","symbol":"elec","name":"Electrify.Asia"},{"id":"electroneum","symbol":"etn","name":"Electroneum"},{"id":"electronicgulden","symbol":"efl","name":"Electronic Gulden"},{"id":"electronic-usd","symbol":"eusd","name":"Electronic USD"},{"id":"element-black","symbol":"elt","name":"Element Black"},{"id":"elementrem","symbol":"ele","name":"Elementrem"},{"id":"elements-2","symbol":"elm","name":"Elements"},{"id":"elemon","symbol":"elmon","name":"Elemon"},{"id":"elephant-money","symbol":"elephant","name":"Elephant Money"},{"id":"eleventoken","symbol":"elvn","name":"11Minutes"},{"id":"elf-wallet","symbol":"elf","name":"ELF Wallet"},{"id":"elfworld","symbol":"elft","name":"Elfworld"},{"id":"eligma","symbol":"goc","name":"GoCrypto"},{"id":"elis","symbol":"xls","name":"ELIS"},{"id":"elitium","symbol":"eum","name":"Elitium"},{"id":"elk-finance","symbol":"elk","name":"Elk Finance"},{"id":"ellerium","symbol":"elm","name":"ELLERIUM"},{"id":"ellipsis","symbol":"eps","name":"Ellipsis [OLD]"},{"id":"ellipsis-x","symbol":"epx","name":"Ellipsis X"},{"id":"eloin","symbol":"eloin","name":"Eloin"},{"id":"elo-inu","symbol":"elo inu","name":"Elo Inu"},{"id":"elonbank","symbol":"elonbank","name":"ElonBank"},{"id":"elondoge-dao","symbol":"edao","name":"ElonDoge DAO"},{"id":"elon-doge-token","symbol":"edoge","name":"ElonDoge.io"},{"id":"elongate-duluxe","symbol":"elongd","name":"Elongate Deluxe"},{"id":"elon-goat","symbol":"egt","name":"Elon GOAT"},{"id":"eloniumcoin","symbol":"elnc","name":"EloniumCoin"},{"id":"elons-marvin","symbol":"marvin","name":"Elon's Marvin"},{"id":"elpis-battle","symbol":"eba","name":"Elpis Battle"},{"id":"elrond-erd-2","symbol":"egld","name":"MultiversX"},{"id":"elseverse-world","symbol":"ells","name":"ElseVerse World"},{"id":"eltcoin","symbol":"eltcoin","name":"Eltcoin"},{"id":"elumia","symbol":"elu","name":"Elumia"},{"id":"elvishmagic","symbol":"emp","name":"ElvishMagic"},{"id":"elya","symbol":"elya","name":"Elya"},{"id":"elyfi","symbol":"elfi","name":"ELYFI"},{"id":"elysia","symbol":"el","name":"ELYSIA"},{"id":"elysiant-token","symbol":"els","name":"Elysian ELS"},{"id":"elysiumg","symbol":"lcmg","name":"ElysiumG"},{"id":"emanate","symbol":"emt","name":"Emanate"},{"id":"ember","symbol":"ember","name":"Ember"},{"id":"embr","symbol":"embr","name":"Embr"},{"id":"emcis-network","symbol":"emc1","name":"EMCIS NETWORK"},{"id":"emerald-crypto","symbol":"emd","name":"Emerald Crypto"},{"id":"emercoin","symbol":"emc","name":"EmerCoin"},{"id":"emg-coin","symbol":"emg","name":"EMG Coin"},{"id":"eminer","symbol":"em","name":"Eminer"},{"id":"emirate-swap-token","symbol":"emc","name":"Emirate Swap Token"},{"id":"e-money","symbol":"ngm","name":"e-Money"},{"id":"e-money-eur","symbol":"eeur","name":"e-Money EUR"},{"id":"empire-capital-token","symbol":"ecc","name":"Empire Capital"},{"id":"empire-network","symbol":"empire","name":"Empire Network"},{"id":"empire-token","symbol":"empire","name":"Empire"},{"id":"empowa","symbol":"emp","name":"Empowa"},{"id":"emp-shares","symbol":"eshare","name":"EMP Shares"},{"id":"empty-set-share","symbol":"ess","name":"Empty Set Share"},{"id":"encrypgen","symbol":"dna","name":"EncrypGen"},{"id":"endless-battlefield","symbol":"eng","name":"Endless Board Game"},{"id":"endlesswebworlds","symbol":"eww","name":"EndlessWebWorlds"},{"id":"endor","symbol":"edr","name":"Endor Protocol"},{"id":"endpoint-cex-fan-token","symbol":"endcex","name":"Endpoint Cex Fan Token"},{"id":"eneftiverse","symbol":"evr","name":"ENEFTIVERSE"},{"id":"eneftor","symbol":"eftr","name":"Eneftor"},{"id":"enegra","symbol":"egx","name":"Enegra"},{"id":"energi","symbol":"nrg","name":"Energi"},{"id":"energi-dollar","symbol":"usde","name":"Energi Dollar"},{"id":"energo","symbol":"tsl","name":"Tesla TSL"},{"id":"energy8","symbol":"e8","name":"Energy8"},{"id":"energy-efficient-mortgage-tokenized-stock-defichain","symbol":"deem","name":"iShares MSCI Emerging Markets ETF Defichain"},{"id":"energyfi","symbol":"eft","name":"Energyfi"},{"id":"energytrade-token","symbol":"ett","name":"EnergyTrade Token"},{"id":"energy-web-token","symbol":"ewt","name":"Energy Web"},{"id":"enex","symbol":"enx","name":"ENEX"},{"id":"eng-crypto","symbol":"eng","name":"Eng Crypto"},{"id":"enigma","symbol":"eng","name":"Enigma"},{"id":"enjincoin","symbol":"enj","name":"Enjin Coin"},{"id":"enjinstarter","symbol":"ejs","name":"Enjinstarter"},{"id":"enno-cash","symbol":"enno","name":"ENNO Cash"},{"id":"eno","symbol":"eno","name":"ENO"},{"id":"enq-enecuum","symbol":"enq","name":"Enecuum"},{"id":"enreachdao","symbol":"nrch","name":"Enreach"},{"id":"enrex","symbol":"enrx","name":"Enrex"},{"id":"enterbutton","symbol":"entc","name":"EnterButton"},{"id":"enterdao","symbol":"entr","name":"EnterDAO"},{"id":"entice-v2","symbol":"ntic","name":"Entice"},{"id":"entropyfi","symbol":"erp","name":"Entropyfi"},{"id":"envida","symbol":"edat","name":"EnviDa"},{"id":"envion","symbol":"evn","name":"Envion"},{"id":"envision","symbol":"vis","name":"Envision"},{"id":"envoy-network","symbol":"env","name":"Envoy"},{"id":"eos","symbol":"eos","name":"EOS"},{"id":"eosbet","symbol":"bet","name":"EarnBet"},{"id":"eosdac","symbol":"eosdac","name":"eosDAC"},{"id":"eosforce","symbol":"eosc","name":"EOSForce"},{"id":"eos-pow-coin","symbol":"pow","name":"EOS PoW Coin"},{"id":"epic-cash","symbol":"epic","name":"Epic Cash"},{"id":"epichero","symbol":"epichero","name":"EpicHero"},{"id":"epics-token","symbol":"epct","name":"Epics Token"},{"id":"epik-prime","symbol":"epik","name":"Epik Prime"},{"id":"epik-protocol","symbol":"epk","name":"EpiK Protocol"},{"id":"epillo","symbol":"epillo","name":"Epillo"},{"id":"eq9","symbol":"eq9","name":"Equals9"},{"id":"eqifi","symbol":"eqx","name":"EQIFi"},{"id":"equalizer","symbol":"eqz","name":"Equalizer"},{"id":"equalizer-dex","symbol":"equal","name":"Equalizer DEX"},{"id":"equilibre","symbol":"vara","name":"Équilibre"},{"id":"equilibrium","symbol":"eq","name":"Equilibrium Games"},{"id":"equilibrium-eosdt","symbol":"eosdt","name":"Equilibrium EOSDT"},{"id":"equilibrium-exchange","symbol":"edx","name":"Equilibrium Exchange"},{"id":"equilibrium-token","symbol":"eq","name":"Equilibrium"},{"id":"equinox","symbol":"enx","name":"Equinox"},{"id":"era","symbol":"era","name":"Era"},{"id":"era7","symbol":"era","name":"Era7"},{"id":"era7-game-of-truth","symbol":"got","name":"Era7: Game of Truth"},{"id":"e-radix","symbol":"exrd","name":"e-Radix"},{"id":"era-swap-token","symbol":"es","name":"Era Swap"},{"id":"ergo","symbol":"erg","name":"Ergo"},{"id":"erica-social-token","symbol":"est","name":"Erica Social Token"},{"id":"eris-amplified-luna","symbol":"ampluna","name":"Eris Amplified Luna"},{"id":"eron","symbol":"eron","name":"ERON"},{"id":"eroverse","symbol":"ero","name":"Eroverse"},{"id":"ertha","symbol":"ertha","name":"Ertha"},{"id":"erth-point","symbol":"erth","name":"Erth Point"},{"id":"erugo-world-coin","symbol":"ewc","name":"Erugo World Coin"},{"id":"erzurumspor-token","symbol":"erz","name":"Erzurumspor Token"},{"id":"escoin-token","symbol":"elg","name":"Escoin"},{"id":"escrowed-illuvium-2","symbol":"silv2","name":"Escrowed Illuvium 2"},{"id":"esg","symbol":"esg","name":"ESG"},{"id":"esg-chain","symbol":"esgc","name":"ESG Chain"},{"id":"eska","symbol":"esk","name":"Eska"},{"id":"eskisehir-fan-token","symbol":"eses","name":"Eskişehir Fan Token"},{"id":"espers","symbol":"esp","name":"Espers"},{"id":"espl-arena","symbol":"arena","name":"ESPL Arena"},{"id":"esporte-clube-bahia-fan-token","symbol":"bahia","name":"Esporte Clube Bahia Fan Token"},{"id":"esports","symbol":"ert","name":"Esports.com"},{"id":"esportspro","symbol":"espro","name":"EsportsPro"},{"id":"esportsref","symbol":"esr","name":"EsportsRef"},{"id":"essentia","symbol":"ess","name":"Essentia"},{"id":"estar-games","symbol":"estar","name":"ESTAR.GAMES"},{"id":"eswapping-v2","symbol":"eswapv2","name":"eSwapping v2"},{"id":"eterland","symbol":"eter","name":"Eterland"},{"id":"etermon","symbol":"etm","name":"Etermon"},{"id":"eternal-finance","symbol":"etern","name":"Eternal Finance"},{"id":"eternalflow","symbol":"eft","name":"EternalFlow"},{"id":"eth2-staking-by-poolx","symbol":"eth2","name":"Eth 2.0 Staking by Pool-X"},{"id":"eth-2x-flexible-leverage-index","symbol":"eth2x-fli","name":"Index Coop - ETH 2x Flexible Leverage Index"},{"id":"eth3s","symbol":"eth3s","name":"ETH3S"},{"id":"etha-lend","symbol":"etha","name":"ETHA Lend"},{"id":"ethart","symbol":"arte","name":"Items"},{"id":"ethax","symbol":"ethax","name":"ETHAX"},{"id":"ethbnt","symbol":"ethbnt","name":"ETHBNT Relay"},{"id":"ethburn","symbol":"burning","name":"EthBurn"},{"id":"ethdown","symbol":"ethdown","name":"ETHDOWN"},{"id":"etheal","symbol":"heal","name":"Etheal"},{"id":"ether-1","symbol":"etho","name":"Etho Protocol"},{"id":"etherconnect","symbol":"ecc","name":"Etherconnect"},{"id":"ethereans","symbol":"os","name":"Ethereans"},{"id":"ethereum","symbol":"eth","name":"Ethereum"},{"id":"ethereum-cash","symbol":"ecash","name":"Ethereum Cash"},{"id":"ethereum-classic","symbol":"etc","name":"Ethereum Classic"},{"id":"ethereumfair","symbol":"ethf","name":"EthereumFair"},{"id":"ethereummax","symbol":"emax","name":"EthereumMax"},{"id":"ethereum-meta","symbol":"ethm","name":"Ethereum Meta"},{"id":"ethereum-name-service","symbol":"ens","name":"Ethereum Name Service"},{"id":"ethereum-pow-iou","symbol":"ethw","name":"EthereumPoW"},{"id":"ethereum-push-notification-service","symbol":"push","name":"Push Protocol"},{"id":"ethereum-victory","symbol":"evic","name":"Ethereum Victory"},{"id":"ethereum-volatility-index-token","symbol":"ethv","name":"Ethereum Volatility Index Token"},{"id":"ethereum-wormhole","symbol":"eth","name":"Ethereum (Wormhole)"},{"id":"ethereumx","symbol":"etx","name":"EthereumX"},{"id":"ethereum-yield","symbol":"ethy","name":"Ethereum Yield"},{"id":"ethergem","symbol":"egem","name":"EtherGem"},{"id":"etherinc","symbol":"eti","name":"EtherInc"},{"id":"etherisc","symbol":"dip","name":"Etherisc DIP"},{"id":"etherland","symbol":"eland","name":"Etherland"},{"id":"etherlite-2","symbol":"etl","name":"EtherLite"},{"id":"ethermon","symbol":"emon","name":"Ethermon"},{"id":"ethernaal","symbol":"naal","name":"Ethernaal"},{"id":"ethernal","symbol":"ethernal","name":"Ethernal"},{"id":"ethernal-finance","symbol":"ethfin","name":"Ethernal Finance"},{"id":"ethernity-chain","symbol":"ern","name":"Ethernity Chain"},{"id":"etherparty","symbol":"fuel","name":"Etherparty"},{"id":"etherrock-72","symbol":"pebble","name":"Etherrock #72"},{"id":"ethersmart","symbol":"etm","name":"EtherSmart"},{"id":"ether-tech","symbol":"ether","name":"Ether Tech"},{"id":"ethfan-burn","symbol":"$efb","name":"ETHFan Burn"},{"id":"eth-fan-token","symbol":"eft","name":"ETH Fan Token Ecosystem"},{"id":"ethforestai","symbol":"ethfai","name":"ETHforestAI"},{"id":"ethichub","symbol":"ethix","name":"Ethix"},{"id":"ethlend","symbol":"lend","name":"Aave [OLD]"},{"id":"eth-max-yield-index","symbol":"ethmaxy","name":"ETH Max Yield Index"},{"id":"ethos","symbol":"vgx","name":"Voyager VGX"},{"id":"ethpad","symbol":"ethpad","name":"ETHPad"},{"id":"eth-shiba","symbol":"ethshib","name":"Eth Shiba"},{"id":"ethst-governance-token","symbol":"et","name":"ETHST Governance"},{"id":"ethtez","symbol":"ethtz","name":"ETHtez"},{"id":"ethup","symbol":"ethup","name":"ETHUP"},{"id":"ethw-id","symbol":"eid","name":"ETHW ID"},{"id":"etna-metabolism","symbol":"mtb","name":"ETNA Metabolism"},{"id":"etna-network","symbol":"etna","name":"ETNA Network"},{"id":"etwinfinity","symbol":"etw","name":"ETWInfinity"},{"id":"etxinfinity","symbol":"etx","name":"ETXInfinity"},{"id":"eub-chain","symbol":"eubc","name":"EUB Chain"},{"id":"euler","symbol":"eul","name":"Euler"},{"id":"euno","symbol":"euno","name":"EUNO"},{"id":"euphoria-2","symbol":"wagmi","name":"Euphoria"},{"id":"euro-coin","symbol":"euroc","name":"Euro Coin"},{"id":"eurocoinpay","symbol":"ecte","name":"EurocoinToken"},{"id":"euroe-stablecoin","symbol":"euroe","name":"EUROe Stablecoin"},{"id":"europa","symbol":"orbit","name":"Europa"},{"id":"euro-shiba-inu","symbol":"eshib","name":"Euro Shiba Inu"},{"id":"euro-stable-token","symbol":"eurst","name":"Euro Stable Token"},{"id":"evai-2","symbol":"ev","name":"Evai"},{"id":"evanesco-network","symbol":"eva","name":"Evanesco Network"},{"id":"evedo","symbol":"eved","name":"Evedo"},{"id":"eve-exchange","symbol":"eve","name":"EVE"},{"id":"evencoin","symbol":"evn","name":"EvenCoin"},{"id":"everchain","symbol":"ec","name":"EverChain"},{"id":"everdome","symbol":"dome","name":"Everdome"},{"id":"everearn","symbol":"earn","name":"EverEarn"},{"id":"everearn-eth","symbol":"$earn","name":"EverEarn ETH"},{"id":"everestcoin","symbol":"evcoin","name":"EverestCoin"},{"id":"evereth","symbol":"evereth","name":"EverETH"},{"id":"everex","symbol":"evx","name":"Everex"},{"id":"evergrowcoin","symbol":"egc","name":"EverGrow Coin"},{"id":"everid","symbol":"id","name":"Everest"},{"id":"everipedia","symbol":"iq","name":"IQ"},{"id":"everreflect","symbol":"evrf","name":"EverReflect"},{"id":"everrise","symbol":"rise","name":"EverRise"},{"id":"eversafu","symbol":"eversafu","name":"EverSAFU"},{"id":"eversafuv2","symbol":"es2","name":"EverSAFUv2"},{"id":"everscale","symbol":"ever","name":"Everscale"},{"id":"everstart","symbol":"start","name":"EverStart"},{"id":"everton-fan-token","symbol":"efc","name":"Everton Fan Token"},{"id":"everycoin","symbol":"evy","name":"EveryCoin"},{"id":"every-game","symbol":"egame","name":"Every Game"},{"id":"evil-coin","symbol":"evil","name":"Evil Coin"},{"id":"evilsquidgame","symbol":"evilsquid","name":"EvilSquidGame"},{"id":"evmos","symbol":"evmos","name":"Evmos"},{"id":"evmos-domains","symbol":"evd","name":"Evmos Domains"},{"id":"evo-finance","symbol":"evo","name":"Evo Finance"},{"id":"evoload","symbol":"evld","name":"Evoload"},{"id":"evolution-finance","symbol":"evn","name":"Evolution Finance"},{"id":"evolveai","symbol":"evoai","name":"EvolveAI"},{"id":"evoverse-power","symbol":"epw","name":"Evoverse Power"},{"id":"evoverses","symbol":"evo","name":"EvoVerses"},{"id":"evoverse-shard","symbol":"evs","name":"Evoverse Shard"},{"id":"evrice","symbol":"evc","name":"Evrice"},{"id":"evrynet","symbol":"evry","name":"Evrynet"},{"id":"evulus","symbol":"evu","name":"Evulus"},{"id":"excalibur","symbol":"exc","name":"Excalibur"},{"id":"excelon","symbol":"xlon","name":"Excelon"},{"id":"exchangecoin","symbol":"excc","name":"ExchangeCoin"},{"id":"exchange-genesis-ethlas-medium","symbol":"xgem","name":"Exchange Genesis Ethlas Medium"},{"id":"exchange-union","symbol":"xuc","name":"Exchange Union"},{"id":"exciting-japan-coin","symbol":"xjp","name":"eXciting Japan Coin"},{"id":"exeedme","symbol":"xed","name":"Exeedme"},{"id":"exeno","symbol":"exn","name":"Exeno"},{"id":"exenpay-token","symbol":"exenp","name":"ExenPay"},{"id":"exentoken","symbol":"exen","name":"Exen"},{"id":"exmo-coin","symbol":"exm","name":"EXMO Coin"},{"id":"exobots","symbol":"exos","name":"Exobots"},{"id":"exodusext","symbol":"ext","name":"ExodusExt"},{"id":"exohood","symbol":"exo","name":"Exohood"},{"id":"exorde","symbol":"exd","name":"Exorde"},{"id":"exosama-network","symbol":"sama","name":"Moonsama"},{"id":"exp","symbol":"exp","name":"Exp"},{"id":"expanse","symbol":"exp","name":"Expanse"},{"id":"experience-chain","symbol":"xpc","name":"eXPerience Chain"},{"id":"experiencecoin","symbol":"epc","name":"ExperienceCoin"},{"id":"experty-wisdom-token","symbol":"wis","name":"Experty Wisdom"},{"id":"exponential-capital","symbol":"expo","name":"Exponential Capital [OLD]"},{"id":"exponential-capital-2","symbol":"expo","name":"Exponential Capital"},{"id":"export-mortos-platform","symbol":"emp","name":"Export Motors Platform"},{"id":"exrnchain","symbol":"exrn","name":"EXRNchain"},{"id":"exrt-network","symbol":"exrt","name":"EXRT Network"},{"id":"extractodao-bull","symbol":"xbll","name":"ExtractoDAO Bull"},{"id":"extradna","symbol":"xdna","name":"extraDNA"},{"id":"eyes-protocol","symbol":"eyes","name":"EYES Protocol"},{"id":"eyeverse","symbol":"eye","name":"Eyeverse"},{"id":"ezillion","symbol":"ezi","name":"Ezillion"},{"id":"ezystayz","symbol":"ezy","name":"Ezystayz"},{"id":"ezzy-game","symbol":"ezy","name":"EZZY Game"},{"id":"f1d-token","symbol":"f1d","name":"F1D Token"},{"id":"fable-of-the-dragon","symbol":"tyrant","name":"Fable Of The Dragon"},{"id":"fable-of-the-shiba","symbol":"syrant","name":"Fable Of The Shiba"},{"id":"fabric","symbol":"fab","name":"Fabric"},{"id":"fabwelt","symbol":"welt","name":"Fabwelt"},{"id":"facebook-tokenized-stock-defichain","symbol":"dfb","name":"Facebook Tokenized Stock Defichain"},{"id":"facedao","symbol":"face","name":"FaceDAO"},{"id":"factor","symbol":"fctr","name":"FactorDAO"},{"id":"facts","symbol":"bkc","name":"FACTS"},{"id":"fado-go","symbol":"fado","name":"FADO Go"},{"id":"fahrenheit-chain","symbol":"wfac","name":"Fahrenheit Chain"},{"id":"fairgame","symbol":"fair","name":"FairGame"},{"id":"fairum","symbol":"fai","name":"Fairum"},{"id":"fairy-finance-unicorn","symbol":"unicorn","name":"Fairy Finance UNICORN"},{"id":"faith-tribe","symbol":"ftrb","name":"Faith Tribe"},{"id":"falafel","symbol":"falafel","name":"Falafel"},{"id":"falcon","symbol":"fln","name":"Falcon"},{"id":"falcon-nine","symbol":"f9","name":"Falcon Nine"},{"id":"falcon-swaps","symbol":"falcons","name":"FALCONS"},{"id":"falcon-token","symbol":"fnt","name":"Falcon Project"},{"id":"falconx","symbol":"falcx","name":"FalconX"},{"id":"fame-mma","symbol":"fame","name":"Fame MMA"},{"id":"fame-reward-plus","symbol":"frp","name":"Fame Reward Plus"},{"id":"familyparty","symbol":"fpc","name":"FamilyParty"},{"id":"famous-fox-federation","symbol":"foxy","name":"Famous Fox Federation"},{"id":"fanadise","symbol":"fan","name":"Fanadise"},{"id":"fanc","symbol":"fanc","name":"fanC"},{"id":"fancy-games","symbol":"fnc","name":"Fancy Games"},{"id":"fandom","symbol":"fdm","name":"Fautor"},{"id":"fandora-network","symbol":"fan","name":"Fandora Network"},{"id":"fanfury","symbol":"fury","name":"Fanfury"},{"id":"fang-token","symbol":"fang","name":"FANG"},{"id":"fanitrade","symbol":"fani","name":"FaniTrade"},{"id":"fanstime","symbol":"fti","name":"FansTime"},{"id":"fantasy-gold","symbol":"fgc","name":"Fantasy Gold"},{"id":"fantasy-war","symbol":"fawa","name":"Fantasy War"},{"id":"fantaverse","symbol":"ut","name":"Fantaverse"},{"id":"fantohm","symbol":"fhm","name":"Fantohm"},{"id":"fan-tokens-football","symbol":"ftf","name":"Fan Tokens Football"},{"id":"fantom","symbol":"ftm","name":"Fantom"},{"id":"fantom-doge","symbol":"rip","name":"Fantom Doge"},{"id":"fantomgo","symbol":"ftg","name":"OnGo"},{"id":"fantom-libero-financial","symbol":"flibero","name":"Fantom Libero Financial"},{"id":"fantom-maker","symbol":"fame","name":"Fantom Maker"},{"id":"fantom-oasis","symbol":"ftmo","name":"Fantom Oasis"},{"id":"fantomstarter","symbol":"fs","name":"FantomStarter"},{"id":"fantom-usd","symbol":"fusd","name":"Fantom USD"},{"id":"fanverse-token","symbol":"ft","name":"Fanverse Token"},{"id":"fanzee-token","symbol":"fnz","name":"Fanzee Token"},{"id":"fanzy","symbol":"fx1","name":"FANZY"},{"id":"faraland","symbol":"fara","name":"FaraLand"},{"id":"farmercryptocoin","symbol":"fcc","name":"FarmerCryptoCoin"},{"id":"farmerdoge","symbol":"crop","name":"FarmerDoge"},{"id":"farmers-only","symbol":"fox","name":"FoxSwap"},{"id":"farmers-world-wood","symbol":"fww","name":"Farmers World Wood"},{"id":"farming-paradise","symbol":"fpg","name":"Farming Paradise"},{"id":"farmland-protocol","symbol":"far","name":"Farmland Protocol"},{"id":"farm-planet","symbol":"fpl","name":"Farm Planet"},{"id":"farms-of-ryoshi","symbol":"noni","name":"Farms of Ryoshi"},{"id":"fashion-coin","symbol":"fshn","name":"Fashion Coin"},{"id":"fasst","symbol":"fas","name":"Fasst"},{"id":"fast-finance","symbol":"fast","name":"Fast Finance"},{"id":"fast-food-wolf-game","symbol":"ffwool","name":"Fast Food Wolf Game"},{"id":"fastmoon","symbol":"fastmoon","name":"FastMoon"},{"id":"fastswap","symbol":"fast","name":"FastSwap"},{"id":"fastswap-bsc","symbol":"fast","name":"Fastswap (BSC)"},{"id":"fasttoken","symbol":"ftn","name":"Fasttoken"},{"id":"fatcake","symbol":"fatcake","name":"FatCake"},{"id":"fat-cat","symbol":"fatcat","name":"FAT CAT"},{"id":"fat-cat-killer","symbol":"killer","name":"Fat Cat Killer"},{"id":"fate-token","symbol":"fate","name":"Fate"},{"id":"fatih-karagumruk-sk-fan-token","symbol":"fksk","name":"Fatih Karagümrük SK Fan Token"},{"id":"favor","symbol":"favor","name":"Favor"},{"id":"fayre","symbol":"fayre","name":"Fayre"},{"id":"fbomb","symbol":"bomb","name":"Fantom Bomb"},{"id":"fc-barcelona-fan-token","symbol":"bar","name":"FC Barcelona Fan Token"},{"id":"fc-porto","symbol":"porto","name":"FC Porto"},{"id":"fcr-coin","symbol":"fcr","name":"FCR Coin"},{"id":"fc-sion-fan-token","symbol":"sion","name":"FC Sion Fan Token"},{"id":"fear","symbol":"fear","name":"FEAR"},{"id":"feathercoin","symbol":"ftc","name":"Feathercoin"},{"id":"fedoracoin","symbol":"tips","name":"Fedoracoin"},{"id":"feeder-finance","symbol":"feed","name":"Feeder Finance"},{"id":"feg-bsc","symbol":"feg","name":"FEG BSC"},{"id":"feg-token","symbol":"feg","name":"FEG (OLD)"},{"id":"feg-token-2","symbol":"feg","name":"FEG"},{"id":"feg-token-bsc","symbol":"feg","name":"FEG BSC (OLD)"},{"id":"feichang-niu","symbol":"fcn","name":"Feichang Niu"},{"id":"feisty-doge-nft","symbol":"nfd","name":"Feisty Doge NFT"},{"id":"fei-usd","symbol":"fei","name":"Fei USD"},{"id":"felix","symbol":"flx","name":"Felix"},{"id":"fellaz","symbol":"flz","name":"Fellaz"},{"id":"fenerbahce-token","symbol":"fb","name":"Fenerbahçe"},{"id":"fenomy","symbol":"fenomy","name":"Fenomy"},{"id":"ferma","symbol":"ferma","name":"Ferma"},{"id":"ferro","symbol":"fer","name":"Ferro"},{"id":"ferrum-network","symbol":"frm","name":"Ferrum Network"},{"id":"fertilizer","symbol":"frt","name":"Fertilizer"},{"id":"festa-finance","symbol":"ffa","name":"Festa Finance"},{"id":"fetch-1dbdbfe5-2eb9-46c9-81dc-ecca4fa884a7","symbol":"fetch","name":"Fetch"},{"id":"fetch-ai","symbol":"fet","name":"Fetch.ai"},{"id":"feyorra","symbol":"fey","name":"Feyorra"},{"id":"fgdswap","symbol":"fgds","name":"FGDSwap"},{"id":"fiat-dao-token","symbol":"fdt","name":"Fiat DAO"},{"id":"fibodex","symbol":"fibo","name":"FiboDex"},{"id":"fibos","symbol":"fo","name":"FIBOS"},{"id":"fibo-token","symbol":"fibo","name":"FibSwap DEX"},{"id":"fidance","symbol":"fdc","name":"Fidance"},{"id":"fidelis","symbol":"fdls","name":"FIDELIS"},{"id":"fidira","symbol":"fid","name":"Fidira"},{"id":"fidlecoin","symbol":"fidle","name":"Fidlecoin"},{"id":"fidu","symbol":"fidu","name":"Fidu"},{"id":"fief","symbol":"fief","name":"Fief"},{"id":"fifa-inu","symbol":"finu","name":"Fifa Inu"},{"id":"fifa-laeeb","symbol":"laeeb","name":"FIFA Laeeb"},{"id":"fifasport","symbol":"ffs","name":"FiFaSport"},{"id":"fiftyonefifty","symbol":"fifty","name":"FIFTYONEFIFTY"},{"id":"fight-4-hope","symbol":"f4h","name":"Fight 4 Hope"},{"id":"fight-of-the-ages","symbol":"fota","name":"Fight Of The Ages"},{"id":"fight-win-ai","symbol":"fwin-ai","name":"Fight Win AI"},{"id":"figure-dao","symbol":"fdao","name":"Figure DAO"},{"id":"filda","symbol":"filda","name":"Filda"},{"id":"filecash","symbol":"fic","name":"Filecash"},{"id":"filecoin","symbol":"fil","name":"Filecoin"},{"id":"filecoin-standard-full-hashrate","symbol":"sfil","name":"Filecoin Standard Full Hashrate"},{"id":"fileshare-platform","symbol":"fsc","name":"Fileshare Platform"},{"id":"filestar","symbol":"star","name":"FileStar"},{"id":"filipcoin","symbol":"fcp","name":"Filipcoin"},{"id":"film-coin","symbol":"fliks","name":"Film Coin"},{"id":"filmcredits","symbol":"film","name":"FILMCredits"},{"id":"final-frontier","symbol":"frnt","name":"Final Frontier"},{"id":"finance-ai","symbol":"financeai","name":"Finance AI"},{"id":"finance-blocks","symbol":"fbx","name":"Finance Blocks"},{"id":"finance-vote","symbol":"fvt","name":"Finance Vote"},{"id":"financie-token","symbol":"fnct","name":"Financie Token"},{"id":"findora","symbol":"fra","name":"Findora"},{"id":"finexbox-token","symbol":"fnb","name":"Finexbox"},{"id":"fingerprints","symbol":"prints","name":"FingerprintsDAO"},{"id":"finminity","symbol":"fmt","name":"Finminity"},{"id":"fino-dao","symbol":"fino","name":"FINO DAO"},{"id":"fins-token","symbol":"fins","name":"Fins"},{"id":"fintoken","symbol":"ftc","name":"FinToken"},{"id":"fintropy","symbol":"fint","name":"Fintropy"},{"id":"fintrux","symbol":"ftx","name":"FintruX"},{"id":"finxflo","symbol":"fxf","name":"FINXFLO"},{"id":"fio-protocol","symbol":"fio","name":"FIO Protocol"},{"id":"fira","symbol":"fira","name":"FIRA"},{"id":"fira-cronos","symbol":"fira","name":"Defira (Cronos)"},{"id":"fireal","symbol":"frl","name":"Fireal"},{"id":"fireants","symbol":"ants","name":"FireAnts"},{"id":"fireball-2","symbol":"fire","name":"FireBall"},{"id":"firebird-aggregator","symbol":"fba","name":"Firebird Aggregator"},{"id":"firebot","symbol":"fbx","name":"FireBot"},{"id":"fireflame-inu","symbol":"fire","name":"FireFlame Inu"},{"id":"fire-lotto","symbol":"flot","name":"Fire Lotto"},{"id":"fire-protocol","symbol":"fire","name":"Fire Protocol"},{"id":"firerocket","symbol":"firerocket","name":"FireRocket"},{"id":"firestarter","symbol":"flame","name":"FireStarter"},{"id":"firetoken","symbol":"fire","name":"Firework Games"},{"id":"fire-token-2","symbol":"fire","name":"Fire"},{"id":"firmachain","symbol":"fct","name":"Firmachain"},{"id":"first-ever-nft","symbol":"fen","name":"First Ever NFT"},{"id":"firsthare","symbol":"firsthare","name":"FirstHare"},{"id":"firulais-wallet-token","symbol":"fiwt","name":"Firulais Wallet"},{"id":"fisco","symbol":"fscc","name":"FISCO Coin"},{"id":"fish-crypto","symbol":"fico","name":"Fish Crypto"},{"id":"fistbump","symbol":"fist","name":"Fistbump"},{"id":"fitmax","symbol":"fitm","name":"FitMax"},{"id":"fitmint","symbol":"fitt","name":"Fitmint"},{"id":"fitr-metaverse-token","symbol":"fmt","name":"FitR Metaverse Token"},{"id":"fitr-social-token","symbol":"fst","name":"FitR Social Token"},{"id":"fit-token","symbol":"fit","name":"FIT"},{"id":"fivekm-kmt","symbol":"kmt","name":"FiveKM KMT"},{"id":"fix00","symbol":"fix00","name":"Fix00"},{"id":"flag-media","symbol":"flag","name":"Flag Media"},{"id":"flag-network","symbol":"flag","name":"Flag Network"},{"id":"flamengo-fan-token","symbol":"mengo","name":"Flamengo Fan Token"},{"id":"flamingghost","symbol":"fghst","name":"FlamingGhost"},{"id":"flamingo-finance","symbol":"flm","name":"Flamingo Finance"},{"id":"flare-finance","symbol":"exfi","name":"Flare Finance"},{"id":"flare-networks","symbol":"flr","name":"Flare"},{"id":"flare-token","symbol":"1flr","name":"Flare Token"},{"id":"flash-stake","symbol":"flash","name":"Flashstake"},{"id":"flash-token","symbol":"flash","name":"Flash Loans"},{"id":"flash-token-2","symbol":"flash","name":"Flash"},{"id":"flatqube","symbol":"qube","name":"FlatQube"},{"id":"fleta","symbol":"fleta","name":"FLETA"},{"id":"flex-coin","symbol":"flex","name":"FLEX Coin"},{"id":"flexq","symbol":"flq","name":"FlexQ"},{"id":"flex-usd","symbol":"flexusd","name":"flexUSD"},{"id":"flightclupcoin","symbol":"flight","name":"FlightClupcoin"},{"id":"flits","symbol":"fls","name":"Flits"},{"id":"flixxo","symbol":"flixx","name":"Flixxo"},{"id":"float-protocol","symbol":"bank","name":"Float Protocol"},{"id":"flock","symbol":"flock","name":"Flock"},{"id":"floki","symbol":"floki","name":"FLOKI"},{"id":"flokibonk","symbol":"flobo","name":"FlokiBonk"},{"id":"floki-ceo","symbol":"flokiceo","name":"FLOKI CEO"},{"id":"floki-ceo-coin","symbol":"fcc","name":"Floki CEO Coin"},{"id":"flokicoke","symbol":"flokicoke","name":"FlokiCoke"},{"id":"flokidash","symbol":"flokidash","name":"FlokiDash"},{"id":"flokimooni","symbol":"flokim","name":"Flokimooni"},{"id":"floki-musk","symbol":"floki","name":"Floki Musk"},{"id":"floki-rocket","symbol":"rloki","name":"Floki Rocket"},{"id":"floki-santa","symbol":"flokisanta","name":"Floki Santa"},{"id":"floki-shiba-pepe-ceo","symbol":"3ceo","name":"FLOKI SHIBA PEPE CEO"},{"id":"flokiswap","symbol":"flokis","name":"FlokiSwap"},{"id":"flokiter-ai","symbol":"fai","name":"FlokiTer"},{"id":"flona","symbol":"flona","name":"Flona"},{"id":"floof","symbol":"floof","name":"FLOOF"},{"id":"floordao","symbol":"floor","name":"FloorDAO"},{"id":"florachain-yield-token","symbol":"fyt","name":"FloraChain"},{"id":"florin","symbol":"xfl","name":"Florin"},{"id":"floshido-inu","symbol":"floshido","name":"FLOSHIDO INU"},{"id":"floshin","symbol":"floshin","name":"Floshin"},{"id":"flourishing-ai-token","symbol":"ai","name":"Flourishing AI"},{"id":"flow","symbol":"flow","name":"Flow"},{"id":"flowchaincoin","symbol":"flc","name":"Flowchain"},{"id":"floyx-new","symbol":"floyx","name":"Floyx"},{"id":"fluffy-coin","symbol":"fluf","name":"Fluffy Coin"},{"id":"fluffy-token","symbol":"$fluffy","name":"Fluffy Token"},{"id":"fluid-dai","symbol":"fdai","name":"Fluid DAI"},{"id":"fluidfi","symbol":"fluid","name":"FluidFi"},{"id":"fluid-frax","symbol":"ffrax","name":"Fluid FRAX"},{"id":"fluid-tusd","symbol":"ftusd","name":"Fluid TUSD"},{"id":"fluid-usdc","symbol":"fusdc","name":"Fluid USDC"},{"id":"fluid-usdt","symbol":"fusdt","name":"Fluid USDT"},{"id":"fluminense-fc-fan-token","symbol":"flu","name":"Fluminense FC Fan Token"},{"id":"flurry","symbol":"flurry","name":"Flurry Finance"},{"id":"flute","symbol":"flut","name":"Flute"},{"id":"fluus","symbol":"fluus","name":"FLUUS"},{"id":"flux","symbol":"flux","name":"Datamine FLUX"},{"id":"flux-dai","symbol":"fdai","name":"Flux DAI"},{"id":"flux-frax","symbol":"ffrax","name":"Flux FRAX"},{"id":"flux-protocol","symbol":"flux","name":"Flux Protocol"},{"id":"flux-token","symbol":"flx","name":"SEDA Protocol"},{"id":"flux-usdc","symbol":"fusdc","name":"Flux USDC"},{"id":"flux-usdt","symbol":"fusdt","name":"Flux USDT"},{"id":"flycoin-fly","symbol":"fly","name":"Flycoin FLY"},{"id":"flypaper","symbol":"sticky","name":"FlyPaper"},{"id":"flypme","symbol":"fyp","name":"FlypMe"},{"id":"fmoney-finance","symbol":"fmon","name":"FMONEY FINANCE"},{"id":"fncy","symbol":"fncy","name":"FNCY"},{"id":"fndz-token","symbol":"fndz","name":"FNDZ"},{"id":"fnkcom","symbol":"fnk","name":"Fnk.com"},{"id":"foam-protocol","symbol":"foam","name":"FOAM"},{"id":"fodl-finance","symbol":"fodl","name":"Fodl Finance"},{"id":"foho-coin","symbol":"foho","name":"Foho Coin"},{"id":"foincoin","symbol":"foin","name":"Foin"},{"id":"folder-protocol","symbol":"fol","name":"Folder Protocol"},{"id":"follow-token","symbol":"folo","name":"Alpha Impact"},{"id":"fomo-baby","symbol":"fomobaby","name":"FOMO BABY"},{"id":"fomobsc","symbol":"fomo","name":"FomoBSC"},{"id":"fone","symbol":"fone","name":"Fone"},{"id":"fonsmartchain","symbol":"fon","name":"FONSmartChain"},{"id":"font","symbol":"font","name":"Font"},{"id":"food-bank","symbol":"food","name":"Food Bank"},{"id":"foodchain-global","symbol":"food","name":"FoodChain Global"},{"id":"football-coin","symbol":"xfc","name":"Football Coin"},{"id":"football-decentralized","symbol":"fbd","name":"Football Decentralized"},{"id":"footballfanapp","symbol":"fnc","name":"FanCoin®"},{"id":"footballstars","symbol":"fts","name":"FootballStars"},{"id":"football-world-community","symbol":"fwc","name":"Football World Community"},{"id":"force-bridge-usdc","symbol":"usdc","name":"Force Bridge USDC"},{"id":"forcecowboy","symbol":"fcb","name":"ForceCowBoy"},{"id":"force-of-nature","symbol":"fon","name":"Force of Nature"},{"id":"force-protocol","symbol":"for","name":"ForTube"},{"id":"forefront","symbol":"ff","name":"Forefront"},{"id":"forest-knight","symbol":"knight","name":"Forest Knight"},{"id":"forestry","symbol":"fry","name":"Forestry"},{"id":"foreverblast","symbol":"feb","name":"ForeverBlast"},{"id":"forever-burn","symbol":"fburn","name":"Forever Burn"},{"id":"forever-shiba","symbol":"4shiba","name":"FOREVER SHIBA"},{"id":"forexcoin","symbol":"forex","name":"FOREXCOIN"},{"id":"forge-finance","symbol":"forge","name":"Forge Finance"},{"id":"for-loot-and-glory","symbol":"flag","name":"For Loot And Glory"},{"id":"formation-fi","symbol":"form","name":"Formation FI"},{"id":"formula-inu","symbol":"finu","name":"Formula Inu"},{"id":"forta","symbol":"fort","name":"Forta"},{"id":"forthbox","symbol":"fbx","name":"ForthBox"},{"id":"fortknoxter","symbol":"fkx","name":"FortKnoxster"},{"id":"fortress","symbol":"fts","name":"Fortress Loans"},{"id":"fortressdao","symbol":"fort","name":"Fortress"},{"id":"fortuna-sittard-fan-token","symbol":"for","name":"Fortuna Sittard Fan Token"},{"id":"fortune","symbol":"fortune","name":"Fortune"},{"id":"fortune-cookie","symbol":"fct","name":"Fortune Cookie"},{"id":"fortuneum","symbol":"fortune","name":"FORTUNEUM"},{"id":"forus","symbol":"fors","name":"Forus"},{"id":"foundation","symbol":"fnd","name":"Foundation"},{"id":"fountain-protocol","symbol":"ftp","name":"Fountain Protocol"},{"id":"fox-financev2","symbol":"fox","name":"Fox Finance V2"},{"id":"foxgirl","symbol":"foxgirl","name":"FoxGirl"},{"id":"fox-trading-token","symbol":"foxt","name":"Fox Trading"},{"id":"foxy-equilibrium","symbol":"foxy","name":"Foxy Equilibrium"},{"id":"fozeus-coin","symbol":"fzs","name":"Fozeus Coin"},{"id":"fractal","symbol":"fcl","name":"Fractal"},{"id":"fraction","symbol":"fraction","name":"Fraction"},{"id":"fractionalized-smb-2367","symbol":"daojones","name":"Fractionalized SMB-2367"},{"id":"fractionalized-wave-999","symbol":"wav","name":"Fractionalized WAVE-999"},{"id":"fracton-protocol","symbol":"ft","name":"Fracton Protocol"},{"id":"fragments-of-arker","symbol":"foa","name":"Fragments of Arker"},{"id":"fragmint","symbol":"frag","name":"Fragmint"},{"id":"frakt-token","symbol":"frkt","name":"FRAKT"},{"id":"france-fan-token","symbol":"fra","name":"France Fan Token"},{"id":"france-rev-finance","symbol":"frf","name":"FRANCE REV FINANCE"},{"id":"frank-inu","symbol":"frank","name":"Frank Inu"},{"id":"franklin","symbol":"fly","name":"Franklin"},{"id":"frax","symbol":"frax","name":"Frax"},{"id":"frax-ether","symbol":"frxeth","name":"Frax Ether"},{"id":"frax-price-index","symbol":"fpi","name":"Frax Price Index"},{"id":"frax-price-index-share","symbol":"fpis","name":"Frax Price Index Share"},{"id":"frax-share","symbol":"fxs","name":"Frax Share"},{"id":"fredenergy","symbol":"fred","name":"FRED Energy"},{"id":"freebie-life-finance","symbol":"frb","name":"Freebie Life Finance"},{"id":"freecash","symbol":"fch","name":"Freecash"},{"id":"freechat","symbol":"fcc","name":"Freechat"},{"id":"freedomcoin","symbol":"freed","name":"Freedomcoin"},{"id":"freedom-coin","symbol":"free","name":"FREEdom coin"},{"id":"freedom-god-dao","symbol":"fgd","name":"Freedom God DAO"},{"id":"freedom-jobs-business","symbol":"$fjb","name":"Freedom. Jobs. Business"},{"id":"freedom-reserve","symbol":"fr","name":"Freedom Reserve"},{"id":"freela","symbol":"frel","name":"Freela"},{"id":"freemoon-binance","symbol":"fmb","name":"FREEMOON BINANCE"},{"id":"freerossdao","symbol":"free","name":"FreeRossDAO"},{"id":"free-speech","symbol":"1amd","name":"Free Speech"},{"id":"freeway","symbol":"fwt","name":"Freeway"},{"id":"freicoin","symbol":"frc","name":"Freicoin"},{"id":"fren","symbol":"fren","name":"FREN"},{"id":"frenchain","symbol":"fren","name":"FrenChain"},{"id":"french-connection-finance","symbol":"fcf","name":"French Connection Finance"},{"id":"french-digital-reserve","symbol":"fdr","name":"French Digital Reserve"},{"id":"frencoin","symbol":"fren","name":"FrenCoin"},{"id":"freqai","symbol":"freqai","name":"FREQAI"},{"id":"freth","symbol":"freth","name":"frETH"},{"id":"freyala","symbol":"xya","name":"GameFi Crossing"},{"id":"friends-with-benefits-pro","symbol":"fwb","name":"Friends With Benefits Pro"},{"id":"friendz","symbol":"fdz","name":"Friendz"},{"id":"fringe-finance","symbol":"frin","name":"Fringe Finance"},{"id":"frog-ceo","symbol":"frog ceo","name":"FROG CEO"},{"id":"froge-finance","symbol":"frogex","name":"FrogeX"},{"id":"froggies-token","symbol":"frgst","name":"Froggies"},{"id":"frog-inu","symbol":"fgi","name":"Frog Inu"},{"id":"frogswap","symbol":"frog","name":"FrogSwap"},{"id":"fronk","symbol":"fronk","name":"Fronk"},{"id":"frontfanz","symbol":"fanz","name":"FrontFanz"},{"id":"frontier-token","symbol":"front","name":"Frontier"},{"id":"front-row","symbol":"frr","name":"Frontrow"},{"id":"froyo-games","symbol":"froyo","name":"Froyo Games"},{"id":"frozentomb","symbol":"ftomb","name":"Frozentomb"},{"id":"frozen-walrus-share","symbol":"wshare","name":"Frozen Walrus Share"},{"id":"fruits","symbol":"frts","name":"Fruits"},{"id":"fruits-of-ryoshi","symbol":"yuzu","name":"Fruits of Ryoshi"},{"id":"frutti-dino","symbol":"fdt","name":"Frutti Dino"},{"id":"frz-solar-system","symbol":"frzss","name":"Frz Solar System"},{"id":"frzswap","symbol":"frzw","name":"FRZSwap"},{"id":"fsn","symbol":"fsn","name":"FUSION"},{"id":"fsw-token","symbol":"fsw","name":"Falconswap"},{"id":"ftdex","symbol":"ftd","name":"FTDex"},{"id":"ftm-guru","symbol":"elite","name":"ftm.guru"},{"id":"ftmlaunch","symbol":"ftml","name":"FTMlaunch"},{"id":"ftribe-fighters","symbol":"f2c","name":"Ftribe Fighters"},{"id":"ftx-token","symbol":"ftt","name":"FTX"},{"id":"ftx-users-debt","symbol":"fud","name":"FTX Users' Debt"},{"id":"ftx-wormhole","symbol":"ftt","name":"FTX (Wormhole)"},{"id":"fuc","symbol":"fuc","name":"FUBT Token"},{"id":"fuel-network","symbol":"fuel","name":"Fuel Network"},{"id":"fufu","symbol":"fufu","name":"Fufu"},{"id":"fuji","symbol":"fuji","name":"Fuji"},{"id":"fujitoken","symbol":"fjt","name":"Fuji FJT"},{"id":"fulcrom","symbol":"ful","name":"Fulcrom"},{"id":"fumoney","symbol":"fum","name":"FUMoney"},{"id":"fund-of-yours","symbol":"foy","name":"Fund Of Yours"},{"id":"funex","symbol":"funex","name":"Funex"},{"id":"funfair","symbol":"fun","name":"FUNToken"},{"id":"funfi","symbol":"fnf","name":"FunFi"},{"id":"fungie-dao","symbol":"fng","name":"Fungie DAO"},{"id":"furio","symbol":"$fur","name":"Furio"},{"id":"furucombo","symbol":"combo","name":"Furucombo"},{"id":"fuse-dollar","symbol":"fusd","name":"Fuse Dollar"},{"id":"fusefi","symbol":"volt","name":"Voltage Finance"},{"id":"fuse-network-token","symbol":"fuse","name":"Fuse"},{"id":"fusotao","symbol":"tao","name":"Fusotao"},{"id":"future","symbol":"ftr","name":"Future"},{"id":"future-ai","symbol":"future-ai","name":"Future AI"},{"id":"futurecoin","symbol":"future","name":"FutureCoin"},{"id":"future-of-fintech","symbol":"fof","name":"Future Of Fintech"},{"id":"futurescoin","symbol":"fc","name":"FuturesCoin"},{"id":"futureswap","symbol":"fst","name":"Futureswap"},{"id":"futureswap-finance","symbol":"fs","name":"FutureSwap Finance"},{"id":"futurocoin","symbol":"fto","name":"FuturoCoin"},{"id":"fuze-token","symbol":"fuze","name":"FUZE"},{"id":"fuzz-finance","symbol":"fuzz","name":"Fuzz Finance"},{"id":"fx-coin","symbol":"fx","name":"Function X"},{"id":"fydcoin","symbol":"fyd","name":"FYDcoin"},{"id":"fyooz","symbol":"fyz","name":"Fyooz"},{"id":"g","symbol":"g*","name":"G*"},{"id":"g999","symbol":"g999","name":"G999"},{"id":"gabecoin","symbol":"gabecoin","name":"Gabecoin"},{"id":"gabur","symbol":"gbr","name":"Gabur"},{"id":"gacube","symbol":"gac","name":"GACUBE"},{"id":"gadget-war","symbol":"gwar","name":"Gadget War"},{"id":"gafa","symbol":"gafa","name":"Gafa"},{"id":"gagarin","symbol":"ggr","name":"GAGARIN"},{"id":"gaia-everworld","symbol":"gaia","name":"Gaia Everworld"},{"id":"gainfull","symbol":"gfull","name":"Gainfull"},{"id":"gains","symbol":"gains","name":"Gains"},{"id":"gains-farm","symbol":"gfarm2","name":"Gains Farm"},{"id":"gains-network","symbol":"gns","name":"Gains Network"},{"id":"gaj","symbol":"gaj","name":"Gaj Finance"},{"id":"gala","symbol":"gala","name":"GALA"},{"id":"galactic-arena-the-nftverse","symbol":"gan","name":"Galactic Arena: The NFTverse"},{"id":"galatasaray-fan-token","symbol":"gal","name":"Galatasaray Fan Token"},{"id":"galaxia","symbol":"gxa","name":"Galaxia"},{"id":"galaxiaverse","symbol":"glxia","name":"GalaxiaVerse"},{"id":"galaxy-arena","symbol":"esnc","name":"Galaxy Arena Metaverse"},{"id":"galaxycoin","symbol":"galaxy","name":"GalaxyCoin"},{"id":"galaxy-doge","symbol":"galaxydoge","name":"Galaxy Doge"},{"id":"galaxy-essential","symbol":"gxe","name":"Galaxy Essential"},{"id":"galaxy-fight-club","symbol":"gcoin","name":"Galaxy Fight Club"},{"id":"galaxy-finance","symbol":"gft","name":"Galaxy Finance"},{"id":"galaxy-finance-glf","symbol":"glf","name":"Galaxy Finance GLF"},{"id":"galaxy-heroes-coin","symbol":"ghc","name":"Galaxy Heroes Coin [OLD]"},{"id":"galaxy-villains","symbol":"gvc","name":"Galaxy Villains"},{"id":"galaxy-war","symbol":"gwt","name":"Galaxy War"},{"id":"gale-network","symbol":"gale","name":"Gale Network"},{"id":"galeon","symbol":"galeon","name":"Galeon"},{"id":"galileo","symbol":"gali","name":"Galileo"},{"id":"galvan","symbol":"ize","name":"Galvan"},{"id":"gamb","symbol":"gmb","name":"GAMB"},{"id":"gambler-shiba","symbol":"gshiba","name":"Gambler Shiba"},{"id":"game","symbol":"gtc","name":"Game"},{"id":"game-ace-token","symbol":"gat","name":"Game Ace"},{"id":"gameantz","symbol":"antz","name":"GameAntz"},{"id":"game-coin","symbol":"gmex","name":"Game Coin"},{"id":"gamecredits","symbol":"game","name":"GameCredits"},{"id":"gamee","symbol":"gmee","name":"GAMEE"},{"id":"gamefantasystar","symbol":"gfs","name":"GameFantasyStar"},{"id":"game-fantasy-token","symbol":"gft","name":"Game Fantasy"},{"id":"gamefi","symbol":"gafi","name":"GameFi"},{"id":"gamefi-token","symbol":"gfi","name":"GameFi Protocol"},{"id":"gameflip","symbol":"flp","name":"Gameflip"},{"id":"gameguru","symbol":"ggt","name":"GameGuru"},{"id":"game-of-dragons","symbol":"god","name":"Game of Dragons"},{"id":"gameology","symbol":"gmy","name":"Gameology"},{"id":"gamer","symbol":"gmr","name":"GAMER"},{"id":"gamer-arena","symbol":"gau","name":"Gamer Arena"},{"id":"gamercoin","symbol":"ghx","name":"GamerCoin"},{"id":"gamerse","symbol":"lfg","name":"Gamerse"},{"id":"gamesafe","symbol":"gamesafe","name":"Gamesafe"},{"id":"games-for-a-living","symbol":"gfal","name":"Games for a living"},{"id":"gamespad","symbol":"gmpd","name":"GamesPad"},{"id":"gamestar-exchange","symbol":"gms","name":"GameStar"},{"id":"gamestarplus","symbol":"gstar","name":"GameStarPlus"},{"id":"gamestarter","symbol":"game","name":"Gamestarter"},{"id":"gamestation","symbol":"gamer","name":"GameStation"},{"id":"gamestop-finance","symbol":"gme","name":"GameStop Finance"},{"id":"gamestop-tokenized-stock-defichain","symbol":"dgme","name":"GameStop Tokenized Stock Defichain"},{"id":"gameswap-org","symbol":"gswap","name":"Gameswap"},{"id":"game-tree","symbol":"gtcoin","name":"Game Tree"},{"id":"game-x-change-potion","symbol":"gxp","name":"Game X Change Potion"},{"id":"gameyoo","symbol":"gyc","name":"GameYoo"},{"id":"gamezone","symbol":"gzone","name":"GameZone"},{"id":"gami","symbol":"gami","name":"Gami"},{"id":"gamifi","symbol":"gmi","name":"GamiFi"},{"id":"gaming-doge","symbol":"gamingdoge","name":"Gaming Doge"},{"id":"gamingshiba","symbol":"gamingshiba","name":"GamingShiba"},{"id":"gaming-stars","symbol":"games","name":"Gaming Stars"},{"id":"gamium","symbol":"gmm","name":"Gamium"},{"id":"gami-world","symbol":"gami","name":"GAMI World"},{"id":"gamma-strategies","symbol":"gamma","name":"Gamma Strategies"},{"id":"gammaswap","symbol":"","name":"GammaSwap"},{"id":"gamyfi-token","symbol":"gfx","name":"GamyFi"},{"id":"gandercoin","symbol":"gand","name":"GanderCoin"},{"id":"gangs-rabbit","symbol":"rabbit","name":"Gangs Rabbit"},{"id":"gapcoin","symbol":"gap","name":"Gapcoin"},{"id":"garbi-protocol","symbol":"grb","name":"Garbi Protocol"},{"id":"gard","symbol":"gard","name":"GARD"},{"id":"gari-network","symbol":"gari","name":"Gari Network"},{"id":"garlicoin","symbol":"grlc","name":"Garlicoin"},{"id":"gary","symbol":"gary","name":"Gary"},{"id":"gas","symbol":"gas","name":"Gas"},{"id":"gasblock","symbol":"gsbl","name":"GasBlock"},{"id":"gas-dao","symbol":"gas","name":"Gas DAO"},{"id":"gasp","symbol":"gasp","name":"gAsp"},{"id":"gatechain-token","symbol":"gt","name":"Gate"},{"id":"gatenet","symbol":"gate","name":"GATENet"},{"id":"gateway-protocol","symbol":"gwp","name":"Gateway Protocol"},{"id":"gather","symbol":"gth","name":"Gather"},{"id":"gatorswap","symbol":"gator","name":"GatorSwap"},{"id":"gatsby-inu-2","symbol":"gatsby","name":"Gatsby Inu"},{"id":"gaur-money","symbol":"gaur","name":"Gaur Money"},{"id":"gax-liquidity-token-reward","symbol":"gltr","name":"GAX Liquidity Token Reward"},{"id":"gazetv","symbol":"gaze","name":"GazeTV"},{"id":"gaziantep-fk-fan-token","symbol":"gfk","name":"Gaziantep FK Fan Token"},{"id":"gcn-coin","symbol":"gcn","name":"GCN Coin"},{"id":"gdrt","symbol":"gdrt","name":"GDRT"},{"id":"gdx-token","symbol":"gdx","name":"Gridex"},{"id":"gear","symbol":"gear","name":"Gear"},{"id":"gearbox","symbol":"gear","name":"Gearbox"},{"id":"gecoin","symbol":"gec","name":"Gecoin"},{"id":"geegoopuzzle","symbol":"ggp","name":"Geegoopuzzle"},{"id":"geek-protocol","symbol":"geek","name":"Geek Protocol"},{"id":"geeq","symbol":"geeq","name":"GEEQ"},{"id":"geist-dai","symbol":"gdai","name":"Geist Dai"},{"id":"geist-eth","symbol":"geth","name":"Geist ETH"},{"id":"geist-finance","symbol":"geist","name":"Geist Finance"},{"id":"geist-ftm","symbol":"gftm","name":"Geist FTM"},{"id":"geist-fusdt","symbol":"gfusdt","name":"Geist fUSDT"},{"id":"geist-usdc","symbol":"gusdc","name":"Geist USDC"},{"id":"geist-wbtc","symbol":"gwbtc","name":"Geist WBTC"},{"id":"gelato","symbol":"gel","name":"Gelato"},{"id":"geld-finance","symbol":"geldf","name":"GELD Finance"},{"id":"gemcave-token","symbol":"gems","name":"GemCave Token"},{"id":"gemdao","symbol":"gemdao","name":"Gemdao"},{"id":"gem-exchange-and-trading","symbol":"gxt","name":"Gem Exchange and Trading"},{"id":"gemguardian","symbol":"gemg","name":"GemGuardian"},{"id":"gemholic","symbol":"gems","name":"Gemholic"},{"id":"gemhub","symbol":"ghub","name":"GemHUB"},{"id":"gemie","symbol":"gem","name":"Gemie"},{"id":"gemini-dollar","symbol":"gusd","name":"Gemini Dollar"},{"id":"gemlink","symbol":"glink","name":"GemLink"},{"id":"gempad","symbol":"gems","name":"GemPad"},{"id":"gempay","symbol":"gpay","name":"GemPay"},{"id":"gems-2","symbol":"gem","name":"Gems"},{"id":"gemswap-2","symbol":"zgem","name":"GemSwap"},{"id":"gemuni","symbol":"geni","name":"GemUni"},{"id":"gemx","symbol":"gemx","name":"GEMX"},{"id":"genaro-network","symbol":"gnx","name":"Genaro Network"},{"id":"genart","symbol":"genart","name":"GENART"},{"id":"genclerbirligi-fan-token","symbol":"gbsk","name":"Gençlerbirliği Fan Token"},{"id":"gene","symbol":"gene","name":"Gene"},{"id":"generaitiv","symbol":"gai","name":"Generaitiv"},{"id":"generation","symbol":"gen","name":"Generation"},{"id":"genesis-defi","symbol":"genf","name":"Genesis Defi"},{"id":"genesis-finance","symbol":"gefi","name":"Genesis Finance"},{"id":"genesis-mana","symbol":"mana","name":"Genesis Mana"},{"id":"genesis-particle","symbol":"gp","name":"Genesis Particle"},{"id":"genesis-pool","symbol":"gpool","name":"Genesis Pool"},{"id":"genesis-shards","symbol":"gs","name":"Genesis Shards"},{"id":"genesis-universe","symbol":"gut","name":"Genesis Universe"},{"id":"genesis-vision","symbol":"gvt","name":"Genesis Vision"},{"id":"genesis-wink","symbol":"gwink","name":"Genesis Wink"},{"id":"genesis-worlds","symbol":"genesis","name":"Genesis Worlds"},{"id":"genesisx","symbol":"xgs","name":"GenesisX"},{"id":"genesysgo-shadow","symbol":"shdw","name":"Shadow Token"},{"id":"genesys-token","symbol":"gsys","name":"Genesys Token"},{"id":"genie-protocol","symbol":"gnp","name":"Genie Protocol"},{"id":"genius","symbol":"geni","name":"Genius"},{"id":"genius-yield","symbol":"gens","name":"Genius Yield"},{"id":"geniux","symbol":"iux","name":"GeniuX"},{"id":"genix","symbol":"genix","name":"Genix"},{"id":"genomesdao","symbol":"$gene","name":"GenomesDAO"},{"id":"genopet-ki","symbol":"ki","name":"Genopets KI"},{"id":"genopets","symbol":"gene","name":"Genopets"},{"id":"genshinflokiinu","symbol":"gfloki","name":"GenshinFlokiInu"},{"id":"genshiro","symbol":"gens","name":"Genshiro"},{"id":"gensokishis-metaverse","symbol":"mv","name":"GensoKishi Metaverse"},{"id":"genz-token","symbol":"genz","name":"GENZ Token"},{"id":"geocoin","symbol":"geo","name":"Geocoin"},{"id":"geodb","symbol":"geo","name":"GeoDB"},{"id":"geojam","symbol":"jam","name":"Geojam"},{"id":"geopoly","symbol":"geo$","name":"Geopoly"},{"id":"gera-coin","symbol":"gera","name":"Gera Coin"},{"id":"germany-rabbit-token","symbol":"germany","name":"Germany Rabbit Token"},{"id":"gerowallet","symbol":"gero","name":"GeroWallet"},{"id":"get","symbol":"get","name":"GET"},{"id":"getkicks","symbol":"kicks","name":"GetKicks"},{"id":"get-token","symbol":"get","name":"GET Protocol"},{"id":"geuro","symbol":"geuro","name":"GEURO"},{"id":"geyser","symbol":"gysr","name":"Geyser"},{"id":"geysercoin","symbol":"gsr","name":"GeyserCoin"},{"id":"gforce","symbol":"gfce","name":"GFORCE"},{"id":"ggtkn","symbol":"ggtkn","name":"GGTKN"},{"id":"gg-token","symbol":"ggtk","name":"GG"},{"id":"ghospers-game","symbol":"ghsp","name":"Ghospers Game"},{"id":"ghost-by-mcafee","symbol":"ghost","name":"Ghost"},{"id":"ghostface","symbol":"ghostface","name":"Ghostface"},{"id":"ghostkids","symbol":"boo","name":"GhostKids"},{"id":"ghostmarket","symbol":"gm","name":"GhostMarket"},{"id":"ghost-trader-5867bf90-0523-4432-80b3-2c19f84ebf8d","symbol":"gtr","name":"Ghost Trader "},{"id":"ghoul-token","symbol":"ghoul","name":"Ghoul"},{"id":"giannidoge-esport","symbol":"gde","name":"GianniDoge Esport"},{"id":"giant-mammoth","symbol":"gmmt","name":"Giant Mammoth"},{"id":"gibx-swap","symbol":"x","name":"GIBX Swap"},{"id":"gictrade","symbol":"gict","name":"GICTrade"},{"id":"giddy","symbol":"gddy","name":"Giddy"},{"id":"gif-dao","symbol":"gif","name":"GIF DAO"},{"id":"gift-coin","symbol":"gift","name":"Gift Coin"},{"id":"giftedhands","symbol":"ghd","name":"Giftedhands"},{"id":"gifto","symbol":"gft","name":"Gifto"},{"id":"gigaswap","symbol":"giga","name":"GigaSwap"},{"id":"gigecoin","symbol":"gig","name":"GigEcoin"},{"id":"gimmer","symbol":"gmr","name":"Gimmer"},{"id":"ginoa","symbol":"ginoa","name":"Ginoa"},{"id":"ginspirit","symbol":"ginspirit","name":"GinSpirit"},{"id":"ginza-network","symbol":"ginza","name":"Ginza Network"},{"id":"giresunspor-token","symbol":"grs","name":"Giresunspor Token"},{"id":"gitcoin","symbol":"gtc","name":"Gitcoin"},{"id":"gitcoin-staked-eth-index","symbol":"gtceth","name":"Gitcoin Staked ETH Index"},{"id":"gitshock-finance","symbol":"gtfx","name":"Gitshock Finance"},{"id":"giveth","symbol":"giv","name":"Giveth"},{"id":"givewell-inu","symbol":"ginu","name":"Givewell Inu"},{"id":"givingtoservices-svs","symbol":"svs","name":"GivingToServices SVS"},{"id":"gizadao","symbol":"giza","name":"GizaDao"},{"id":"gld-tokenized-stock-defichain","symbol":"dgld","name":"SPDR Gold Shares Defichain"},{"id":"gleec-coin","symbol":"gleec","name":"Gleec Coin"},{"id":"glex","symbol":"glex","name":"GLEX"},{"id":"glide-finance","symbol":"glide","name":"Glide Finance"},{"id":"glitch-protocol","symbol":"glch","name":"Glitch Protocol"},{"id":"glitter-finance","symbol":"xgli","name":"Glitter Finance"},{"id":"glitzkoin","symbol":"gtn","name":"GlitzKoin"},{"id":"globalboost","symbol":"bsty","name":"GlobalBoost-Y"},{"id":"globalchainz","symbol":"gcz","name":"GlobalChainZ"},{"id":"globalcoin","symbol":"glc","name":"GlobalCoin"},{"id":"global-coin-research","symbol":"gcr","name":"Global Coin Research"},{"id":"global-digital-cluster-co","symbol":"gdcc","name":"Global Digital Cluster Co"},{"id":"global-digital-content","symbol":"gdc","name":"Global Digital Content"},{"id":"global-human-trust","symbol":"ght","name":"Global Human Trust"},{"id":"global-innovative-solutions","symbol":"gsi","name":"Global Innovative Solutions"},{"id":"global-smart-asset","symbol":"gsa","name":"Global Smart Asset"},{"id":"global-social-chain","symbol":"gsc","name":"Global Social Chain"},{"id":"global-trading-xenocurren","symbol":"gtx","name":"Global Trading Xenocurrency"},{"id":"globe-derivative-exchange","symbol":"gdt","name":"Globe Derivative Exchange"},{"id":"globiance-exchange","symbol":"gbex","name":"Globiance Exchange"},{"id":"glo-dollar","symbol":"usdglo","name":"Glo Dollar"},{"id":"glosfer-token","symbol":"glo","name":"Glosfer"},{"id":"glouki","symbol":"glk","name":"Glouki"},{"id":"glove","symbol":"glo","name":"Glove"},{"id":"glow-token-8fba1e9e-5643-47b4-8fef-d0eef67af854","symbol":"glow","name":"Glow Token"},{"id":"gm","symbol":"gm","name":"GM"},{"id":"gmcoin-2","symbol":"gmcoin","name":"GMCoin"},{"id":"gmd-protocol","symbol":"gmd","name":"GMD"},{"id":"gmsol","symbol":"gmsol","name":"GMSOL"},{"id":"gmt-token","symbol":"gmt","name":"GMT Token"},{"id":"gmx","symbol":"gmx","name":"GMX"},{"id":"gn","symbol":"gn","name":"GN"},{"id":"gnft","symbol":"gnft","name":"GNFT"},{"id":"gnome","symbol":"$gnome","name":"GNOME"},{"id":"gnome-mines","symbol":"gmines","name":"Gnome Mines"},{"id":"gnome-mines-token-v2","symbol":"gminesv2","name":"Gnome Mines Token V2"},{"id":"gnosis","symbol":"gno","name":"Gnosis"},{"id":"gny","symbol":"gny","name":"GNY"},{"id":"go2e-otm","symbol":"otm","name":"GO2E OTM"},{"id":"go2e-token","symbol":"gte","name":"GO2E GTE"},{"id":"goal-champion","symbol":"gc","name":"Goal Champion"},{"id":"goal-token","symbol":"goal","name":"GOAL Token"},{"id":"goat-coin","symbol":"goat","name":"Goat Coin"},{"id":"goats","symbol":"goats","name":"GOATS"},{"id":"gobi-labs","symbol":"gobi","name":"Gobi Labs"},{"id":"goblin","symbol":"goblin","name":"Goblin"},{"id":"gobtc","symbol":"gobtc","name":"goBTC"},{"id":"gobyte","symbol":"gbx","name":"GoByte"},{"id":"gochain","symbol":"go","name":"GoChain"},{"id":"gocryptome","symbol":"gcme","name":"GoCryptoMe"},{"id":"gode-chain","symbol":"gode","name":"Gode Chain"},{"id":"gods-unchained","symbol":"gods","name":"Gods Unchained"},{"id":"godzilla","symbol":"godz","name":"Godzilla"},{"id":"goerli-eth","symbol":"geth","name":"Goerli ETH"},{"id":"goeth","symbol":"goeth","name":"goETH"},{"id":"gofitterai","symbol":"fitai","name":"GoFitterAI"},{"id":"gogocoin","symbol":"gogo","name":"GOGOcoin"},{"id":"gogo-finance","symbol":"gogo","name":"GOGO Finance"},{"id":"gogolcoin","symbol":"gol","name":"GogolCoin"},{"id":"goin","symbol":"goin","name":"GOIN"},{"id":"goku","symbol":"goku","name":"Goku"},{"id":"gokumarket-credit","symbol":"gmc","name":"GokuMarket Credit"},{"id":"golcoin","symbol":"golc","name":"GOLCOIN"},{"id":"gold8","symbol":"gold8","name":"GOLD8"},{"id":"goldario","symbol":"gld","name":"Goldario"},{"id":"goldcoin","symbol":"glc","name":"Goldcoin"},{"id":"goldefy","symbol":"god","name":"GoldeFy"},{"id":"golden-ball","symbol":"glb","name":"Golden Ball"},{"id":"goldendiamond9","symbol":"g9","name":"GoldenDiamond9"},{"id":"golden-doge","symbol":"gdoge","name":"Golden Doge"},{"id":"golden-goal","symbol":"gdg","name":"Golden Goal"},{"id":"golden-goose","symbol":"gold","name":"Golden Goose"},{"id":"golden-inu","symbol":"golden","name":"Golden Inu"},{"id":"golden-token","symbol":"gold","name":"Golden"},{"id":"goldenzone","symbol":"gld","name":"Goldenzone"},{"id":"goldex-token","symbol":"gldx","name":"Goldex"},{"id":"goldfarm","symbol":"gold","name":"GoldFarm"},{"id":"gold-fever-native-gold","symbol":"ngl","name":"Gold Fever Native Gold"},{"id":"goldfinch","symbol":"gfi","name":"Goldfinch"},{"id":"goldfinx","symbol":"gix","name":"GoldFinX"},{"id":"gold-guaranteed-coin","symbol":"ggcm","name":"Gold Guaranteed Coin"},{"id":"goldkash","symbol":"xgk","name":"GoldKash"},{"id":"goldminer","symbol":"gm","name":"GoldMiner"},{"id":"gold-mining-members","symbol":"gmm","name":"Gold Mining Members"},{"id":"goldmint","symbol":"mntp","name":"Goldmint"},{"id":"gold-retriever","symbol":"gldn","name":"Gold Retriever"},{"id":"gold-secured-currency","symbol":"gsx","name":"Gold Secured Currency"},{"id":"gold-socialfi-gamefi","symbol":"gsg","name":"Gold Socialfi GameFi"},{"id":"goledo","symbol":"gol","name":"Goledo"},{"id":"golem","symbol":"glm","name":"Golem"},{"id":"golff","symbol":"gof","name":"Golff"},{"id":"golteum","symbol":"gltm","name":"Golteum"},{"id":"gomeat","symbol":"gomt","name":"GoMeat"},{"id":"gomoney2","symbol":"gom2","name":"GoMoney2"},{"id":"good-bridging","symbol":"gb","name":"Good Bridging"},{"id":"good-dog","symbol":"heel","name":"Good Dog"},{"id":"good-games-guild","symbol":"ggg","name":"Good Games Guild"},{"id":"good-person-coin","symbol":"gpcx","name":"Good Person Coin"},{"id":"gooeys","symbol":"goo","name":"Gooeys"},{"id":"google-tokenized-stock-defichain","symbol":"dgoogl","name":"Google Tokenized Stock Defichain"},{"id":"goons-of-balatroon","symbol":"gob","name":"Goons of Balatroon"},{"id":"gooreo","symbol":"gooreo","name":"Gooreo"},{"id":"goose-finance","symbol":"egg","name":"Goose Finance"},{"id":"goosefx","symbol":"gofx","name":"GooseFX"},{"id":"go-out-now","symbol":"gon","name":"Go Out Now"},{"id":"gorgeous","symbol":"gorgeous","name":"Gorgeous"},{"id":"gorilla-finance","symbol":"gorilla","name":"Gorilla Finance"},{"id":"gotem","symbol":"gotem","name":"gotEM"},{"id":"got-guaranteed","symbol":"gotg","name":"Got Guaranteed"},{"id":"gourmetgalaxy","symbol":"gum","name":"Gourmet Galaxy"},{"id":"governance-algo","symbol":"galgo","name":"Governance Algo"},{"id":"governance-ohm","symbol":"gohm","name":"Governance OHM"},{"id":"governance-zil","symbol":"gzil","name":"governance ZIL"},{"id":"governor-dao","symbol":"gdao","name":"Governor DAO"},{"id":"govi","symbol":"govi","name":"CVI"},{"id":"govworld","symbol":"gov","name":"GovWorld"},{"id":"gowithmi","symbol":"gmat","name":"GoWithMi"},{"id":"goztepe-s-k-fan-token","symbol":"goz","name":"Göztepe S.K. Fan Token"},{"id":"gp-coin","symbol":"xgp","name":"GP Coin"},{"id":"gpex","symbol":"gpx","name":"GPEX"},{"id":"gpt-ai","symbol":"ai","name":"GPT AI"},{"id":"graft-blockchain","symbol":"grft","name":"Graft Blockchain"},{"id":"grail","symbol":"grail","name":"Grail"},{"id":"gram","symbol":"gram","name":"OpenGram"},{"id":"grape-2","symbol":"grape","name":"Grape Protocol"},{"id":"grape-finance","symbol":"grape","name":"Grape Finance"},{"id":"grape-token","symbol":"grape","name":"Grape"},{"id":"grapevine","symbol":"xgrape","name":"GrapeVine"},{"id":"graphen","symbol":"eltg","name":"Graphen"},{"id":"graphene","symbol":"gfn","name":"Graphene"},{"id":"graphlinq-protocol","symbol":"glq","name":"GraphLinq Protocol"},{"id":"grave","symbol":"grve","name":"Grave"},{"id":"graviocoin","symbol":"gio","name":"Graviocoin"},{"id":"gravitationally-bound-aura","symbol":"graviaura","name":"Gravitationally Bound AURA"},{"id":"graviton","symbol":"grav","name":"Graviton"},{"id":"graviton-zero","symbol":"grav","name":"Graviton Zero"},{"id":"gravity-bridge-dai","symbol":"g-dai","name":"Gravity Bridge DAI"},{"id":"gravity-bridge-tether","symbol":"g-usdt","name":"Gravity Bridge Tether"},{"id":"gravity-bridge-usdc","symbol":"g-usdc","name":"Gravity Bridge USDC"},{"id":"gravity-bridge-wbtc","symbol":"g-wbtc","name":"Gravity Bridge WBTC"},{"id":"gravity-bridge-weth","symbol":"g-weth","name":"Gravity Bridge WETH"},{"id":"gravity-finance","symbol":"gfi","name":"Gravity Finance"},{"id":"grearn","symbol":"gst","name":"GrEarn"},{"id":"great-bounty-dealer","symbol":"gbd","name":"Great Bounty Dealer"},{"id":"greatdane","symbol":"greatdane","name":"GreatDane"},{"id":"greed","symbol":"$greed","name":"Greed"},{"id":"greenair","symbol":"green","name":"GreenAir"},{"id":"green-beli","symbol":"grbe","name":"Green Beli"},{"id":"green-ben","symbol":"eben","name":"Green Ben"},{"id":"green-block","symbol":"gbt","name":"Green Block"},{"id":"green-climate-world","symbol":"wgc","name":"Green Climate World"},{"id":"greencoin","symbol":"gre","name":"Greencoin"},{"id":"green-cycgo","symbol":"gct","name":"Green CycGo"},{"id":"green-energy-coin","symbol":"gec","name":"Green Energy Coin"},{"id":"greenfuel","symbol":"greenfuel","name":"Greenfuel"},{"id":"greenheart-cbd","symbol":"cbd","name":"Greenheart CBD"},{"id":"greenhouse","symbol":"green","name":"Greenhouse"},{"id":"green-life-energy","symbol":"gle","name":"Green Life Energy"},{"id":"green-meta","symbol":"gmeta","name":"Green Meta"},{"id":"green-pet-egg","symbol":"dfkgreenegg","name":"Green Pet Egg"},{"id":"green-planet","symbol":"gamma","name":"Green Planet"},{"id":"green-ride-token","symbol":"grt","name":"Green Ride"},{"id":"greens","symbol":"greens","name":"Greens"},{"id":"green-satoshi-token","symbol":"gst-sol","name":"STEPN Green Satoshi Token on Solana"},{"id":"green-satoshi-token-bsc","symbol":"gst-bsc","name":"STEPN Green Satoshi Token on BSC"},{"id":"green-satoshi-token-on-eth","symbol":"gst-eth","name":"STEPN Green Satoshi Token on ETH"},{"id":"green-shiba-inu","symbol":"ginux","name":"Green Shiba Inu"},{"id":"greentrust","symbol":"gnt","name":"GreenTrust"},{"id":"greenworld","symbol":"gwd","name":"GreenWorld"},{"id":"greenzonex","symbol":"gzx","name":"GreenZoneX"},{"id":"grelf","symbol":"grelf","name":"GRELF"},{"id":"greyhound","symbol":"greyhound","name":"Greyhound"},{"id":"gridcoin-research","symbol":"grc","name":"Gridcoin"},{"id":"gridzone","symbol":"zone","name":"GridZone.io"},{"id":"griffin-art","symbol":"gart","name":"Griffin Art"},{"id":"griffin-art-ecosystem","symbol":"gart","name":"Griffin Art Ecosystem"},{"id":"grimace-coin","symbol":"grimace","name":"Grimace Coin"},{"id":"grim-evo","symbol":"grim evo","name":"Grim EVO"},{"id":"grimm","symbol":"grimm","name":"Grimm"},{"id":"grimtoken","symbol":"grim","name":"Grim"},{"id":"grin","symbol":"grin","name":"Grin"},{"id":"grinbit","symbol":"grbt","name":"Grinbit"},{"id":"grizzly-honey","symbol":"ghny","name":"Grizzly Honey"},{"id":"grn-grid","symbol":"g","name":"GRN Grid"},{"id":"gro-dao-token","symbol":"gro","name":"Gro DAO"},{"id":"groestlcoin","symbol":"grs","name":"Groestlcoin"},{"id":"grok","symbol":"grok","name":"Grok"},{"id":"grom","symbol":"gr","name":"GROM"},{"id":"groupdao","symbol":"gdo","name":"GroupDao"},{"id":"gro-vault-token","symbol":"gvt","name":"Gro Vault"},{"id":"grove","symbol":"grv","name":"GroveCoin"},{"id":"growmoon","symbol":"gm","name":"GrowMoon"},{"id":"growth-defi","symbol":"xgro","name":"GROWTH DeFi"},{"id":"gscarab","symbol":"gscarab","name":"GScarab"},{"id":"gsenetwork","symbol":"gse","name":"GSENetwork"},{"id":"gsmcoin","symbol":"gsm","name":"GSMcoin"},{"id":"gstcoin","symbol":"gst","name":"GSTCOIN"},{"id":"gti-token","symbol":"gti","name":"GTI Token"},{"id":"gton-capital","symbol":"gton","name":"GTON CAPITAL"},{"id":"gu","symbol":"gu","name":"Kugle GU"},{"id":"guapcoin","symbol":"guap","name":"Guapcoin"},{"id":"guarded-ether","symbol":"geth","name":"Guarded Ether"},{"id":"guardian-token","symbol":"guard","name":"Guardian GUARD"},{"id":"guider","symbol":"gdr","name":"Guider"},{"id":"guildfi","symbol":"gf","name":"GuildFi"},{"id":"guild-of-guardians","symbol":"gog","name":"Guild of Guardians"},{"id":"gulden","symbol":"munt","name":"Munt"},{"id":"gulfcoin-2","symbol":"gulf","name":"GulfCoin"},{"id":"guncoin","symbol":"gun","name":"Guncoin"},{"id":"gunstar-metaverse","symbol":"gsts","name":"Gunstar Metaverse"},{"id":"gunstar-metaverse-currency","symbol":"gsc","name":"Gunstar Metaverse Currency"},{"id":"gunthy","symbol":"gunthy","name":"GUNTHY"},{"id":"guzzler","symbol":"gzlr","name":"Guzzler"},{"id":"gxchain","symbol":"gxc","name":"GXChain"},{"id":"gyen","symbol":"gyen","name":"GYEN"},{"id":"gym-ai","symbol":"gym ai","name":"Gym AI"},{"id":"gym-network","symbol":"gymnet","name":"Gym Network"},{"id":"gyro","symbol":"gyro","name":"Gyro"},{"id":"h2finance","symbol":"yfih2","name":"H2Finance"},{"id":"h2o","symbol":"h2o","name":"H2O"},{"id":"h2o-dao","symbol":"h2o","name":"H2O Dao"},{"id":"h2o-securities","symbol":"h2on","name":"H2O Securities"},{"id":"h3ro3s","symbol":"h3ro3s","name":"H3RO3S"},{"id":"hachi","symbol":"hachi","name":"Hachi"},{"id":"hachikoinu","symbol":"inu","name":"HachikoInu"},{"id":"hackenai","symbol":"hai","name":"Hacken"},{"id":"hackerlabs-dao","symbol":"hld","name":"Hackerlabs DAO"},{"id":"hades","symbol":"hades","name":"Hades"},{"id":"hairdao","symbol":"hair","name":"HairDAO"},{"id":"haki-token","symbol":"haki","name":"HAKI Token"},{"id":"hakka-finance","symbol":"hakka","name":"Hakka Finance"},{"id":"hakuswap","symbol":"haku","name":"HakuSwap"},{"id":"halcyon","symbol":"hal","name":"Halcyon"},{"id":"halfpizza","symbol":"piza","name":"Half Pizza"},{"id":"half-shiba-inu","symbol":"shib0.5","name":"Half Shiba Inu"},{"id":"halisworld","symbol":"hls","name":"HalisWorld"},{"id":"halloween-crows","symbol":"scary","name":"Halloween Crows"},{"id":"halloween-floki","symbol":"floh","name":"Halloween Floki"},{"id":"halo-coin","symbol":"halo","name":"Halo Coin"},{"id":"halo-network","symbol":"ho","name":"HALO Network"},{"id":"halonft-art","symbol":"halo","name":"HALOnft.art"},{"id":"hamachi-finance","symbol":"hami","name":"Hamachi Finance"},{"id":"hamdan-coin","symbol":"hmc","name":"Hamdan Coin"},{"id":"hamster","symbol":"ham","name":"Hamster"},{"id":"hanchain","symbol":"han","name":"HanChain"},{"id":"handle-fi","symbol":"forex","name":"handle.fi"},{"id":"handleusd","symbol":"fxusd","name":"handleUSD"},{"id":"handshake","symbol":"hns","name":"Handshake"},{"id":"handy","symbol":"handy","name":"Handy"},{"id":"hanu-yokia","symbol":"hanu","name":"Hanu Yokia"},{"id":"hanzo-inu","symbol":"hanzo","name":"Hanzo"},{"id":"hapi","symbol":"hapi","name":"HAPI"},{"id":"happybear","symbol":"happy","name":"HappyBear"},{"id":"happy-birthday-coin","symbol":"hbdc","name":"Happy Birthday Coin"},{"id":"happyfans","symbol":"happy","name":"HappyFans"},{"id":"happyland","symbol":"hpl","name":"HappyLand"},{"id":"happyland-reward-token","symbol":"hpw","name":"HappyLand Reward"},{"id":"harambe","symbol":"harambe","name":"Harambe"},{"id":"harambe-protocol","symbol":"riph","name":"Harambe Protocol"},{"id":"hara-token","symbol":"hart","name":"Hara"},{"id":"hare","symbol":"hare","name":"Hare"},{"id":"hare-plus","symbol":"hare plus","name":"Hare Plus"},{"id":"hare-token","symbol":"hare","name":"Hare [OLD]"},{"id":"harlequins-fan-token","symbol":"quins","name":"Harlequins Fan Token"},{"id":"harmony","symbol":"one","name":"Harmony"},{"id":"harmonycoin","symbol":"hmc","name":"HarmonyCoin"},{"id":"harmonylauncher","symbol":"harl","name":"HarmonyLauncher"},{"id":"harmony-token","symbol":"harm","name":"Harmony Token"},{"id":"harmonyville","symbol":"hville","name":"Harmonyville"},{"id":"haroldcoin","symbol":"hrld","name":"Haroldcoin"},{"id":"harrypotterobamasonic10inu","symbol":"bitcoin","name":"HarryPotterObamaSonic10Inu"},{"id":"harvest-finance","symbol":"farm","name":"Harvest Finance"},{"id":"hashbit","symbol":"hbit","name":"HashBit"},{"id":"hash-bridge-oracle","symbol":"hbo","name":"Hash Bridge Oracle"},{"id":"hashcoin","symbol":"hsc","name":"HashCoin"},{"id":"hashflow","symbol":"hft","name":"Hashflow"},{"id":"hashgard","symbol":"gard","name":"Hashgard"},{"id":"hashland-coin","symbol":"hc","name":"HashLand Coin"},{"id":"hashmasks","symbol":"mask20","name":"Hashmasks"},{"id":"hashnet-biteco","symbol":"hnb","name":"HashNet BitEco"},{"id":"hashpanda","symbol":"panda","name":"HashPanda"},{"id":"hashtagger","symbol":"mooo","name":"Hashtagger"},{"id":"hashtag-united-fan-token","symbol":"hashtag","name":"Hashtag United Fan Token"},{"id":"hatayspor-token","symbol":"hatay","name":"Hatayspor Token"},{"id":"hatchypocket","symbol":"hatchy","name":"HatchyPocket"},{"id":"hathor","symbol":"htr","name":"Hathor"},{"id":"hati","symbol":"hati","name":"Hati"},{"id":"havah","symbol":"hvh","name":"HAVAH"},{"id":"haven","symbol":"xhv","name":"Haven"},{"id":"haven1","symbol":"h1","name":"Haven1"},{"id":"haven-token","symbol":"haven","name":"Safehaven DeFi"},{"id":"havven","symbol":"snx","name":"Synthetix Network"},{"id":"hawksight","symbol":"hawk","name":"Hawksight"},{"id":"hbarx","symbol":"hbarx","name":"HBARX"},{"id":"h-df0f364f-76a6-47fd-9c38-f8a239a4faad","symbol":"h","name":"H"},{"id":"headline","symbol":"hdl","name":"Headline"},{"id":"headstarter","symbol":"hst","name":"HeadStarter"},{"id":"heal-the-world","symbol":"heal","name":"Heal The World"},{"id":"healthify","symbol":"htf","name":"Healthify"},{"id":"health-potion","symbol":"hep","name":"Health Potion"},{"id":"hearn-fi","symbol":"hearn","name":"Hearn.fi"},{"id":"heart-rate","symbol":"htr","name":"Heart Rate"},{"id":"heavenland-hto","symbol":"hto","name":"Heavenland HTO"},{"id":"hebeblock","symbol":"hebe","name":"HebeBlock"},{"id":"hecofi","symbol":"hfi","name":"HecoFi"},{"id":"heco-origin-token","symbol":"hogt","name":"Heco Origin"},{"id":"heco-peg-bnb","symbol":"bnb","name":"Heco-Peg Binance Coin"},{"id":"heco-peg-xrp","symbol":"xrp","name":"Heco-Peg XRP"},{"id":"hectagon","symbol":"hecta","name":"Hectagon"},{"id":"hector-dao","symbol":"hec","name":"Hector Network"},{"id":"hedera-hashgraph","symbol":"hbar","name":"Hedera"},{"id":"hedgehog","symbol":"hedgehog","name":"Hedgehog"},{"id":"hedgepay","symbol":"hpay","name":"HedgePay"},{"id":"hedge-protocol","symbol":"hdg","name":"Hedge Protocol"},{"id":"hedget","symbol":"hget","name":"Hedget"},{"id":"hedgetrade","symbol":"hedg","name":"HedgeTrade"},{"id":"hedge-usd","symbol":"ush","name":"Hedge USD"},{"id":"hedpay","symbol":"hdp.ф","name":"HEdpAY"},{"id":"hedron","symbol":"hdrn","name":"Hedron"},{"id":"hedron-ethw","symbol":"hdrn","name":"Hedron ETHW"},{"id":"hegic","symbol":"hegic","name":"Hegic"},{"id":"hegic-yvault","symbol":"yvhegic","name":"HEGIC yVault"},{"id":"helena","symbol":"helena","name":"Helena Financial"},{"id":"helicopter-finance","symbol":"copter","name":"Helicopter Finance"},{"id":"helio-protocol-hay","symbol":"hay","name":"Destablecoin HAY"},{"id":"heliswap","symbol":"heli","name":"HeliSwap"},{"id":"helium","symbol":"hnt","name":"Helium"},{"id":"helleniccoin","symbol":"hnc","name":"HNC Coin"},{"id":"hellmoon","symbol":"hmoon","name":"HELLMOON"},{"id":"hello-art","symbol":"htt","name":"Hello Art"},{"id":"hello-labs","symbol":"hello","name":"HELLO"},{"id":"hellsing-inu","symbol":"hellsing","name":"Hellsing Inu"},{"id":"helmet-insure","symbol":"helmet","name":"Helmet Insure"},{"id":"help-coin","symbol":"hlp","name":"HLP"},{"id":"helper-coin","symbol":"hlpr","name":"Helper Coin"},{"id":"helpico","symbol":"help","name":"Helpico"},{"id":"helpkidz-coin","symbol":"hkc","name":"HelpKidz Coin"},{"id":"helpseed","symbol":"helps","name":"HelpSeed"},{"id":"help-the-homeless-coin","symbol":"hth","name":"Help The Homeless Coin"},{"id":"hempcoin-thc","symbol":"thc","name":"Hempcoin"},{"id":"heptafranc","symbol":"hptf","name":"HEPTAFRANC"},{"id":"hera-finance","symbol":"hera","name":"Hera Finance"},{"id":"herbalist-token","symbol":"herb","name":"Herbalist"},{"id":"herbee","symbol":"bee","name":"Herbee"},{"id":"herity-network","symbol":"her","name":"Herity Network"},{"id":"hermes-dao","symbol":"hmx","name":"Hermes DAO"},{"id":"hermes-protocol","symbol":"hermes","name":"Hermes Protocol"},{"id":"hermez-network-token","symbol":"hez","name":"Hermez Network"},{"id":"hero","symbol":"hero","name":"HERO"},{"id":"hero-arena","symbol":"hera","name":"Hero Arena"},{"id":"hero-blaze-three-kingdoms","symbol":"mudol2","name":"Hero Blaze: Three Kingdoms"},{"id":"herobook","symbol":"hbg","name":"HeroBook"},{"id":"hero-cat-key","symbol":"hck","name":"Hero Cat Key"},{"id":"hero-cat-token","symbol":"hct","name":"Hero Cat"},{"id":"herocoin","symbol":"play","name":"HEROcoin"},{"id":"heroeschained","symbol":"hec","name":"HeroesChained"},{"id":"heroes-empires","symbol":"he","name":"Heroes \u0026 Empires"},{"id":"heroes-of-nft","symbol":"hon","name":"Heroes of NFT"},{"id":"heroes-td","symbol":"htd","name":"Heroes TD"},{"id":"heroestd-cgc","symbol":"cgc","name":"HeroesTD CGC"},{"id":"herofi","symbol":"heroegg","name":"HeroFi"},{"id":"herofi-token-2","symbol":"rofi","name":"HeroFi ROFI"},{"id":"hero-inu","symbol":"heros","name":"Heros"},{"id":"heropark","symbol":"hp","name":"HeroPark"},{"id":"heroverse","symbol":"her","name":"HeroVerse"},{"id":"hertz-network","symbol":"htz","name":"Hertz Network"},{"id":"heruka-tsangnyon","symbol":"tsangnyon","name":"HERUKA TSANGNYON"},{"id":"herum","symbol":"ram","name":"Herum"},{"id":"hesman-shard","symbol":"hes","name":"Hesman Shard"},{"id":"hest-stake","symbol":"hse","name":"Hest stake"},{"id":"hex","symbol":"hex","name":"HEX"},{"id":"hex-ethw","symbol":"hex","name":"HEX ETHW"},{"id":"hey","symbol":"hey","name":"Hey"},{"id":"heyflokiai","symbol":"a2e","name":"Hey Floki Ai"},{"id":"hey-reborn-new","symbol":"rb","name":"Hey Reborn [NEW]"},{"id":"hiazuki","symbol":"hiazuki","name":"hiAZUKI"},{"id":"hibayc","symbol":"hibayc","name":"hiBAYC"},{"id":"hibeanz","symbol":"hibeanz","name":"hiBEANZ"},{"id":"hibiki-finance","symbol":"hibiki","name":"Hibiki Finance"},{"id":"hiblocks","symbol":"hibs","name":"Hiblocks"},{"id":"hic-et-nunc-dao","symbol":"hdao","name":"Hic et nunc DAO"},{"id":"hiclonex","symbol":"hiclonex","name":"hiCLONEX"},{"id":"hicoolcats","symbol":"hicoolcats","name":"hiCOOLCATS"},{"id":"hideous-coin","symbol":"hideous","name":"Hideous Finance"},{"id":"hi-dollar","symbol":"hi","name":"hi Dollar"},{"id":"hidoodles","symbol":"hidoodles","name":"hiDOODLES"},{"id":"hiens3","symbol":"hiens3","name":"hiENS3"},{"id":"hiens4","symbol":"hiens4","name":"hiENS4"},{"id":"hifidenza","symbol":"hifidenza","name":"hiFIDENZA"},{"id":"hifi-finance","symbol":"hifi","name":"Hifi Finance"},{"id":"hifluf","symbol":"hifluf","name":"hiFLUF"},{"id":"hifriends","symbol":"hifriends","name":"hiFRIENDS"},{"id":"higazers","symbol":"higazers","name":"hiGAZERS"},{"id":"high-performance-blockchain","symbol":"hpb","name":"High Performance Blockchain"},{"id":"high-roller-hippo-clique","symbol":"roll","name":"High Roller Hippo Clique"},{"id":"highstreet","symbol":"high","name":"Highstreet"},{"id":"hikari-protocol","symbol":"hikari","name":"Hikari Protocol"},{"id":"hillstone","symbol":"hsf","name":"Hillstone Finance"},{"id":"hilo","symbol":"hilo","name":"HILO"},{"id":"himalayan-cat-coin","symbol":"hima","name":"Himalayan Cat Coin"},{"id":"himayc","symbol":"himayc","name":"hiMAYC"},{"id":"himeebits","symbol":"himeebits","name":"hiMEEBITS"},{"id":"himfers","symbol":"himfers","name":"hiMFERS"},{"id":"himoonbirds","symbol":"himoonbirds","name":"hiMOONBIRDS"},{"id":"himo-world","symbol":"himo","name":"Himo World"},{"id":"hina-inu","symbol":"hina","name":"Hina Inu"},{"id":"hintchain","symbol":"hint","name":"Hintchain"},{"id":"hiod","symbol":"hiod","name":"hiOD"},{"id":"hiodbs","symbol":"hiodbs","name":"hiODBS"},{"id":"hipenguins","symbol":"hipenguins","name":"hiPENGUINS"},{"id":"hippopotamus","symbol":"hpo","name":"Hippo Wallet"},{"id":"hippo-token","symbol":"hip","name":"Hippo"},{"id":"hipunks","symbol":"hipunks","name":"hiPunks"},{"id":"hiram","symbol":"hiram","name":"Hiram"},{"id":"hirenga","symbol":"hirenga","name":"hiRENGA"},{"id":"hisand33","symbol":"hisand33","name":"hiSAND33"},{"id":"hiseals","symbol":"hiseals","name":"hiSEALS"},{"id":"hisquiggle","symbol":"hisquiggle","name":"hiSQUIGGLE"},{"id":"historia","symbol":"hta","name":"Historia"},{"id":"historydao","symbol":"hao","name":"HistoryDAO"},{"id":"hitbtc-token","symbol":"hit","name":"HitBTC"},{"id":"hitchain","symbol":"hit","name":"HitChain"},{"id":"hitop","symbol":"hitop","name":"Hitop"},{"id":"hiundead","symbol":"hiundead","name":"hiUNDEAD"},{"id":"hivalhalla","symbol":"hivalhalla","name":"hiVALHALLA"},{"id":"hive","symbol":"hive","name":"Hive"},{"id":"hive_dollar","symbol":"hbd","name":"Hive Dollar"},{"id":"hive-investments-honey","symbol":"hny","name":"Hive.Investments HONEY"},{"id":"hivemapper","symbol":"honey","name":"Hivemapper"},{"id":"hiveterminal","symbol":"hvn","name":"Hiveterminal"},{"id":"hive-vault","symbol":"hiv","name":"Hive Vault"},{"id":"hnb-protocol","symbol":"hnb","name":"HNB Protocol"},{"id":"hnk-orijent-1919-token","symbol":"ori","name":"HNK Orijent 1919"},{"id":"hoard","symbol":"hrd","name":"Hoard"},{"id":"hobbes","symbol":"hobbes","name":"Hobbes"},{"id":"hobonickels","symbol":"hbn","name":"Hobonickels"},{"id":"hodlassets","symbol":"hodl","name":"HodlAssets"},{"id":"hodlcoin","symbol":"hodl","name":"HOdlcoin"},{"id":"hodl-finance","symbol":"hft","name":"Hodl Finance"},{"id":"hodl-token","symbol":"hodl","name":"HODL"},{"id":"hodooi-com","symbol":"hod","name":"HoDooi.com"},{"id":"hoge-finance","symbol":"hoge","name":"Hoge Finance"},{"id":"hoichi","symbol":"hoichi","name":"Hoichi"},{"id":"hokkaido-inu-30bdfab6-dfb9-4fc0-b3c3-02bffe162ee4","symbol":"hoka","name":"Hokkaido Inu"},{"id":"hokkaidu-inu","symbol":"hokk","name":"HOKK Finance"},{"id":"holdr","symbol":"hldr","name":"Holdr"},{"id":"hollaex-token","symbol":"xht","name":"HollaEx"},{"id":"hollygold","symbol":"hgold","name":"HollyGold"},{"id":"hollywood-capital-group-warrior","symbol":"wor","name":"Hollywood Capital Group WARRIOR"},{"id":"holoclear","symbol":"holo","name":"HOLOCLEAR"},{"id":"hololoot","symbol":"hol","name":"Hololoot"},{"id":"holonus","symbol":"hln","name":"Holonus"},{"id":"holoride","symbol":"ride","name":"holoride"},{"id":"holotoken","symbol":"hot","name":"Holo"},{"id":"holydoge","symbol":"hdoge","name":"HolyDoge"},{"id":"holygrail","symbol":"hly","name":"HolyGrail"},{"id":"holygrails-io","symbol":"holy","name":"HolyGrails.io"},{"id":"holyheld-2","symbol":"move","name":"Mover"},{"id":"homeros","symbol":"hmr","name":"Homeros"},{"id":"homerun","symbol":"hmrn","name":"Homerun"},{"id":"homie-wars","symbol":"homiecoin","name":"Homie Wars"},{"id":"hondaiscoin","symbol":"hndc","name":"HondaisCoin"},{"id":"honest-mining","symbol":"hnst","name":"Honest"},{"id":"honey","symbol":"hny","name":"Honey"},{"id":"honeybee","symbol":"bee","name":"HoneyBee"},{"id":"honey-deluxe","symbol":"honeyd","name":"Honey Deluxe"},{"id":"honeyfarm-finance","symbol":"honey","name":"HoneyFarm Finance"},{"id":"honey-finance","symbol":"honey","name":"Honey Finance"},{"id":"honeyland-honey","symbol":"hxd","name":"Honeyland"},{"id":"honeymoon-token","symbol":"moon","name":"HoneyMOON"},{"id":"honeypad","symbol":"honey","name":"HONEYPAD"},{"id":"honey-pot-beekeepers","symbol":"honey","name":"Honey Pot BeeKeepers"},{"id":"honor-token","symbol":"honor","name":"Honor"},{"id":"honor-world-token","symbol":"hwt","name":"Honor World Token"},{"id":"hooked-protocol","symbol":"hook","name":"Hooked Protocol"},{"id":"hoop","symbol":"hoop","name":"Primal Hoop"},{"id":"hoot","symbol":"hoot","name":"Hoot"},{"id":"hopers-io","symbol":"hopers","name":"HOPERS"},{"id":"hoppers-game","symbol":"fly","name":"Hoppers Game"},{"id":"hop-protocol","symbol":"hop","name":"Hop Protocol"},{"id":"hoppy","symbol":"hop","name":"HOPPY"},{"id":"hoppyinu","symbol":"hoppyinu","name":"HoppyInu"},{"id":"hopr","symbol":"hopr","name":"HOPR"},{"id":"hord","symbol":"hord","name":"Hord"},{"id":"hord-heth","symbol":"heth","name":"Hord hETH"},{"id":"horizondollar","symbol":"hzd","name":"Horizon Dollar"},{"id":"horizon-protocol","symbol":"hzn","name":"Horizon Protocol"},{"id":"horny-hyenas","symbol":"horny","name":"Horny Hyenas"},{"id":"horseafi","symbol":"horsea","name":"HorseaFi"},{"id":"horuspay","symbol":"horus","name":"HorusPay"},{"id":"hosky","symbol":"hosky","name":"Hosky"},{"id":"hotbit-token","symbol":"htb","name":"Hotbit"},{"id":"hot-cross","symbol":"hotcross","name":"Hot Cross"},{"id":"hot-doge","symbol":"hotdoge","name":"HotDoge [OLD]"},{"id":"hotelium","symbol":"htl","name":"Hotelium"},{"id":"hotmoon","symbol":"hotmoon","name":"HotMoon"},{"id":"hot-n-cold-finance","symbol":"hnc","name":"Hot'n Cold Finance"},{"id":"hourglass","symbol":"wait","name":"Hourglass"},{"id":"house-of-frenchies","symbol":"hofr","name":"House of Frenchies"},{"id":"houston-token","symbol":"hou","name":"Houston Token"},{"id":"howdoo","symbol":"udoo","name":"Hyprr"},{"id":"howl-city","symbol":"hwl","name":"Howl City"},{"id":"hrdgcoin","symbol":"hrdg","name":"HRDGCOIN"},{"id":"hshare","symbol":"hc","name":"HyperCash"},{"id":"h-space-metaverse","symbol":"hksm","name":"H-Space Metaverse"},{"id":"hsuite","symbol":"hsuite","name":"HbarSuite"},{"id":"htm","symbol":"htm","name":"HTM"},{"id":"htmlcoin","symbol":"html","name":"HTMLCOIN"},{"id":"htmoon2-0","symbol":"htmoon2.0","name":"HTMOON2.0"},{"id":"hubble","symbol":"hbb","name":"Hubble"},{"id":"hubcoin-2","symbol":"hub","name":"HubCoin"},{"id":"hubgame","symbol":"hub","name":"HubGame"},{"id":"hubin-network","symbol":"hbn","name":"Hubin Network"},{"id":"hubswirl","symbol":"swirlx","name":"SwirlTokenX"},{"id":"hub-token","symbol":"hub","name":"Hub"},{"id":"huckleberry","symbol":"finn","name":"Huckleberry"},{"id":"huckleberry-inu","symbol":"hkby","name":"Huckleberry Inu"},{"id":"hudi","symbol":"hudi","name":"Hudi"},{"id":"hughug-coin","symbol":"hghg","name":"HUGHUG"},{"id":"huh","symbol":"huh","name":"HUH"},{"id":"hulk-inu","symbol":"hulk","name":"Hulk Inu"},{"id":"huma-finance","symbol":"huma","name":"Huma Finance"},{"id":"humandao","symbol":"hdao","name":"humanDAO"},{"id":"humaniq","symbol":"hmq","name":"Humaniq"},{"id":"humanize","symbol":"$hmt","name":"Humanize"},{"id":"humanode","symbol":"hmnd","name":"Humanode"},{"id":"humanoid-ai","symbol":"humai","name":"Humanoid AI"},{"id":"human-protocol","symbol":"hmt","name":"HUMAN Protocol"},{"id":"humans-ai","symbol":"heart","name":"Humans.ai"},{"id":"humanscape","symbol":"hum","name":"Humanscape"},{"id":"hummingbird-egg-token","symbol":"hegg","name":"Hummingbird Egg"},{"id":"hummingbird-finance","symbol":"hmng","name":"Hummingbird Finance"},{"id":"hummingbot","symbol":"hbot","name":"Hummingbot"},{"id":"hummus","symbol":"hum","name":"Hummus"},{"id":"hundred-finance","symbol":"hnd","name":"Hundred Finance"},{"id":"hungarian-vizsla-inu","symbol":"hvi","name":"Hungarian Vizsla Inu"},{"id":"hunger-token","symbol":"hunger","name":"Hunger"},{"id":"hungrybear","symbol":"hungry","name":"HungryBear"},{"id":"hunny-love-token","symbol":"love","name":"HunnyDAO"},{"id":"hunter","symbol":"hntr","name":"Hunter"},{"id":"hunter-diamond","symbol":"hunt","name":"Hunter Diamond"},{"id":"hunt-token","symbol":"hunt","name":"Hunt"},{"id":"huny","symbol":"huny","name":"Huny"},{"id":"huobi-bitcoin-cash","symbol":"hbch","name":"Huobi Bitcoin Cash"},{"id":"huobi-btc","symbol":"hbtc","name":"Huobi BTC"},{"id":"huobi-ethereum","symbol":"heth","name":"Huobi Ethereum"},{"id":"huobi-fil","symbol":"hfil","name":"Huobi Fil"},{"id":"huobi-litecoin","symbol":"hltc","name":"Huobi Litecoin"},{"id":"huobi-polkadot","symbol":"hdot","name":"Huobi Polkadot"},{"id":"huobi-pool-token","symbol":"hpt","name":"Huobi Pool"},{"id":"huobi-token","symbol":"ht","name":"Huobi"},{"id":"hupayx","symbol":"hpx","name":"HUPAYX"},{"id":"hurrian-network","symbol":"mld","name":"Hurrian Network"},{"id":"hurricane-nft","symbol":"nhct","name":"Hurricane NFT"},{"id":"hurricaneswap-token","symbol":"hct","name":"HurricaneSwap"},{"id":"husd","symbol":"husd","name":"HUSD"},{"id":"hush","symbol":"hush","name":"Hush"},{"id":"husky","symbol":"husky","name":"Husky"},{"id":"husky-avax","symbol":"husky","name":"Husky AVAX"},{"id":"huskyshiba","symbol":"hshiba","name":"HuskyShiba"},{"id":"hxro","symbol":"hxro","name":"Hxro"},{"id":"hybrid-token-2f302f60-395f-4dd0-8c18-9c5418a61a31","symbol":"hbd","name":"HYBRID TOKEN"},{"id":"hybrix","symbol":"hy","name":"Hybrix"},{"id":"hycon","symbol":"hyc","name":"Hycon"},{"id":"hydra","symbol":"hydra","name":"Hydra"},{"id":"hydradx","symbol":"hdx","name":"HydraDX"},{"id":"hydranet","symbol":"hdx","name":"Hydranet"},{"id":"hydraverse","symbol":"hdv","name":"Hydraverse"},{"id":"hydro","symbol":"hydro","name":"Hydro"},{"id":"hymnode","symbol":"hnt","name":"Hymnode"},{"id":"hyperalloy","symbol":"alloy","name":"HyperAlloy"},{"id":"hypercent","symbol":"hype","name":"Hypercent"},{"id":"hyperchainx","symbol":"hyper","name":"HyperChainX"},{"id":"hypercomic","symbol":"hyco","name":"HYPERCOMIC"},{"id":"hyperdao","symbol":"hdao","name":"HyperDAO"},{"id":"hyperonchain","symbol":"hpn","name":"HyperonChain"},{"id":"hypersign-identity-token","symbol":"hid","name":"Hypersign Identity"},{"id":"hyperstake","symbol":"hyp","name":"Element"},{"id":"hyperverse","symbol":"hvt","name":"HyperVerse"},{"id":"hyruleswap","symbol":"rupee","name":"HyruleSwap"},{"id":"hyve","symbol":"hyve","name":"Hyve"},{"id":"hzm-coin","symbol":"hzm","name":"HZM Coin"},{"id":"i0coin","symbol":"i0c","name":"I0Coin"},{"id":"iagon","symbol":"iag","name":"Iagon"},{"id":"iamx","symbol":"iamx","name":"IAMX"},{"id":"iassets","symbol":"asset","name":"iAssets"},{"id":"iazuki","symbol":"iazuki","name":"IAzuki"},{"id":"ibank","symbol":"ibank","name":"iBank"},{"id":"ibetyou","symbol":"iby","name":"iBetYou"},{"id":"ibg-token","symbol":"ibg","name":"iBG Finance (BSC)"},{"id":"ibithub","symbol":"ibh","name":"iBitHub"},{"id":"ibiza-token","symbol":"ibz","name":"Ibiza"},{"id":"ibs","symbol":"ibs","name":"IBS"},{"id":"ibtc-2","symbol":"ibtc","name":"iBTC"},{"id":"ibuffer","symbol":"ibfr","name":"iBuffer"},{"id":"ibuffer-token","symbol":"bfr","name":"Buffer Token"},{"id":"icarus-network","symbol":"ica","name":"Icarus Network"},{"id":"ic-drip-token","symbol":"icd","name":"IC Drip Token"},{"id":"icecream","symbol":"ice","name":"IceCreamSwap"},{"id":"icecream-finance","symbol":"cream","name":"IceCream Finance"},{"id":"icel-idman-yurdu","symbol":"miy","name":"Icel Idman Yurdu"},{"id":"ice-token","symbol":"ice","name":"Popsicle Finance"},{"id":"ichello","symbol":"ello","name":"Ichello"},{"id":"ichi-farm","symbol":"ichi","name":"ICHI"},{"id":"ichigo-inu","symbol":"ichigo","name":"Ichigo Inu"},{"id":"icocryptomarketcap","symbol":"icmc","name":"IcoCryptoMarketCap"},{"id":"i-coin","symbol":"icn","name":"I-Coin V2"},{"id":"icomex","symbol":"icmx","name":"iCOMEX"},{"id":"icommunity","symbol":"icom","name":"iCommunity"},{"id":"icon","symbol":"icx","name":"ICON"},{"id":"iconiq-lab-token","symbol":"icnq","name":"Deutsche Digital Assets"},{"id":"icosa","symbol":"icsa","name":"Icosa"},{"id":"icy","symbol":"ic","name":"Icy"},{"id":"idavoll-network","symbol":"idv","name":"Idavoll DAO"},{"id":"ideachain","symbol":"ich","name":"IdeaChain"},{"id":"ideamarket","symbol":"imo","name":"Ideamarket"},{"id":"ideaology","symbol":"idea","name":"Ideaology"},{"id":"ideas","symbol":"ideas","name":"IDEAS"},{"id":"idefiyieldprotocol","symbol":"idyp","name":"iDeFiYieldProtocol"},{"id":"idena","symbol":"idna","name":"Idena"},{"id":"idendefi","symbol":"id","name":"IdenDEFI"},{"id":"identity","symbol":"idtt","name":"Identity"},{"id":"idexo-token","symbol":"ido","name":"Idexo"},{"id":"idia","symbol":"idia","name":"Impossible Finance Launchpad"},{"id":"idk","symbol":"idk","name":"IDK"},{"id":"idle","symbol":"idle","name":"IDLE"},{"id":"idle-dai-risk-adjusted","symbol":"idledaisafe","name":"IdleDAI (Risk Adjusted)"},{"id":"idle-dai-yield","symbol":"idledaiyield","name":"IdleDAI (Best Yield)"},{"id":"idle-susd-yield","symbol":"idlesusdyield","name":"IdleSUSD (Yield)"},{"id":"idletreasureparty","symbol":"dtevil","name":"IdleTreasureParty"},{"id":"idle-tusd-yield","symbol":"idletusdyield","name":"IdleTUSD (Best Yield)"},{"id":"idle-usdc-risk-adjusted","symbol":"idleusdcsafe","name":"IdleUSDC (Risk Adjusted)"},{"id":"idle-usdc-yield","symbol":"idleusdcyield","name":"IdleUSDC (Yield)"},{"id":"idle-usdt-risk-adjusted","symbol":"idleusdtsafe","name":"IdleUSDT (Risk Adjusted)"},{"id":"idle-usdt-yield","symbol":"idleusdtyield","name":"IdleUSDT (Yield)"},{"id":"idle-wbtc-yield","symbol":"idlewbtcyield","name":"IdleWBTC (Best Yield)"},{"id":"idm-token","symbol":"idm","name":"IDM Coop"},{"id":"idoodles","symbol":"idoodles","name":"IDOODLES"},{"id":"iethereum","symbol":"ieth","name":"iEthereum"},{"id":"iexec-rlc","symbol":"rlc","name":"iExec RLC"},{"id":"ifarm","symbol":"ifarm","name":"iFARM"},{"id":"ifortune","symbol":"ifc","name":"iFortune"},{"id":"ifoswap-token","symbol":"h2o","name":"IFOSwap"},{"id":"iftoken","symbol":"ift","name":"IFT"},{"id":"ig-gold","symbol":"igg","name":"IG Gold"},{"id":"ignis","symbol":"ignis","name":"Ignis"},{"id":"ignore-fud","symbol":"4token","name":"Ignore Fud"},{"id":"iguverse","symbol":"igup","name":"IguVerse IGUP"},{"id":"iguverse-igu","symbol":"igu","name":"IguVerse IGU"},{"id":"iht-real-estate-protocol","symbol":"iht","name":"IHT Real Estate Protocol"},{"id":"iinjaz","symbol":"ijz","name":"iinjaz"},{"id":"ijascoin","symbol":"ijc","name":"IjasCoin"},{"id":"ikolf","symbol":"ikolf","name":"IKOLF"},{"id":"ilcoin","symbol":"ilc","name":"ILCOIN"},{"id":"illiquiddao","symbol":"jpegs","name":"IlliquidDAO"},{"id":"illuvium","symbol":"ilv","name":"Illuvium"},{"id":"ilus-coin","symbol":"ilus","name":"ILUS Coin"},{"id":"imagecoin","symbol":"img","name":"ImageCoin"},{"id":"imagictoken","symbol":"imagic","name":"iMagic"},{"id":"imayc","symbol":"imayc","name":"IMAYC"},{"id":"ime-lab","symbol":"lime","name":"iMe Lab"},{"id":"imgnai","symbol":"imgnai","name":"Image Generation AI"},{"id":"immortaldao","symbol":"immo","name":"ImmortalDAO"},{"id":"immortl","symbol":"imrtl","name":"Immortl (OLD)"},{"id":"immortl-2","symbol":"imrtl","name":"Immortl"},{"id":"immutable","symbol":"dara","name":"Immutable"},{"id":"immutable-x","symbol":"imx","name":"ImmutableX"},{"id":"imo","symbol":"imo","name":"IMO"},{"id":"i-money-crypto","symbol":"imc","name":"i Money Crypto"},{"id":"imov","symbol":"imt","name":"IMOV"},{"id":"impactmarket","symbol":"pact","name":"impactMarket"},{"id":"impactxp","symbol":"impactxp","name":"ImpactXP"},{"id":"impactxprime","symbol":"ixp","name":"IMPACTXPRIME"},{"id":"imperial-obelisk-2","symbol":"imp","name":"Imperial Obelisk"},{"id":"imperium-empires","symbol":"ime","name":"Imperium Empires"},{"id":"impermax-2","symbol":"ibex","name":"Impermax"},{"id":"impossible-finance","symbol":"if","name":"Impossible Finance"},{"id":"impostors-blood","symbol":"blood","name":"Impostors Blood"},{"id":"impt","symbol":"impt","name":"IMPT"},{"id":"impulse-by-fdr","symbol":"impulse","name":"Impulse By FDR"},{"id":"incakoin","symbol":"nka","name":"IncaKoin"},{"id":"inci-token","symbol":"inci","name":"Inci"},{"id":"incognito-2","symbol":"prv","name":"Incognito"},{"id":"income-island","symbol":"income","name":"Income Island"},{"id":"incube-chain","symbol":"icb","name":"Incube Chain"},{"id":"indahash","symbol":"idh","name":"indaHash"},{"id":"index-cooperative","symbol":"index","name":"Index Cooperative"},{"id":"index-coop-eth-2x-flexible-leverage-index","symbol":"eth2x-fli-p","name":"Index Coop - ETH 2x Flexible Leverage Index (Polygon)"},{"id":"index-coop-matic-2x-flexible-leverage-index","symbol":"matic2x-fli-p","name":"Index Coop - MATIC 2x Flexible Leverage Index"},{"id":"indexed-finance","symbol":"ndx","name":"Indexed Finance"},{"id":"indian-shiba-inu","symbol":"indshib","name":"Indian Shiba Inu"},{"id":"indigg","symbol":"indi","name":"IndiGG"},{"id":"indigo-dao-governance-token","symbol":"indy","name":"Indigo Protocol"},{"id":"indigo-protocol-ieth","symbol":"ieth","name":"Indigo Protocol iETH"},{"id":"indorse","symbol":"ind","name":"Indorse"},{"id":"inery","symbol":"$inr","name":"Inery"},{"id":"infam","symbol":"inf","name":"Infam"},{"id":"infinite-arcade-tic","symbol":"tic","name":"Infinite Arcade TIC"},{"id":"infinitee","symbol":"inftee","name":"Infinitee"},{"id":"infinite-launch","symbol":"ila","name":"Infinite Launch"},{"id":"infinitorr","symbol":"torr","name":"InfiniTORR"},{"id":"infinity-angel","symbol":"ing","name":"Infinity Games"},{"id":"infinity-arena","symbol":"inaz","name":"Infinity Arena"},{"id":"infinity-esaham","symbol":"infs","name":"Infinity Esaham"},{"id":"infinity-game-nft","symbol":"ign","name":"Infinity Game NFT"},{"id":"infinity-pad-2","symbol":"ipad","name":"Infinity PAD"},{"id":"infinity-protocol","symbol":"infinity","name":"Infinity Protocol"},{"id":"infinity-rocket-token","symbol":"irt","name":"Infinity Rocket"},{"id":"infinity-skies","symbol":"isky","name":"Infinity Skies"},{"id":"infinium","symbol":"inf","name":"Infinium"},{"id":"inflationcoin","symbol":"iflt","name":"InflationCoin"},{"id":"inflation-hedging-coin","symbol":"ihc","name":"Inflation Hedging Coin"},{"id":"infliv","symbol":"ifv","name":"INFLIV"},{"id":"influencer","symbol":"imi","name":"Influencer"},{"id":"influxcoin","symbol":"infx","name":"Influxcoin"},{"id":"infomatix","symbol":"info","name":"Infomatix"},{"id":"info-token","symbol":"info","name":"Kardia Info"},{"id":"inftspace","symbol":"ins","name":"iNFTspace"},{"id":"inheritance-art","symbol":"iai","name":"inheritance Art"},{"id":"init","symbol":"init","name":"Inite"},{"id":"injective-protocol","symbol":"inj","name":"Injective"},{"id":"ink","symbol":"ink","name":"Ink"},{"id":"ink-fantom","symbol":"ink","name":"Ink Fantom"},{"id":"ink-finance","symbol":"quill","name":"Ink Finance"},{"id":"inkz","symbol":"inkz","name":"INKz"},{"id":"in-meta-travel","symbol":"imt","name":"In Meta Travel"},{"id":"innitforthetech","symbol":"innit","name":"InnitForTheTECH"},{"id":"innova","symbol":"inn","name":"Innova"},{"id":"innovation-blockchain-payment","symbol":"ibp","name":"Innovation Blockchain Payment"},{"id":"innovative-bioresearch","symbol":"innbc","name":"Innovative Bioresearch Coin"},{"id":"inoovi","symbol":"ivi","name":"Inoovi"},{"id":"inpulse-x","symbol":"ipx","name":"InpulseX"},{"id":"ins3-finance-coin","symbol":"itf","name":"Ins3.Finance Coin"},{"id":"insight-ai","symbol":"insai","name":"Insight AI"},{"id":"insight-protocol","symbol":"inx","name":"Insight Protocol"},{"id":"insights-network","symbol":"instar","name":"INSTAR"},{"id":"insrt-finance","symbol":"$insrt","name":"Insrt Finance"},{"id":"instadapp","symbol":"inst","name":"Instadapp"},{"id":"instadapp-dai","symbol":"idai","name":"Instadapp DAI"},{"id":"instadapp-eth","symbol":"ieth","name":"iETH v1"},{"id":"instadapp-eth-v2","symbol":"ieth v2","name":"Instadapp ETH v2"},{"id":"instadapp-usdc","symbol":"iusdc","name":"Instadapp USDC"},{"id":"instadapp-wbtc","symbol":"iwbtc","name":"Instadapp WBTC"},{"id":"instinct","symbol":"ins","name":"Instinct"},{"id":"instrumental-finance","symbol":"strm","name":"Instrumental Finance"},{"id":"insula","symbol":"isla","name":"Insula"},{"id":"insurace","symbol":"insur","name":"InsurAce"},{"id":"insure","symbol":"sure","name":"inSure DeFi"},{"id":"insuredao","symbol":"insure","name":"InsureDAO"},{"id":"insured-finance","symbol":"infi","name":"Insured Finance"},{"id":"insurepal","symbol":"ipl","name":"InsurePal"},{"id":"insureum","symbol":"isr","name":"Insureum"},{"id":"insurex","symbol":"ixt","name":"iXledger"},{"id":"integral","symbol":"itgr","name":"Integral"},{"id":"integritee","symbol":"teer","name":"Integritee"},{"id":"intelly","symbol":"intl","name":"Intelly"},{"id":"interactwith-token","symbol":"inter","name":"InteractWith"},{"id":"interbtc","symbol":"ibtc","name":"interBTC"},{"id":"interest-bearing-eth","symbol":"ibeth","name":"Interest Bearing ETH"},{"id":"interest-compounding-eth-index","symbol":"iceth","name":"Interest Compounding ETH Index"},{"id":"interest-protocol","symbol":"usdi","name":"Interest Protocol USDi"},{"id":"interest-protocol-token","symbol":"ipt","name":"Interest Protocol Token"},{"id":"interfinex-bills","symbol":"ifex","name":"Interfinex Bills"},{"id":"interlay","symbol":"intr","name":"Interlay"},{"id":"inter-milan-fan-token","symbol":"inter","name":"Inter Milan Fan Token"},{"id":"internet-computer","symbol":"icp","name":"Internet Computer"},{"id":"internet-money","symbol":"im","name":"Internet Money (ETH)"},{"id":"internet-money-bsc","symbol":"im","name":"Internet Money (BSC)"},{"id":"internet-node-token","symbol":"int","name":"INTchain"},{"id":"internet-of-energy-network","symbol":"ioen","name":"Internet of Energy Network"},{"id":"internxt","symbol":"inxt","name":"Internxt"},{"id":"interport-token","symbol":"itp","name":"Interport Token"},{"id":"intersola","symbol":"isola","name":"Intersola"},{"id":"inter-stable-token","symbol":"ist","name":"Inter Stable Token"},{"id":"interstellar-domain-order","symbol":"ido","name":"Interstellar Domain Order"},{"id":"intexcoin","symbol":"intx","name":"INTEXCOIN"},{"id":"intucoin","symbol":"intu","name":"INTUCoin"},{"id":"inu","symbol":"inu","name":"Inu."},{"id":"inu-inu","symbol":"inuinu","name":"Inu Inu"},{"id":"inuko-finance","symbol":"inuko","name":"Inuko Finance"},{"id":"inusanity","symbol":"inusanity","name":"Inusanity"},{"id":"inu-token","symbol":"inu","name":"INU"},{"id":"inu-wars","symbol":"iwr","name":"Inu Wars"},{"id":"invectai","symbol":"invectai","name":"InvectAI"},{"id":"inverse-ethereum-volatility-index-token","symbol":"iethv","name":"Inverse Ethereum Volatility Index Token"},{"id":"inverse-finance","symbol":"inv","name":"Inverse Finance"},{"id":"investdex","symbol":"invest","name":"InvestDex"},{"id":"investin","symbol":"ivn","name":"Investin"},{"id":"invest-like-stakeborg-index","symbol":"ilsi","name":"Invest Like Stakeborg Index"},{"id":"invictus","symbol":"in","name":"Invictus"},{"id":"invictus-hyprion-fund","symbol":"ihf","name":"Invictus Hyperion Fund"},{"id":"invi-token","symbol":"invi","name":"INVI"},{"id":"invoke","symbol":"iv","name":"Invoker"},{"id":"invox-finance","symbol":"invox","name":"Invox Finance"},{"id":"inx-token-2","symbol":"inx","name":"INX Token"},{"id":"iobusd","symbol":"iobusd","name":"ioBUSD"},{"id":"iocoin","symbol":"ioc","name":"I/O Coin"},{"id":"ioeth","symbol":"ioeth","name":"ioETH"},{"id":"ioex","symbol":"ioex","name":"ioeX"},{"id":"ioi-token","symbol":"ioi","name":"IOI"},{"id":"ion","symbol":"ion","name":"Ion"},{"id":"ionomy","symbol":"ion","name":"Ionomy"},{"id":"iostoken","symbol":"iost","name":"IOST"},{"id":"iota","symbol":"miota","name":"IOTA"},{"id":"iotex","symbol":"iotx","name":"IoTeX"},{"id":"iotex-monster-go","symbol":"mtgo","name":"Iotex Monster Go"},{"id":"iotexpad","symbol":"tex","name":"IoTeXPad"},{"id":"iotexshiba","symbol":"ioshib","name":"IoTexShiba"},{"id":"iouni","symbol":"iouni","name":"ioUNI"},{"id":"iousdc","symbol":"iousdc","name":"ioUSDC"},{"id":"iousdt","symbol":"iousdt","name":"ioUSDT"},{"id":"iowbtc","symbol":"iowbtc","name":"ioWBTC"},{"id":"iown","symbol":"iown","name":"iOWN"},{"id":"ipay","symbol":"ipay","name":"iPay"},{"id":"ipi-shorter","symbol":"ipistr","name":"Shorter Finance"},{"id":"ipor","symbol":"ipor","name":"IPOR"},{"id":"ipulse","symbol":"pls","name":"iPulse"},{"id":"ipverse","symbol":"ipv","name":"IPVERSE"},{"id":"ipx-token","symbol":"ipx","name":"Tachyon Protocol"},{"id":"iq-cash","symbol":"iq","name":"IQ.cash"},{"id":"iqeon","symbol":"iqn","name":"IQeon"},{"id":"iqoniq","symbol":"iqq","name":"Iqoniq"},{"id":"irena-green-energy","symbol":"irena","name":"IRENA Green Energy"},{"id":"iridium","symbol":"ird","name":"Iridium"},{"id":"iris-ecosystem","symbol":"iristoken","name":"Iris Ecosystem"},{"id":"iris-network","symbol":"iris","name":"IRISnet"},{"id":"iris-token-2","symbol":"iris","name":"Iris"},{"id":"iron-bank","symbol":"ib","name":"Iron Bank"},{"id":"iron-bank-chf","symbol":"ibchf","name":"Iron Bank CHF"},{"id":"iron-bank-euro","symbol":"ibeur","name":"Iron Bank EUR"},{"id":"iron-bank-gbp","symbol":"ibgbp","name":"Iron Bank GBP"},{"id":"iron-bsc","symbol":"iron","name":"Iron BSC"},{"id":"iron-finance","symbol":"ice","name":"Iron Finance"},{"id":"iron-stablecoin","symbol":"iron","name":"Iron"},{"id":"iron-titanium-token","symbol":"titan","name":"IRON Titanium"},{"id":"isengard-nft-marketplace","symbol":"iset-84e55e","name":"Isengard NFT Marketplace"},{"id":"ishares-msci-world-etf-tokenized-stock-defichain","symbol":"durth","name":"iShares MSCI World ETF Tokenized Stock Defichain"},{"id":"isiklar-coin","symbol":"isikc","name":"Isiklar Coin"},{"id":"iskra-token","symbol":"isk","name":"ISKRA Token"},{"id":"islamicoin","symbol":"islami","name":"ISLAMICOIN"},{"id":"islander","symbol":"isa","name":"Islander"},{"id":"ispolink","symbol":"isp","name":"Ispolink"},{"id":"istable","symbol":"i-stable","name":"iStable"},{"id":"istanbul-basaksehir-fan-token","symbol":"ibfk","name":"İstanbul Başakşehir Fan Token"},{"id":"istanbul-wild-cats-fan-token","symbol":"iwft","name":"İstanbul Wild Cats Fan Token"},{"id":"istardust","symbol":"isdt","name":"Istardust"},{"id":"istep","symbol":"istep","name":"iSTEP"},{"id":"italian-national-football-team-fan-token","symbol":"ita","name":"Italian National Football Team Fan Token"},{"id":"itam-games","symbol":"itam","name":"ITAM Games"},{"id":"itc","symbol":"itc","name":"ITC"},{"id":"itemverse","symbol":"item","name":"ITEMVERSE"},{"id":"iteration-syndicate","symbol":"its","name":"Iteration Syndicate"},{"id":"itheum","symbol":"itheum","name":"Itheum"},{"id":"itrust-governance-token","symbol":"itg","name":"iTrust Governance"},{"id":"itsbloc","symbol":"itsb","name":"ITSBLOC"},{"id":"itsmyne","symbol":"myne","name":"ITSMYNE"},{"id":"itube","symbol":"itube","name":"iTube"},{"id":"iusd","symbol":"iusd","name":"iUSD"},{"id":"ivar-coin","symbol":"ivar","name":"Ivar Coin"},{"id":"ivogel","symbol":"ivg","name":"IVOGEL"},{"id":"i-will-poop-it-nft","symbol":"shit","name":"I will poop it NFT"},{"id":"ixcoin","symbol":"ixc","name":"Ixcoin"},{"id":"ixicash","symbol":"ixi","name":"IxiCash"},{"id":"ixirswap","symbol":"ixir","name":"Ixirswap"},{"id":"ixo","symbol":"ixo","name":"IXO"},{"id":"ix-swap","symbol":"ixs","name":"IX Swap"},{"id":"ix-token","symbol":"ixt","name":"IX"},{"id":"ize","symbol":"ize","name":"IZE"},{"id":"izombie","symbol":"izu","name":"iZombie"},{"id":"izumi-bond-usd","symbol":"iusd","name":"iZUMi Bond USD"},{"id":"izumi-finance","symbol":"izi","name":"Izumi Finance"},{"id":"jackal-protocol","symbol":"jkl","name":"Jackal Protocol"},{"id":"jackpool-finance","symbol":"jfi","name":"JackPool.finance"},{"id":"jackpot","symbol":"777","name":"Jackpot"},{"id":"jackpotdoge","symbol":"jpd","name":"JackpotDoge"},{"id":"jack-token","symbol":"jack","name":"Jack Token"},{"id":"jacy","symbol":"jacy","name":"JACY"},{"id":"jade","symbol":"jade","name":"DeFi Kingdoms Jade"},{"id":"jade-currency","symbol":"jade","name":"Jade Currency"},{"id":"jade-protocol","symbol":"jade","name":"Jade Protocol"},{"id":"jaiho-crypto","symbol":"jaiho","name":"Jaiho Crypto"},{"id":"jail-kwon","symbol":"jkwon","name":"JAIL KWON"},{"id":"janus-network","symbol":"jns","name":"Janus Network"},{"id":"jarvis","symbol":"jar","name":"Jarvis+"},{"id":"jarvis-reward-token","symbol":"jrt","name":"Jarvis Reward"},{"id":"jarvis-synthetic-british-pound","symbol":"jgbp","name":"Jarvis Synthetic British Pound"},{"id":"jarvis-synthetic-euro","symbol":"jeur","name":"Jarvis Synthetic Euro"},{"id":"jarvis-synthetic-japanese-yen","symbol":"jjpy","name":"Jarvis Synthetic Japanese Yen"},{"id":"jarvis-synthetic-swiss-franc","symbol":"jchf","name":"Jarvis Synthetic Swiss Franc"},{"id":"jasan-wellness","symbol":"jw","name":"Jasan Wellness"},{"id":"jasmycoin","symbol":"jasmy","name":"JasmyCoin"},{"id":"javascript-token","symbol":"js","name":"JavaScript"},{"id":"jax-network","symbol":"wjxn","name":"Jax.Network"},{"id":"jd-coin","symbol":"jdc","name":"JD Coin"},{"id":"jedstar","symbol":"$jed","name":"JEDSTAR"},{"id":"jeet-detector-bot","symbol":"jdb","name":"JDB"},{"id":"jefe","symbol":"jefe","name":"Jefe"},{"id":"jejudoge","symbol":"jejudoge","name":"Jejudoge"},{"id":"jelly-esports","symbol":"jelly","name":"Jelly eSports"},{"id":"jem","symbol":"jem","name":"Jem"},{"id":"jen-coin","symbol":"jen","name":"JEN COIN"},{"id":"jenny-dao-v2","symbol":"jenny","name":"Jenny DAO V2"},{"id":"jenny-metaverse-dao-token","symbol":"ujenny","name":"Jenny DAO V1"},{"id":"jet","symbol":"jet","name":"JET"},{"id":"jetcoin","symbol":"jet","name":"Jetcoin"},{"id":"jetoken","symbol":"jets","name":"JeToken"},{"id":"jetset","symbol":"jts","name":"Jetset"},{"id":"jfin-coin","symbol":"jfin","name":"JFIN Coin"},{"id":"jigen","symbol":"jig","name":"Jigen"},{"id":"jigstack","symbol":"stak","name":"Jigstack"},{"id":"jimizz","symbol":"jmz","name":"Jimizz"},{"id":"jimngalaxy","symbol":"jimn","name":"JimnGalaxy"},{"id":"jindo-inu","symbol":"jind","name":"Jindo Inu"},{"id":"jito-staked-sol","symbol":"jitosol","name":"Jito Staked SOL"},{"id":"jiyuu","symbol":"jiyuu","name":"Jiyuu"},{"id":"jizzrocket","symbol":"jizz","name":"JizzRocket"},{"id":"jk-coin","symbol":"jk","name":"JK Coin"},{"id":"jobchain","symbol":"job","name":"Jobchain"},{"id":"joe","symbol":"joe","name":"JOE"},{"id":"joe-hat-token","symbol":"hat","name":"Joe Hat"},{"id":"joe-yo-coin","symbol":"jyc","name":"Joe-Yo Coin"},{"id":"jojo","symbol":"jojo","name":"JOJO"},{"id":"jojos-adventure","symbol":"jojo","name":"JoJos Adventure"},{"id":"jokermanor-metaverse","symbol":"jkt","name":"JokerManor Metaverse"},{"id":"jokes-meme","symbol":"joke","name":"Jokes Meme"},{"id":"joltify","symbol":"jolt","name":"Joltify"},{"id":"jomon-shiba","symbol":"jshiba","name":"Jomon Shiba"},{"id":"jones-dao","symbol":"jones","name":"Jones DAO"},{"id":"jones-glp","symbol":"jglp","name":"Jones GLP"},{"id":"jones-usdc","symbol":"jusdc","name":"Jones USDC"},{"id":"jot-art","symbol":"jot","name":"Jot Art"},{"id":"joulecoin","symbol":"xjo","name":"Joulecoin"},{"id":"journart","symbol":"jart","name":"JournArt"},{"id":"joys","symbol":"joys","name":"JOYS"},{"id":"joystick1","symbol":"joy","name":"Joystick"},{"id":"joystick-club","symbol":"joy","name":"Joystick.club"},{"id":"jp","symbol":"jp","name":"JP"},{"id":"jpeg-d","symbol":"jpeg","name":"JPEG'd"},{"id":"jpegvaultdao-2","symbol":"jp3g","name":"JP3Gvault"},{"id":"jpex-coin","symbol":"jpc","name":"JPEX Coin"},{"id":"jpg-nft-index","symbol":"jpg","name":"JPG NFT Index"},{"id":"jpgoldcoin","symbol":"jpgc","name":"JPGoldCoin"},{"id":"jpool","symbol":"jsol","name":"JPool"},{"id":"jpyc","symbol":"jpyc","name":"JPY Coin v1"},{"id":"jpy-coin","symbol":"jpyc","name":"JPY Coin"},{"id":"jswap-finance","symbol":"jf","name":"Jswap.Finance"},{"id":"jubi-token","symbol":"jt","name":"Jubi Token"},{"id":"juggernaut","symbol":"jgn","name":"Juggernaut"},{"id":"juicebox","symbol":"jbx","name":"Juicebox"},{"id":"jujube","symbol":"jujube","name":"Jujube"},{"id":"julien","symbol":"julien","name":"JULIEN"},{"id":"julswap","symbol":"juld","name":"JulSwap"},{"id":"jumbo-exchange","symbol":"jumbo","name":"Jumbo Exchange"},{"id":"jump-defi","symbol":"jump","name":"Jump DeFi"},{"id":"jumptoken","symbol":"jmpt","name":"JumpToken"},{"id":"jumpx","symbol":"jumpx","name":"JumpX"},{"id":"junca-cash","symbol":"jcc","name":"Junca cash"},{"id":"jungle","symbol":"jungle","name":"Jungle"},{"id":"jungle-defi","symbol":"jfi","name":"Jungle DeFi"},{"id":"jungleking-tigercoin","symbol":"tiger","name":"JungleKing TigerCoin"},{"id":"juno-network","symbol":"juno","name":"JUNO"},{"id":"jupiter","symbol":"jup","name":"Jupiter"},{"id":"jur","symbol":"jur","name":"Jur"},{"id":"just","symbol":"jst","name":"JUST"},{"id":"justcarbon-governance","symbol":"jcg","name":"JustCarbon Governance"},{"id":"justfarm","symbol":"jfm","name":"JustFarm"},{"id":"justmoney-2","symbol":"jm","name":"JustMoney"},{"id":"just-stablecoin","symbol":"usdj","name":"JUST Stablecoin"},{"id":"juventus-fan-token","symbol":"juv","name":"Juventus Fan Token"},{"id":"k21","symbol":"k21","name":"K21"},{"id":"k9","symbol":"k9","name":"K9"},{"id":"kaafila","symbol":"kfl","name":"Kaafila"},{"id":"kabosu","symbol":"kabosu","name":"Kabosu"},{"id":"kabosuceo","symbol":"kceo","name":"KabosuCEO"},{"id":"kabosu-inu","symbol":"kabosu inu","name":"Kabosu Inu"},{"id":"kaby-arena","symbol":"kaby","name":"Kaby Arena"},{"id":"kaby-gaming-token","symbol":"kgt","name":"Kaby Gaming"},{"id":"kaddex","symbol":"kdx","name":"eckoDAO"},{"id":"kadena","symbol":"kda","name":"Kadena"},{"id":"kaeri","symbol":"kaeri","name":"Kaeri"},{"id":"kagla-finance","symbol":"kgl","name":"Kagla Finance"},{"id":"kaidex","symbol":"kdx","name":"Kaidex"},{"id":"kai-inu","symbol":"kaiinu","name":"Kai Inu"},{"id":"kainet","symbol":"kainet","name":"KAINET"},{"id":"kaizen","symbol":"kzen","name":"Kaizen"},{"id":"kaka-nft-world","symbol":"kaka","name":"KAKA NFT World"},{"id":"kala","symbol":"kala","name":"Kala"},{"id":"kalamint","symbol":"kalam","name":"Kalamint"},{"id":"kalao","symbol":"klo","name":"Kalao"},{"id":"kalima-blockchain","symbol":"klx","name":"Kalima Blockchain"},{"id":"kalissa","symbol":"kali","name":"Kalissa"},{"id":"kalisten","symbol":"ks","name":"Kalisten"},{"id":"kalkulus","symbol":"klks","name":"Kalkulus"},{"id":"kalmar","symbol":"kalm","name":"KALM"},{"id":"kamaleont","symbol":"klt","name":"Kamaleont"},{"id":"kambria","symbol":"kat","name":"Kambria"},{"id":"kampay","symbol":"kampay","name":"Kampay"},{"id":"kan","symbol":"kan","name":"BitKan"},{"id":"kanagawa-nami","symbol":"okinami","name":"Kanagawa Nami"},{"id":"kanaloa-network","symbol":"kana","name":"Kanaloa Network"},{"id":"kang3n","symbol":"kang3n","name":"kang3n"},{"id":"kanga-exchange","symbol":"kng","name":"Kanga Exchange"},{"id":"kangal","symbol":"kangal","name":"Kangal"},{"id":"kanpeki","symbol":"kae","name":"Kanpeki"},{"id":"kapital-dao","symbol":"kap","name":"Kapital DAO"},{"id":"karastar-umy","symbol":"umy","name":"KaraStar UMY"},{"id":"karbo","symbol":"krb","name":"Karbo"},{"id":"kardiachain","symbol":"kai","name":"KardiaChain"},{"id":"karencoin","symbol":"karen","name":"KarenCoin"},{"id":"karma-dao","symbol":"karma","name":"Karma DAO"},{"id":"karmaverse","symbol":"knot","name":"Karmaverse"},{"id":"karmaverse-zombie-serum","symbol":"serum","name":"Karmaverse Zombie Serum"},{"id":"karsiyaka-taraftar-token","symbol":"ksk","name":"Karşıyaka Taraftar Fan Token"},{"id":"karura","symbol":"kar","name":"Karura"},{"id":"kasa-central","symbol":"kasa","name":"Kasa Central"},{"id":"kashhcoin","symbol":"kashh","name":"Kashhcoin"},{"id":"kaspa","symbol":"kas","name":"Kaspa"},{"id":"kassandra","symbol":"kacy","name":"Kassandra"},{"id":"kasta","symbol":"kasta","name":"Kasta"},{"id":"katalyo","symbol":"ktlyo","name":"Katalyo"},{"id":"katana-inu","symbol":"kata","name":"Katana Inu"},{"id":"kattana","symbol":"ktn","name":"Kattana"},{"id":"kava","symbol":"kava","name":"Kava"},{"id":"kava-lend","symbol":"hard","name":"Kava Lend"},{"id":"kava-swap","symbol":"swp","name":"Kava Swap"},{"id":"kawaii-islands","symbol":"kwt","name":"Kawaii Islands"},{"id":"kawakami","symbol":"kawa","name":"Kawakami"},{"id":"kay-pacha","symbol":"pacha","name":"Kay Pacha"},{"id":"kayserispor","symbol":"kysr","name":"Kayserispor"},{"id":"kcal","symbol":"kcal","name":"KCAL"},{"id":"kcash","symbol":"kcash","name":"Kcash"},{"id":"kcc-memepad","symbol":"kccm","name":"KCC MemePad"},{"id":"kccpad","symbol":"kccpad","name":"KCCPad"},{"id":"kdag","symbol":"kdag","name":"King DAG"},{"id":"kdlaunch","symbol":"kdl","name":"KDLaunch"},{"id":"kdswap","symbol":"kds","name":"KDSwap"},{"id":"keep3rv1","symbol":"kp3r","name":"Keep3rV1"},{"id":"keep4r","symbol":"kp4r","name":"Keep4r"},{"id":"keep-network","symbol":"keep","name":"Keep Network"},{"id":"keeps-coin","symbol":"kverse","name":"KEEPs Coin"},{"id":"keeshond","symbol":"$ksh","name":"Keeshond"},{"id":"keisuke-inu","symbol":"$kei","name":"Keisuke Inu"},{"id":"kekchain","symbol":"kek","name":"KeKChain"},{"id":"kekwcoin","symbol":"kekw","name":"Kekwcoin"},{"id":"kelvpn","symbol":"kel","name":"KelVPN"},{"id":"kemacoin","symbol":"kema","name":"KemaCoin"},{"id":"kennel-locker","symbol":"kennel","name":"Kennel"},{"id":"kenshi","symbol":"kenshi","name":"Kenshi"},{"id":"kephi-gallery","symbol":"kphi","name":"Kephi Gallery"},{"id":"kermit","symbol":"kermit","name":"Kermit Finance"},{"id":"keyfi","symbol":"keyfi","name":"KeyFi"},{"id":"keysatin","symbol":"keysatin","name":"KeySATIN"},{"id":"keys-token","symbol":"keys","name":"Keys"},{"id":"keytango","symbol":"tango","name":"keyTango"},{"id":"khalifa-finance","symbol":"khalifa","name":"Khalifa Finance"},{"id":"khaos-finance","symbol":"khaos","name":"Khaos Finance"},{"id":"ki","symbol":"xki","name":"KI"},{"id":"kiba-inu","symbol":"kiba","name":"Kiba Inu"},{"id":"kibbleswap","symbol":"kib","name":"KibbleSwap"},{"id":"kiboshib","symbol":"kibshi","name":"KiboShib"},{"id":"kichicoin","symbol":"kich","name":"KichiCoin"},{"id":"kick","symbol":"kick","name":"Kick"},{"id":"kick-io","symbol":"kick","name":"KICK.IO"},{"id":"kickpad","symbol":"kpad","name":"KickPad"},{"id":"killthezero","symbol":"ktz","name":"KILLTHEZERO"},{"id":"kilopi-8ee65670-efa5-4414-b9b4-1a1240415d74","symbol":"lop","name":"Kilopi"},{"id":"kilt-protocol","symbol":"kilt","name":"KILT Protocol"},{"id":"kimchi-finance","symbol":"kimchi","name":"KIMCHI.finance"},{"id":"kin","symbol":"kin","name":"Kin"},{"id":"kindness-for-soul","symbol":"kfs g","name":"Kindness For Soul"},{"id":"kinect-finance","symbol":"knt","name":"Kinect Finance"},{"id":"kineko","symbol":"kko","name":"KKO Protocol"},{"id":"kineko-knk","symbol":"knk","name":"Kineko"},{"id":"kine-protocol","symbol":"kine","name":"Kine Protocol"},{"id":"king","symbol":"king","name":"King Finance"},{"id":"king-arthur","symbol":"bking","name":"King Arthur"},{"id":"kingaru","symbol":"kru","name":"Kingaru"},{"id":"kingcorgi-chain","symbol":"kcc","name":"KingCorgi Chain"},{"id":"kingdoge","symbol":"kdoge","name":"KingDoge"},{"id":"king-dog-inu","symbol":"kingdog","name":"King Dog Inu"},{"id":"kingdom-game-4-0","symbol":"kdg","name":"Kingdom Game 4.0"},{"id":"kingdom-karnage","symbol":"kkt","name":"Kingdom Karnage"},{"id":"kingdom-quest","symbol":"kgc","name":"Kingdom Quest"},{"id":"kingdom-raids","symbol":"krs","name":"Kingdom Raids"},{"id":"kingdomswap-2","symbol":"ks2","name":"Kingdom Swap 2.0"},{"id":"kingdomverse","symbol":"king","name":"Kingdomverse"},{"id":"kingdomx","symbol":"kt","name":"KingdomX"},{"id":"king-forever","symbol":"kfr","name":"KING FOREVER"},{"id":"kingmaker","symbol":"power","name":"Kingmaker"},{"id":"king-of-legends-2","symbol":"kol","name":"King of Legends"},{"id":"kingpad","symbol":"crown","name":"KingPad"},{"id":"king-shiba","symbol":"kingshib","name":"King Shiba"},{"id":"kingspeed","symbol":"ksc","name":"KingSpeed"},{"id":"kintsugi","symbol":"kint","name":"Kintsugi"},{"id":"kintsugi-btc","symbol":"kbtc","name":"Kintsugi BTC"},{"id":"kira","symbol":"kira","name":"KIRA"},{"id":"kira-network","symbol":"kex","name":"KIRA Network"},{"id":"kirobo","symbol":"kiro","name":"KIRO"},{"id":"kishimoto","symbol":"kishimoto","name":"Kishimoto"},{"id":"kishimoto-inu","symbol":"kishimoto","name":"Kishimoto (old)"},{"id":"kishu-inu","symbol":"kishu","name":"Kishu Inu"},{"id":"kishu-ken","symbol":"kishk","name":"Kishu Ken"},{"id":"kishutama","symbol":"kishutama","name":"Kishutama"},{"id":"kissan","symbol":"ksn","name":"Kissan"},{"id":"kitsumon","symbol":"$kmc","name":"Kitsumon"},{"id":"kitsune-inu-2","symbol":"kitsune","name":"Kitsune Inu"},{"id":"kitsune-mask","symbol":"kmask","name":"Kitsune Mask"},{"id":"kittee","symbol":"kte","name":"KITTEE"},{"id":"kitten-coin","symbol":"kittens","name":"Kitten Coin"},{"id":"kittenfinance","symbol":"kif","name":"KittenFinance"},{"id":"kitti","symbol":"kitti","name":"KITTI"},{"id":"kitty","symbol":"kit","name":"Kitty"},{"id":"kittycake","symbol":"kcake","name":"KittyCake"},{"id":"kitty-coin-solana","symbol":"kitty","name":"Kitty Coin Solana"},{"id":"kitty-inu","symbol":"kitty","name":"Kitty Inu"},{"id":"kitty-solana","symbol":"kitty","name":"Kitty Solana"},{"id":"kiwi","symbol":"kiwi","name":"kiwi"},{"id":"kiwigo","symbol":"kgo","name":"Kiwigo"},{"id":"klap-finance","symbol":"klap","name":"Klap Finance"},{"id":"klaycity-orb","symbol":"orb","name":"Orbcity"},{"id":"klaydice","symbol":"dice","name":"Klaydice"},{"id":"klayfi-finance","symbol":"kfi","name":"KlayFi Finance"},{"id":"klayswap-protocol","symbol":"ksp","name":"KlaySwap Protocol"},{"id":"klaytn-dai","symbol":"kdai","name":"Klaytn Dai"},{"id":"klay-token","symbol":"klay","name":"Klaytn"},{"id":"klayuniverse","symbol":"kut","name":"KlayUniverse"},{"id":"kleekai","symbol":"klee","name":"KleeKai"},{"id":"klend","symbol":"klt","name":"KLend"},{"id":"kleomedes","symbol":"kleo","name":"Kleomedes"},{"id":"kleros","symbol":"pnk","name":"Kleros"},{"id":"kleva","symbol":"kleva","name":"KLEVA"},{"id":"klever","symbol":"klv","name":"Klever"},{"id":"klever-finance","symbol":"kfi","name":"Klever Finance"},{"id":"klima-dao","symbol":"klima","name":"KlimaDAO"},{"id":"kling","symbol":"kling","name":"Kling"},{"id":"knights-peasants","symbol":"knight","name":"Knights \u0026 Peasants"},{"id":"knightswap","symbol":"knight","name":"KnightSwap"},{"id":"knight-war-spirits","symbol":"kws","name":"Knight War Spirits"},{"id":"knit-finance","symbol":"kft","name":"Knit Finance"},{"id":"knoxfs","symbol":"kfx","name":"KnoxFS"},{"id":"koacombat","symbol":"koacombat","name":"KoaCombat"},{"id":"koakuma","symbol":"kkma","name":"Koakuma"},{"id":"koala-token","symbol":"mkoala","name":"Koala"},{"id":"kobocoin","symbol":"kobo","name":"Kobocoin"},{"id":"kocaelispor-fan-token","symbol":"kstt","name":"Kocaelispor Fan Token"},{"id":"kochi-ken","symbol":"kochi","name":"Kochi Ken"},{"id":"kodachi-token","symbol":"kodachi","name":"Kodachi Token"},{"id":"koda-finance","symbol":"koda","name":"Koda Cryptocurrency"},{"id":"kodi","symbol":"kodi","name":"KODI"},{"id":"kogecoin","symbol":"kogecoin","name":"KogeCoin"},{"id":"koi-network","symbol":"koi","name":"Koi Network"},{"id":"koinos","symbol":"koin","name":"Koinos"},{"id":"koisan","symbol":"kic","name":"Koisan"},{"id":"koji","symbol":"koji","name":"Koji"},{"id":"kok","symbol":"kok","name":"KOK"},{"id":"kokoa-finance","symbol":"kokoa","name":"Kokoa Finance"},{"id":"kokoa-stable-dollar","symbol":"ksd","name":"Kokoa Stable Dollar"},{"id":"kokomo-finance","symbol":"koko","name":"Kokomo Finance"},{"id":"kolibri-dao","symbol":"kdao","name":"Kolibri DAO"},{"id":"kolibri-usd","symbol":"kusd","name":"Kolibri USD"},{"id":"kollect","symbol":"kol","name":"Kollect"},{"id":"kollector","symbol":"kltr","name":"Kollector"},{"id":"kolnet","symbol":"kolnet","name":"KOLNET"},{"id":"kols-offering-token","symbol":"kot","name":"Kols Offering"},{"id":"kommunitas","symbol":"kom","name":"Kommunitas"},{"id":"komodo","symbol":"kmd","name":"Komodo"},{"id":"kompete","symbol":"kompete","name":"KOMPETE Token"},{"id":"kondux-v2","symbol":"kndx","name":"KONDUX"},{"id":"konjungate","symbol":"konj","name":"KONJUNGATE"},{"id":"konnect","symbol":"kct","name":"Konnect"},{"id":"konomi-network","symbol":"kono","name":"Konomi Network"},{"id":"konpay","symbol":"kon","name":"KonPay"},{"id":"korea-entertainment-education-shopping","symbol":"kees","name":"Korea Entertainment Education \u0026 Shopping"},{"id":"kori-inu","symbol":"kori","name":"Kori Inu"},{"id":"koromaru","symbol":"koromaru","name":"KOROMARU"},{"id":"korss-chain-launchpad","symbol":"kclp","name":"Kross Chain Launchpad"},{"id":"kostren-finance","symbol":"ktn","name":"Kostren Finance"},{"id":"kounotori","symbol":"kto","name":"Kounotori"},{"id":"koyo-6e93c7c7-03a3-4475-86a1-f0bc80ee09d6","symbol":"koy","name":"Koyo"},{"id":"kpop-coin","symbol":"kpop","name":"KPOP Coin"},{"id":"krabots","symbol":"krac","name":"Krabots"},{"id":"kragger-inu","symbol":"kinu","name":"Kragger Inu"},{"id":"krakenpad","symbol":"krp","name":"Krakenpad"},{"id":"krause","symbol":"$krause","name":"KRAUSE"},{"id":"kred","symbol":"kred","name":"KRED"},{"id":"krida-fans","symbol":"krida","name":"Krida Fans"},{"id":"krill","symbol":"krill","name":"Krill"},{"id":"kripto","symbol":"kripto","name":"Kripto"},{"id":"kripto-galaxy-battle","symbol":"kaba","name":"Kripto Galaxy Battle"},{"id":"krogan","symbol":"kro","name":"Krogan"},{"id":"kromatika","symbol":"krom","name":"Kromatika"},{"id":"krown","symbol":"krw","name":"KROWN"},{"id":"kryll","symbol":"krl","name":"KRYLL"},{"id":"kryptomon","symbol":"kmon","name":"Kryptomon"},{"id":"krypton-dao","symbol":"krd","name":"Krypton DAO"},{"id":"krypton-token","symbol":"kgc","name":"Krypton Galaxy Coin"},{"id":"kryxivia-game","symbol":"kxa","name":"Kryxivia Game"},{"id":"kryza-exchange","symbol":"krx","name":"KRYZA Exchange"},{"id":"kryza-network","symbol":"krn","name":"KRYZA Network"},{"id":"ksm-starter","symbol":"kst","name":"Karus Starter"},{"id":"kstarcoin","symbol":"ksc","name":"KStarCoin"},{"id":"k-tune","symbol":"ktt","name":"K-Tune"},{"id":"kubecoin","symbol":"kube","name":"KubeCoin"},{"id":"kubic","symbol":"kubic","name":"Kubic"},{"id":"kucoin-shares","symbol":"kcs","name":"KuCoin"},{"id":"kudoe","symbol":"kdoe","name":"Kudoe"},{"id":"kudoge","symbol":"kudo","name":"KuDoge"},{"id":"kujira","symbol":"kuji","name":"Kujira"},{"id":"kuky-star","symbol":"kuky","name":"Kuky Star"},{"id":"kulupu","symbol":"klp","name":"Kulupu"},{"id":"kumadex-token","symbol":"dkuma","name":"KumaDex Token"},{"id":"kuma-inu","symbol":"kuma","name":"Kuma Inu"},{"id":"kumamon-finance","symbol":"kumamon","name":"Kumamon Finance"},{"id":"kunci-coin","symbol":"kunci","name":"Kunci Coin"},{"id":"kurai-inu","symbol":"kurai","name":"Kurai Inu"},{"id":"kurobi","symbol":"kuro","name":"Kurobi"},{"id":"kuro-shiba","symbol":"kuro","name":"Kuro Shiba"},{"id":"kusama","symbol":"ksm","name":"Kusama"},{"id":"kusd-t","symbol":"kusd-t","name":"KUSD-T"},{"id":"kusunoki-samurai","symbol":"kusunoki","name":"Kusunoki Samurai"},{"id":"kuswap","symbol":"kus","name":"KuSwap"},{"id":"kuwa-oracle","symbol":"kuor","name":"Kuwa Oracle"},{"id":"kuy-token","symbol":"kuy","name":"KUY"},{"id":"kwai","symbol":"kwai","name":"KWAI"},{"id":"kwenta","symbol":"kwenta","name":"Kwenta"},{"id":"kwiktrust","symbol":"ktx","name":"KwikTrust"},{"id":"kyanite","symbol":"kyan","name":"Kyanite"},{"id":"kyberdyne","symbol":"kbd","name":"Kyberdyne"},{"id":"kyber-network","symbol":"kncl","name":"Kyber Network Crystal Legacy"},{"id":"kyber-network-crystal","symbol":"knc","name":"Kyber Network Crystal"},{"id":"kyccoin","symbol":"kycc","name":"KYCCOIN"},{"id":"kylin-network","symbol":"kyl","name":"Kylin Network"},{"id":"kyoko","symbol":"kyoko","name":"Kyoko"},{"id":"kyotoswap","symbol":"kswap","name":"KyotoSwap"},{"id":"kyrrex","symbol":"krrx","name":"Kyrrex"},{"id":"kyte-one","symbol":"kte","name":"Kyte.One"},{"id":"kzcash","symbol":"kzc","name":"Kzcash"},{"id":"l3usd","symbol":"l3usd","name":"L3USD"},{"id":"label-foundation","symbol":"lbl","name":"LABEL Foundation"},{"id":"labs-group","symbol":"labsv2","name":"LABSV2"},{"id":"labs-protocol","symbol":"labs","name":"LABS Protocol"},{"id":"laddercaster","symbol":"lada","name":"LadderCaster"},{"id":"lady-uni","symbol":"luni","name":"Lady Uni"},{"id":"laeeb","symbol":"laeeb","name":"LaEeb"},{"id":"la-eeb","symbol":"la´eeb","name":"La´eeb"},{"id":"la-eeb-football","symbol":"laeeb","name":"La'eeb Football"},{"id":"laeeb-inu-erc","symbol":"laeeb","name":"Laeeb Inu ERC"},{"id":"laeeb-token-2","symbol":"laeeb","name":"LaEeb Token"},{"id":"laika","symbol":"laika","name":"Laika"},{"id":"laine-stake","symbol":"lainesol","name":"Laine Stake"},{"id":"lakeviewmeta","symbol":"lvm","name":"LakeViewMeta"},{"id":"lambda","symbol":"lamb","name":"Lambda"},{"id":"lambda-markets","symbol":"lmda","name":"Lambda Markets"},{"id":"lamden","symbol":"tau","name":"Lamden"},{"id":"lanacoin","symbol":"lana","name":"LanaCoin"},{"id":"lance-coin","symbol":"lce","name":"Lance Coin"},{"id":"lanceria","symbol":"lanc","name":"Lanceria"},{"id":"landboard","symbol":"land","name":"Landboard"},{"id":"land-of-conquest-slg","symbol":"slg","name":"Land Of Conquest SLG"},{"id":"land-of-fantasy","symbol":"lof","name":"Land of Fantasy"},{"id":"landshare","symbol":"land","name":"Landshare"},{"id":"landworld","symbol":"lwd","name":"Landworld"},{"id":"lapad","symbol":"lpdt","name":"LaPAD"},{"id":"lapislazuli","symbol":"lilli","name":"Lapislazuli"},{"id":"laqira-protocol","symbol":"lqr","name":"Laqira Protocol"},{"id":"larix","symbol":"larix","name":"Larix"},{"id":"lasereyes","symbol":"lsr","name":"LaserEyes"},{"id":"lasrever","symbol":"lsvr","name":"Lasrever"},{"id":"last-survivor","symbol":"lsc","name":"Last Survivor"},{"id":"latoken","symbol":"la","name":"LA"},{"id":"lattice-token","symbol":"ltx","name":"Lattice"},{"id":"launchblock","symbol":"lbp","name":"LaunchBlock"},{"id":"launchpool","symbol":"lpool","name":"Launchpool"},{"id":"launchverse","symbol":"xlv","name":"LaunchVerse"},{"id":"launchwall","symbol":"wall","name":"LaunchWall"},{"id":"lavaswap","symbol":"lava","name":"Lavaswap"},{"id":"lavax-labs","symbol":"lavax","name":"LavaX Labs"},{"id":"law","symbol":"law","name":"LAW"},{"id":"law-blocks","symbol":"lbt","name":"Law Blocks"},{"id":"layer2dao","symbol":"l2dao","name":"Layer2DAO"},{"id":"lazio-fan-token","symbol":"lazio","name":"Lazio Fan Token"},{"id":"lbk","symbol":"lbk","name":"LBK"},{"id":"lbry-credits","symbol":"lbc","name":"LBRY Credits"},{"id":"lcms","symbol":"lcms","name":"LCMS"},{"id":"lcx","symbol":"lcx","name":"LCX"},{"id":"lead-token","symbol":"lead","name":"Lead"},{"id":"leaguedao-governance-token","symbol":"leag","name":"LeagueDAO Governance"},{"id":"league-of-ancients","symbol":"loa","name":"League of Ancients"},{"id":"league-of-kingdoms","symbol":"loka","name":"League of Kingdoms"},{"id":"league-of-pets","symbol":"glory","name":"League Of Pets"},{"id":"leandro-lopes","symbol":"lopes","name":"Leandro Lopes"},{"id":"lean-management-token","symbol":"lean","name":"Leancoin"},{"id":"leap-token","symbol":"leap","name":"LEAP Token"},{"id":"learning-cash-2","symbol":"ead","name":"Learning Cash"},{"id":"learning-star","symbol":"lstar","name":"Learning Star"},{"id":"leash","symbol":"leash","name":"Doge Killer"},{"id":"ledgerscore","symbol":"led","name":"LedgerScore"},{"id":"ledgis","symbol":"led","name":"Ledgis"},{"id":"ledgity","symbol":"lty","name":"Ledgity"},{"id":"leeds-united-fan-token","symbol":"lufc","name":"Leeds United Fan Token"},{"id":"legacy-ichi","symbol":"ichi","name":"Legacy ICHI"},{"id":"legend-of-fantasy-war","symbol":"lfw","name":"Linked Finance World"},{"id":"legends","symbol":"fwcl","name":"Legends"},{"id":"legends-of-aria","symbol":"aria","name":"Legends Of Aria"},{"id":"legends-of-mitra","symbol":"mita","name":"Legends of Mitra"},{"id":"legends-room","symbol":"more","name":"More"},{"id":"legia-warsaw-fan-token","symbol":"leg","name":"Legia Warsaw Fan Token"},{"id":"legion-network","symbol":"lgx","name":"Legion Network"},{"id":"lego-coin-v2","symbol":"lego","name":"Lego Coin V2"},{"id":"leicester-tigers-fan-token","symbol":"tigers","name":"Leicester Tigers Fan Token"},{"id":"leisure","symbol":"lis","name":"Leisure"},{"id":"leisuremeta","symbol":"lm","name":"LeisureMeta"},{"id":"leisurepay","symbol":"lpy","name":"LeisurePay"},{"id":"lemochain","symbol":"lemo","name":"LemoChain"},{"id":"lemonchain","symbol":"lemc","name":"LemonChain"},{"id":"lemond","symbol":"lemd","name":"Lemond"},{"id":"lemonn-token","symbol":"lmn","name":"Lemonn"},{"id":"lemon-token","symbol":"lemn","name":"Crypto Lemon"},{"id":"lenda","symbol":"lenda","name":"Lenda"},{"id":"lendefi","symbol":"ldfi","name":"Lendefi"},{"id":"lendexe","symbol":"lexe","name":"LendeXe Finance"},{"id":"lend-finance","symbol":"lend","name":"Lend Finance"},{"id":"lend-flare-dao-token","symbol":"lft","name":"Lend Flare Dao"},{"id":"lendhub","symbol":"lhb","name":"Lendhub"},{"id":"lenny-face","symbol":"( ͡° ͜ʖ ͡°)","name":"Lenny Face"},{"id":"leo","symbol":"leo","name":"Leo"},{"id":"leonicorn-swap-leons","symbol":"leons","name":"Leonicorn Swap LEONS"},{"id":"leonidasbilic","symbol":"lio","name":"Leonidasbilic"},{"id":"leonidas-token","symbol":"leonidas","name":"Leonidas Token"},{"id":"leopard","symbol":"leopard","name":"Leopard"},{"id":"leo-token","symbol":"leo","name":"LEO Token"},{"id":"lepasa","symbol":"lepa","name":"Lepasa"},{"id":"leprechaun-finance","symbol":"lep","name":"Leprechaun Finance"},{"id":"leslar-metaverse","symbol":"llverse","name":"LESLAR Metaverse"},{"id":"letcoinshop","symbol":"lcs","name":"LetCoinShop"},{"id":"lethean","symbol":"lthn","name":"Lethean"},{"id":"lets-go-brandon","symbol":"letsgo","name":"Lets Go Brandon"},{"id":"levante-ud-fan-token","symbol":"lev","name":"Levante U.D. Fan Token"},{"id":"leve-invest","symbol":"leve","name":"Leve Invest"},{"id":"level","symbol":"lvl","name":"Level"},{"id":"levelapp","symbol":"lvl","name":"LevelApp"},{"id":"levelg","symbol":"levelg","name":"LEVELG"},{"id":"level-governance","symbol":"lgo","name":"Level Governance"},{"id":"lever","symbol":"lever","name":"LeverFi"},{"id":"leverageinu","symbol":"levi","name":"LeverageInu"},{"id":"leverj-gluon","symbol":"l2","name":"Leverj Gluon"},{"id":"lever-network","symbol":"lev","name":"Lever Network"},{"id":"levolution","symbol":"levl","name":"Levolution"},{"id":"lfgswap-finance","symbol":"lfg","name":"LFGSwap Finance"},{"id":"lfgswap-finance-core","symbol":"lfg","name":"LFGSwap Finance(CORE)"},{"id":"lgcy-network","symbol":"lgcy","name":"LGCY Network"},{"id":"lhcoin","symbol":"lhcoin","name":"LHCoin"},{"id":"libera-financial","symbol":"libera","name":"Libera Financial"},{"id":"liber-coin","symbol":"lbr","name":"LIBER COIN"},{"id":"libero-financial","symbol":"libero","name":"Libero Financial"},{"id":"liberty-square-filth","symbol":"flth","name":"Liberty Square Filth"},{"id":"libfx","symbol":"libfx","name":"Libfx"},{"id":"libonomy","symbol":"lby","name":"Libonomy"},{"id":"libra-credit","symbol":"lba","name":"Libra Credit"},{"id":"libra-protocol","symbol":"lbr","name":"Libra Protocol"},{"id":"libre","symbol":"libre","name":"Libre"},{"id":"lichang","symbol":"lc","name":"Lichang"},{"id":"lido-dao","symbol":"ldo","name":"Lido DAO"},{"id":"lido-dao-wormhole","symbol":"ldo","name":"Lido DAO (Wormhole)"},{"id":"lido-on-kusama","symbol":"wstksm","name":"Lido on Kusama"},{"id":"lido-staked-matic","symbol":"stmatic","name":"Lido Staked Matic"},{"id":"lido-staked-polkadot","symbol":"stdot","name":"Lido Staked Polkadot"},{"id":"lido-staked-sol","symbol":"stsol","name":"Lido Staked SOL"},{"id":"lien","symbol":"lien","name":"Lien"},{"id":"lif3","symbol":"lif3","name":"LIF3"},{"id":"lif3-lshare","symbol":"lshare","name":"LIF3 LSHARE"},{"id":"life-coin","symbol":"lfc","name":"Life Coin"},{"id":"life-crypto","symbol":"life","name":"Life Crypto"},{"id":"life-token-v2","symbol":"ltnv2","name":"Life v2"},{"id":"lifinity","symbol":"lfnty","name":"Lifinity"},{"id":"lightcoin","symbol":"lhc","name":"Lightcoin"},{"id":"light-defi","symbol":"light","name":"Light Defi"},{"id":"lightening-cash","symbol":"lic","name":"Lightening Cash"},{"id":"lightning-bitcoin","symbol":"lbtc","name":"Lightning Bitcoin"},{"id":"lightningcash-gold","symbol":"lnc","name":"LightningCash"},{"id":"lightning-protocol","symbol":"light","name":"Lightning Protocol"},{"id":"light-year","symbol":"lc","name":"Light Year"},{"id":"lightyears","symbol":"year","name":"Lightyears"},{"id":"likecoin","symbol":"like","name":"LikeCoin"},{"id":"lil-floki","symbol":"lilfloki","name":"Lil Floki"},{"id":"lilly-finance","symbol":"ly","name":"Lilly Token"},{"id":"limcore","symbol":"limc","name":"LimCore"},{"id":"limestone-network","symbol":"limex","name":"Limestone Network"},{"id":"limitswap","symbol":"limit","name":"LimitSwap"},{"id":"limocoin-swap","symbol":"lmcswap","name":"Limocoin Swap"},{"id":"lina","symbol":"lina","name":"LINA"},{"id":"linda","symbol":"mrx","name":"Metrix Coin"},{"id":"linear","symbol":"lina","name":"Linear"},{"id":"linear-protocol","symbol":"linear","name":"LiNEAR Protocol"},{"id":"linework-coin","symbol":"lwc","name":"Linework Coin"},{"id":"lingose","symbol":"ling","name":"Lingose"},{"id":"link","symbol":"ln","name":"LINK"},{"id":"linkcoin-token","symbol":"lkn","name":"LinkCoin"},{"id":"linkdao","symbol":"lkd","name":"LinkDao"},{"id":"linkeye","symbol":"let","name":"Linkeye"},{"id":"link-machine-learning","symbol":"lml","name":"Link Machine Learning"},{"id":"linkpool","symbol":"lpl","name":"LinkPool"},{"id":"links","symbol":"links","name":"Links"},{"id":"linksync","symbol":"sync","name":"LinkSync"},{"id":"link-yvault","symbol":"yvlink","name":"LINK yVault"},{"id":"linspirit","symbol":"linspirit","name":"linSpirit"},{"id":"liondex","symbol":"lion","name":"LionDEX"},{"id":"lion-scrub-money-2","symbol":"lion","name":"Lion Scrub Money"},{"id":"lion-token","symbol":"lion","name":"Lion"},{"id":"liq-protocol","symbol":"liq","name":"LIQ Protocol"},{"id":"liquicats","symbol":"meow","name":"LiquiCats"},{"id":"liquid-astr","symbol":"nastr","name":"Liquid ASTR"},{"id":"liquid-collectibles","symbol":"lico","name":"Liquid Collectibles"},{"id":"liquid-cro","symbol":"lcro","name":"Liquid CRO"},{"id":"liquiddriver","symbol":"lqdr","name":"LiquidDriver"},{"id":"liquid-finance","symbol":"liqd","name":"Liquid Finance"},{"id":"liquid-icp","symbol":"licp","name":"Liquid ICP"},{"id":"liquidifty","symbol":"lqt","name":"Lifty"},{"id":"liquid-ksm","symbol":"lksm","name":"Liquid KSM"},{"id":"liquidlock","symbol":"lock","name":"LiquidLock"},{"id":"liquid-mercury","symbol":"merc","name":"Liquid Mercury"},{"id":"liquid-staked-ethereum","symbol":"lseth","name":"Liquid Staked ETH"},{"id":"liquid-staked-eth-index","symbol":"lseth","name":"Liquid Staked ETH Index"},{"id":"liquid-staking-crescent","symbol":"bcre","name":"Liquid Staking Crescent"},{"id":"liquid-staking-derivative","symbol":"lsd","name":"Liquid Staking Derivative"},{"id":"liquidus","symbol":"liq","name":"Liquidus"},{"id":"liquify-network","symbol":"liquify","name":"Liquify Network"},{"id":"liquity","symbol":"lqty","name":"Liquity"},{"id":"liquity-usd","symbol":"lusd","name":"Liquity USD"},{"id":"liqwid-finance","symbol":"lq","name":"Liqwid Finance"},{"id":"lisk","symbol":"lsk","name":"Lisk"},{"id":"listenify","symbol":"audio","name":"Listenify"},{"id":"lit","symbol":"lit","name":"LIT"},{"id":"litebar","symbol":"ltb","name":"LiteBar"},{"id":"litebitcoin","symbol":"lbtc","name":"LiteBitcoin"},{"id":"litecash","symbol":"cash","name":"Litecash"},{"id":"litecoin","symbol":"ltc","name":"Litecoin"},{"id":"litecoin-cash","symbol":"lcc","name":"Litecoin Cash"},{"id":"litecoin-plus","symbol":"spb","name":"SpiderByte"},{"id":"litecoin-ultra","symbol":"ltcu","name":"LiteCoin Ultra"},{"id":"litecoinz","symbol":"ltz","name":"LitecoinZ"},{"id":"litedoge","symbol":"ldoge","name":"LiteDoge"},{"id":"litentry","symbol":"lit","name":"Litentry"},{"id":"liteusd","symbol":"lite","name":"LiteUSD"},{"id":"litex","symbol":"lxt","name":"LITEX"},{"id":"litherium","symbol":"lith","name":"Litherium"},{"id":"lithium-2","symbol":"lithium","name":"Lithium"},{"id":"lithium-finance","symbol":"lith","name":"Lithium Finance"},{"id":"lithium-ventures","symbol":"ions","name":"Lithium Ventures"},{"id":"lithosphere","symbol":"litho","name":"Lithosphere"},{"id":"lition","symbol":"lit","name":"Lition"},{"id":"little-angry-bunny-v2","symbol":"lab-v2","name":"Little Angry Bunny v2"},{"id":"little-bunny-rocket","symbol":"lbr","name":"Little Bunny Rocket"},{"id":"littleghosts-ectoplasm","symbol":"ecto","name":"LittleGhosts Ectoplasm"},{"id":"littleinu","symbol":"linu","name":"LittleInu"},{"id":"little-rabbit-v2","symbol":"ltrbt","name":"Little Rabbit V2"},{"id":"little-ugly-duck","symbol":"lud","name":"Little Ugly Duck"},{"id":"litx","symbol":"litx","name":"Lith Token"},{"id":"liux","symbol":"liux","name":"LIUX"},{"id":"livegreen-coin","symbol":"lgc","name":"LiveGreen Coin"},{"id":"livepeer","symbol":"lpt","name":"Livepeer"},{"id":"lizardtoken-finance","symbol":"liz","name":"LizardToken.Finance"},{"id":"lnko-token","symbol":"lnko","name":"LNKO"},{"id":"loa-protocol","symbol":"loa","name":"LOA Protocol"},{"id":"lobster","symbol":"$lobster","name":"LOBSTER"},{"id":"localcoinswap","symbol":"lcs","name":"LocalCoinSwap"},{"id":"local-money","symbol":"local","name":"Local Money"},{"id":"localtrade","symbol":"ltt","name":"LocalTrade"},{"id":"local-traders","symbol":"lct","name":"Local Traders"},{"id":"locgame","symbol":"locg","name":"LOCG"},{"id":"lockchain","symbol":"loc","name":"LockTrip"},{"id":"locker-token","symbol":"lkt","name":"Locker Token"},{"id":"lockness","symbol":"lkn","name":"Lockness"},{"id":"locometa","symbol":"loco","name":"LocoMeta"},{"id":"locus-chain","symbol":"locus","name":"Locus Chain"},{"id":"lodestar","symbol":"lode","name":"Lodestar"},{"id":"lode-token","symbol":"lode","name":"LODE Token"},{"id":"lofi","symbol":"lofi","name":"LOFI"},{"id":"logos","symbol":"log","name":"LOGOS"},{"id":"loki-network","symbol":"oxen","name":"Oxen"},{"id":"lokr","symbol":"lkr","name":"Lokr"},{"id":"lol","symbol":"lol","name":"LOL"},{"id":"loltoken","symbol":"lol","name":"LOLTOKEN"},{"id":"lonelyfans","symbol":"lof","name":"LonelyFans"},{"id":"lookscoin","symbol":"look","name":"LooksCoin"},{"id":"looksrare","symbol":"looks","name":"LooksRare"},{"id":"loomi","symbol":"loomi","name":"Loomi"},{"id":"loom-network","symbol":"loomold","name":"Loom Network (OLD)"},{"id":"loom-network-new","symbol":"loom","name":"Loom Network (NEW)"},{"id":"loon-network","symbol":"loon","name":"Loon Network"},{"id":"loop","symbol":"loop","name":"LOOP"},{"id":"loopnetwork","symbol":"loop","name":"LoopNetwork"},{"id":"loopring","symbol":"lrc","name":"Loopring"},{"id":"loopswap","symbol":"lswap","name":"LoopSwap"},{"id":"loot","symbol":"loot","name":"Lootex"},{"id":"loot-token","symbol":"loot","name":"Loot"},{"id":"lopo","symbol":"lopo","name":"LOPO"},{"id":"lord-of-dragons","symbol":"logt","name":"Lord of Dragons"},{"id":"lord-of-power-golden-eagle","symbol":"gde","name":"Lord of Power Golden Eagle"},{"id":"lords","symbol":"lords","name":"LORDS"},{"id":"lordtoken","symbol":"ltt","name":"LordToken"},{"id":"loserchick-egg","symbol":"egg","name":"LoserChick EGG"},{"id":"loser-coin","symbol":"lowb","name":"Loser Coin"},{"id":"lossless","symbol":"lss","name":"Lossless"},{"id":"lost-world","symbol":"lost","name":"Lost World"},{"id":"lotto-arbitrum","symbol":"lotto","name":"Lotto Arbitrum"},{"id":"lot-trade","symbol":"lott","name":"LOT.TRADE"},{"id":"loud-market","symbol":"loud","name":"Loud Market"},{"id":"loungem","symbol":"lzm","name":"LoungeM"},{"id":"louvre-finance","symbol":"louvre","name":"Louvre Finance"},{"id":"lovechain","symbol":"lov","name":"LoveChain"},{"id":"lovelace-world","symbol":"lace","name":"Lovelace World"},{"id":"lovely-inu-finance","symbol":"lovely","name":"Lovely Inu finance"},{"id":"lovely-swap-token","symbol":"lst","name":"Lovely Swap"},{"id":"lovepot-token","symbol":"love","name":"LovePot"},{"id":"lox-network","symbol":"lox","name":"Lox Network"},{"id":"lp-3pool-curve","symbol":"3crv","name":"LP 3pool Curve"},{"id":"lp-finance","symbol":"lpfi","name":"LP Finance DAO"},{"id":"lpi-dao","symbol":"lpi","name":"LPI DAO"},{"id":"lp-renbtc-curve","symbol":"renbtccurve","name":"LP renBTC Curve"},{"id":"lp-scurve","symbol":"scurve","name":"LP-sCurve"},{"id":"lsdx-finance","symbol":"lsd","name":"LSDx Finance"},{"id":"lto-network","symbol":"lto","name":"LTO Network"},{"id":"ltradex","symbol":"ltex","name":"Ltradex"},{"id":"lua-token","symbol":"lua","name":"LuaSwap"},{"id":"luca","symbol":"luca","name":"LUCA"},{"id":"lucha","symbol":"lucha","name":"Lucha"},{"id":"lucidao","symbol":"lcd","name":"Lucidao"},{"id":"luck2earn","symbol":"luck","name":"Luck2Earn"},{"id":"lucky-block","symbol":"lblock","name":"Lucky Block"},{"id":"lucky-cats","symbol":"katz","name":"Lucky Cats"},{"id":"luckychip","symbol":"lc","name":"LuckyChip"},{"id":"lucky-lion","symbol":"lucky","name":"Lucky Lion"},{"id":"lucky-property-development-invest","symbol":"lpdi","name":"Lucky Property Development Invest"},{"id":"lucky-roo","symbol":"roo","name":"Lucky Roo"},{"id":"luckytoad","symbol":"toad","name":"LuckyToad"},{"id":"lucretius","symbol":"luc","name":"Lucretius"},{"id":"lucro","symbol":"lcr","name":"Lucro"},{"id":"lucrosus-capital","symbol":"$luca","name":"Lucrosus Capital"},{"id":"ludena-protocol","symbol":"ldn","name":"Ludena Protocol"},{"id":"ludos","symbol":"lud","name":"Ludos Protocol"},{"id":"luffy-inu","symbol":"luffy","name":"Luffy"},{"id":"lukso-token","symbol":"lyxe","name":"LUKSO"},{"id":"lulu-market-luck","symbol":"luck","name":"LULU Market Luck"},{"id":"lumenswap","symbol":"lsp","name":"Lumenswap"},{"id":"lumerin","symbol":"lmr","name":"Lumerin"},{"id":"lumi","symbol":"lumi","name":"LUMI"},{"id":"lumi-credits","symbol":"lumi","name":"LUMI Credits"},{"id":"lumiiitoken","symbol":"lumiii","name":"Lumiii"},{"id":"lum-network","symbol":"lum","name":"Lum Network"},{"id":"luna-ape-protocol","symbol":"$lunape","name":"Luna Ape Protocol"},{"id":"lunachow","symbol":"luchow","name":"LunaChow"},{"id":"lunadoge","symbol":"loge","name":"LunaDoge"},{"id":"lunafi","symbol":"lfi","name":"Lunafi"},{"id":"lunagens","symbol":"lung","name":"LunaGens"},{"id":"luna-inu","symbol":"linu","name":"Luna Inu"},{"id":"lunaone","symbol":"xln","name":"LunaOne"},{"id":"lunar","symbol":"lnr","name":"Lunar [OLD]"},{"id":"lunar-2","symbol":"lnr","name":"Lunar"},{"id":"luna-rush","symbol":"lus","name":"Luna Rush"},{"id":"lunatics","symbol":"lunat","name":"Lunatics"},{"id":"luna-wormhole","symbol":"lunc","name":"Terra Classic (Wormhole)"},{"id":"lunax","symbol":"lunax","name":"Stader LunaX"},{"id":"lunchdao","symbol":"lunch","name":"LunchDAO"},{"id":"lunch-money","symbol":"lmy","name":"Lunch Money"},{"id":"lunes","symbol":"lunes","name":"Lunes"},{"id":"lunr-token","symbol":"lunr","name":"Lunr"},{"id":"lunyr","symbol":"lun","name":"Lunyr"},{"id":"lusd","symbol":"lusd","name":"LUSD"},{"id":"lusd3crv-f","symbol":"lusd3crv","name":"LUSD3CRV-f"},{"id":"lusd-yvault","symbol":"yvlusd","name":"LUSD yVault"},{"id":"luto-cash","symbol":"luto","name":"Luto Cash"},{"id":"lux-bio-exchange-coin","symbol":"lbxc","name":"LUX BIO EXCHANGE COIN"},{"id":"luxcoin","symbol":"lux","name":"LUXCoin"},{"id":"luxetoken","symbol":"luxetoken","name":"LuxeToken"},{"id":"luxfi","symbol":"lxf","name":"LuxFi"},{"id":"luxo","symbol":"luxo","name":"Luxo"},{"id":"luxor","symbol":"lux","name":"Luxor"},{"id":"luxurious-pro-network-token","symbol":"lpnt","name":"Luxurious Pro Network"},{"id":"luxy","symbol":"luxy","name":"Luxy"},{"id":"luzion-protocol","symbol":"lzn","name":"Luzion Protocol"},{"id":"lvusd","symbol":"lvusd","name":"lvUSD"},{"id":"lydia-finance","symbol":"lyd","name":"Lydia Finance"},{"id":"lyfe-2","symbol":"lyfe","name":"Lyfe"},{"id":"lyfe-gold","symbol":"lgold","name":"Lyfe Gold"},{"id":"lyfe-silver","symbol":"lsilver","name":"Lyfe Silver"},{"id":"lympo","symbol":"lym","name":"Lympo"},{"id":"lympo-market-token","symbol":"lmt","name":"Lympo Market"},{"id":"lynkey","symbol":"lynk","name":"LynKey"},{"id":"lynx","symbol":"lynx","name":"Lynx"},{"id":"lyocredit","symbol":"lyo","name":"LYO Credit"},{"id":"lyptus-token","symbol":"lyptus","name":"Lyptus"},{"id":"lyra","symbol":"lyr","name":"Lyra"},{"id":"lyra-finance","symbol":"lyra","name":"Lyra Finance"},{"id":"m","symbol":"m","name":"M"},{"id":"m2","symbol":"m2","name":"M2"},{"id":"macaronswap","symbol":"mcrn","name":"MacaronSwap"},{"id":"machinecoin","symbol":"mac","name":"Machinecoin"},{"id":"madagascar-token","symbol":"$time","name":"Madagascar"},{"id":"mad-bucks","symbol":"mad","name":"MAD Bucks"},{"id":"madchad","symbol":"madchad","name":"MadChad"},{"id":"made-in-real-life","symbol":"mirl","name":"Made In Real Life"},{"id":"mad-hatter-society","symbol":"madhat","name":"Mad Hatter Society"},{"id":"mad-meerkat-etf","symbol":"metf","name":"Mad Meerkat ETF"},{"id":"mad-meerkat-optimizer","symbol":"mmo","name":"Mad Meerkat Optimizer"},{"id":"mad-meerkat-optimizer-polygon","symbol":"mmo","name":"Mad Meerkat Optimizer (Polygon)"},{"id":"mad-network","symbol":"mad","name":"MADNetwork"},{"id":"mad-usd","symbol":"musd","name":"Mad USD"},{"id":"madworld","symbol":"umad","name":"MADworld"},{"id":"mafacoin","symbol":"mafa","name":"MafaCoin"},{"id":"maga-coin","symbol":"maga","name":"MAGA Coin BSC"},{"id":"magic","symbol":"magic","name":"Magic"},{"id":"magic-beasties","symbol":"bsts","name":"Magic Beasties"},{"id":"magiccraft","symbol":"mcrt","name":"MagicCraft"},{"id":"magic-cube","symbol":"mcc","name":"Magic Cube Coin"},{"id":"magic-cube-finance","symbol":"mast","name":"Magic Cube Finance"},{"id":"magicdoge","symbol":"magicdoge","name":"MagicDOGE"},{"id":"magic-elpis-gem","symbol":"meg","name":"Magic Elpis Gem"},{"id":"magicglp","symbol":"magicglp","name":"MagicGLP"},{"id":"magic-internet-money","symbol":"mim","name":"Magic Internet Money"},{"id":"magic-manor","symbol":"mgc","name":"Magic Manor"},{"id":"magic-of-universe","symbol":"mgc","name":"Magic of Universe"},{"id":"magic-power","symbol":"mgp","name":"Magic Power"},{"id":"magic-token","symbol":"magic","name":"MagicLand"},{"id":"magic-yearn-share","symbol":"mys","name":"Magic Yearn Share"},{"id":"magik","symbol":"magik","name":"Magik"},{"id":"magikal-ai","symbol":"mgkl","name":"MAGIKAL.ai"},{"id":"magnetgold","symbol":"mtg","name":"MagnetGold"},{"id":"magpie","symbol":"mgp","name":"Magpie"},{"id":"mahadao","symbol":"maha","name":"MahaDAO"},{"id":"maia","symbol":"maia","name":"Maia"},{"id":"maiar-dex","symbol":"mex","name":"xExchange"},{"id":"maidcoin","symbol":"$maid","name":"MaidCoin"},{"id":"maidsafecoin","symbol":"emaid","name":"MaidSafeCoin"},{"id":"main","symbol":"main","name":"Main"},{"id":"mainframe","symbol":"mft","name":"Hifi Finance [OLD]"},{"id":"mainstream-for-the-underground","symbol":"mftu","name":"Mainstream For The Underground"},{"id":"maison-capital","symbol":"msn","name":"Maison Capital"},{"id":"maker","symbol":"mkr","name":"Maker"},{"id":"makiswap","symbol":"maki","name":"MakiSwap"},{"id":"malgo-finance","symbol":"mgxg","name":"Malgo Finance"},{"id":"malinka","symbol":"mlnk","name":"Malinka"},{"id":"mammoth-mmt","symbol":"mmt","name":"Mammoth MMT"},{"id":"manateecoin","symbol":"mtc","name":"ManateeCoin"},{"id":"manchester-city-fan-token","symbol":"city","name":"Manchester City Fan Token"},{"id":"mancium","symbol":"manc","name":"Mancium"},{"id":"mandala-exchange-token","symbol":"mdx","name":"Mandala Exchange"},{"id":"mandox-2","symbol":"mandox","name":"MandoX"},{"id":"maneki-neko","symbol":"neki","name":"Maneki-neko"},{"id":"mangamon","symbol":"man","name":"MangaMon"},{"id":"mangata-x","symbol":"mgx","name":"Mangata X"},{"id":"manga-token","symbol":"$manga","name":"Manga"},{"id":"mangoman-intelligent","symbol":"mmit","name":"MangoMan Intelligent"},{"id":"mango-markets","symbol":"mngo","name":"Mango"},{"id":"manifold-finance","symbol":"fold","name":"Manifold Finance"},{"id":"manna","symbol":"manna","name":"Manna"},{"id":"mantis-network","symbol":"mntis","name":"Mantis Network"},{"id":"mantra-dao","symbol":"om","name":"MANTRA"},{"id":"manufactory-2","symbol":"mnft","name":"ManuFactory"},{"id":"maorabbit","symbol":"maorabbit","name":"MaoRabbit"},{"id":"mapcoin","symbol":"mapc","name":"MapCoin"},{"id":"maple","symbol":"mpl","name":"Maple"},{"id":"mapmetrics","symbol":"mmaps","name":"MapMetrics"},{"id":"maps","symbol":"maps","name":"MAPS"},{"id":"marble","symbol":"$marble","name":"Marble Dao"},{"id":"marbledao-artex","symbol":"artex","name":"MarbleDAO ARTEX"},{"id":"marble-heroes","symbol":"mbh","name":"Marble Heroes"},{"id":"marbleprix","symbol":"marblex7","name":"MarblePrix"},{"id":"marblex","symbol":"mbx","name":"Marblex"},{"id":"marcopolo","symbol":"map","name":"MAP Protocol"},{"id":"mare-finance","symbol":"mare","name":"Mare Finance"},{"id":"marginswap","symbol":"mfi","name":"Marginswap"},{"id":"marhabadefi","symbol":"mrhb","name":"MarhabaDeFi"},{"id":"maria","symbol":"maria","name":"Maria"},{"id":"maricoin","symbol":"mcoin","name":"MariCoin"},{"id":"marinade","symbol":"mnde","name":"Marinade"},{"id":"market-ledger","symbol":"ml","name":"Market Ledger"},{"id":"market-making-pro","symbol":"mmpro","name":"Market Making Pro"},{"id":"marketmove","symbol":"move","name":"MarketMove"},{"id":"marketpeak","symbol":"peak","name":"PEAKDEFI"},{"id":"marlin","symbol":"pond","name":"Marlin"},{"id":"marmalade-token","symbol":"mard","name":"Marmalade Token"},{"id":"marnotaur","symbol":"taur","name":"Marnotaur"},{"id":"marosca-inu","symbol":"marosca","name":"Marosca Inu"},{"id":"marquee","symbol":"marq","name":"Marquee"},{"id":"mars","symbol":"mars","name":"Mars"},{"id":"mars4","symbol":"mars4","name":"MARS4"},{"id":"marscoin","symbol":"mars","name":"Marscoin"},{"id":"marscolony","symbol":"clny","name":"MarsColony"},{"id":"marsdao","symbol":"mdao","name":"MarsDAO"},{"id":"mars-doge","symbol":"marsdoge","name":"Mars Doge"},{"id":"mars-ecosystem-token","symbol":"xms","name":"Mars Ecosystem"},{"id":"marshall-fighting-champio","symbol":"mfc","name":"Marshall Fighting Championship"},{"id":"marshall-rogan-inu","symbol":"mri","name":"Marshall Inu"},{"id":"mars-inu","symbol":"marsinu","name":"Mars Inu"},{"id":"mars-protocol-a7fcbcfb-fd61-4017-92f0-7ee9f9cc6da3","symbol":"mars","name":"Mars Protocol"},{"id":"marsrise","symbol":"marsrise","name":"MarsRise"},{"id":"marsupilamii","symbol":"mars","name":"Marsupilamii"},{"id":"marsx","symbol":"mx","name":"MarsX"},{"id":"martexcoin","symbol":"mxt","name":"MarteXcoin"},{"id":"martik","symbol":"mtk","name":"Martik"},{"id":"martin-shkreli-inu","symbol":"msi","name":"Martin Shkreli Inu"},{"id":"martkist","symbol":"martk","name":"Martkist"},{"id":"marumarunft","symbol":"maru","name":"marumaruNFT"},{"id":"marutaro","symbol":"maru","name":"MaruTaro"},{"id":"marvellex-classic","symbol":"mlxc","name":"Marvellex Classic"},{"id":"marvelous-nfts","symbol":"mnft","name":"Marvelous NFTs"},{"id":"marvin-inu","symbol":"marvin","name":"Marvin Inu"},{"id":"marx","symbol":"marx","name":"MarX"},{"id":"masari","symbol":"msr","name":"Masari"},{"id":"maskdoge","symbol":"md3","name":"MaskDogeV3"},{"id":"mask-network","symbol":"mask","name":"Mask Network"},{"id":"masq","symbol":"masq","name":"MASQ"},{"id":"mass","symbol":"mass","name":"MASS"},{"id":"massa","symbol":"massa","name":"Massa"},{"id":"massive-protocol","symbol":"mav","name":"Massive Protocol"},{"id":"mass-vehicle-ledger","symbol":"mvl","name":"MVL"},{"id":"mastermind","symbol":"mastermind","name":"Mastermind"},{"id":"masterpiece-maker","symbol":"mama","name":"Masterpiece Maker"},{"id":"masterwin","symbol":"mw","name":"MasterWin"},{"id":"mata","symbol":"mata","name":"Mata"},{"id":"matador-token","symbol":"mtdr","name":"Matador"},{"id":"matchcup","symbol":"match","name":"Matchcup"},{"id":"matchpool","symbol":"gup","name":"Guppy"},{"id":"material","symbol":"mtrl","name":"Material"},{"id":"materium","symbol":"mtrm","name":"Materium"},{"id":"math","symbol":"math","name":"MATH"},{"id":"matic-aave-aave","symbol":"maaave","name":"Matic Aave Interest Bearing AAVE"},{"id":"matic-aave-dai","symbol":"madai","name":"Matic Aave Interest Bearing DAI"},{"id":"matic-aave-link","symbol":"malink","name":"Matic Aave Interest Bearing LINK"},{"id":"matic-aave-usdc","symbol":"mausdc","name":"Matic Aave Interest Bearing USDC"},{"id":"matic-aave-weth","symbol":"maweth","name":"Matic Aave Interest Bearing WETH"},{"id":"matic-dai-stablecoin","symbol":"dai-matic","name":"Matic DAI Stablecoin"},{"id":"matic-launchpad","symbol":"maticpad","name":"Matic Launchpad"},{"id":"matic-network","symbol":"matic","name":"Polygon"},{"id":"matic-plenty-bridge","symbol":"matic.e","name":"MATIC (Plenty Bridge)"},{"id":"maticverse","symbol":"mverse","name":"MaticVerse"},{"id":"matic-wormhole","symbol":"maticpo","name":"MATIC (Wormhole)"},{"id":"matrak-fan-token","symbol":"mtrk","name":"Matrak Fan Token"},{"id":"matrix-533ba916-8d8a-4979-b5d5-34483cdee5b1","symbol":"matrix","name":"Matrix"},{"id":"matrix-ai-network","symbol":"man","name":"Matrix AI Network"},{"id":"matrixetf","symbol":"mdf","name":"MatrixETF"},{"id":"matrixgpt","symbol":"mai","name":"MatrixGPT"},{"id":"matrix-protocol","symbol":"mtx","name":"Matrix Protocol"},{"id":"matrixswap","symbol":"matrix","name":"Matrix Labs"},{"id":"matryx","symbol":"mtx","name":"MATRYX"},{"id":"matsuswap","symbol":"matsuswap","name":"MatsuSwap"},{"id":"mau","symbol":"mau","name":"MAU"},{"id":"mavaverse-token","symbol":"mvx","name":"Mavaverse"},{"id":"maxcoin","symbol":"max","name":"Maxcoin"},{"id":"maximus","symbol":"maxi","name":"Maximus"},{"id":"maximus-base","symbol":"base","name":"Maximus BASE"},{"id":"maximus-coin","symbol":"mxz","name":"Maximus Coin"},{"id":"maximus-dao","symbol":"maxi","name":"Maximus DAO"},{"id":"maximus-deci","symbol":"deci","name":"Maximus DECI"},{"id":"maximus-lucky","symbol":"lucky","name":"Maximus LUCKY"},{"id":"maximus-team","symbol":"team","name":"Maximus TEAM"},{"id":"maximus-trio","symbol":"trio","name":"Maximus TRIO"},{"id":"max-property-group","symbol":"mcf","name":"Max Crowdfund"},{"id":"max-revive","symbol":"maxr","name":"Max Revive"},{"id":"max-token","symbol":"max","name":"MAX"},{"id":"maxx-finance","symbol":"maxx","name":"MAXX Finance"},{"id":"maya-preferred-223","symbol":"mayp","name":"Maya Preferred"},{"id":"mayc-vault-nftx","symbol":"mayc","name":"MAYC Vault (NFTX)"},{"id":"maza","symbol":"mzc","name":"Maza"},{"id":"mbd-financials","symbol":"mbd","name":"MBD Financials"},{"id":"mbitbooks","symbol":"mbit","name":"MBitBooks"},{"id":"mcdex","symbol":"mcb","name":"MUX Protocol"},{"id":"mcdoge","symbol":"mcdoge","name":"McDoge"},{"id":"mcelo","symbol":"mcelo","name":"mCELO"},{"id":"mceur","symbol":"mceur","name":"mcEUR"},{"id":"mcfinance","symbol":"mcf","name":"MCFinance"},{"id":"mch-coin","symbol":"mchc","name":"MCH Coin"},{"id":"mci-coin","symbol":"cyclub","name":"Cyclub"},{"id":"mclaren-f1-fan-token","symbol":"mcl","name":"McLaren F1 Fan Token"},{"id":"mcobit","symbol":"mct","name":"Mcobit"},{"id":"mcoin1","symbol":"mcoin","name":"mCoin"},{"id":"mcontent","symbol":"mcontent","name":"MContent"},{"id":"mcpepe-s","symbol":"pepes","name":"McPepe's"},{"id":"mcverse","symbol":"mcv","name":"MCVERSE"},{"id":"mdcx","symbol":"mdcx","name":"MDCx"},{"id":"mdex","symbol":"mdx","name":"Mdex"},{"id":"mdsquare","symbol":"tmed","name":"MDsquare"},{"id":"meanfi","symbol":"mean","name":"Mean DAO"},{"id":"measurable-data-token","symbol":"mdt","name":"Measurable Data"},{"id":"meblox-protocol","symbol":"meb","name":"Meblox Protocol"},{"id":"mechachain","symbol":"$mecha","name":"Mechanium"},{"id":"mecha-morphing","symbol":"mape","name":"Mecha Morphing"},{"id":"mechashiba","symbol":"mec","name":"MechaShiba"},{"id":"mechaverse","symbol":"mc","name":"Mechaverse"},{"id":"mech-master","symbol":"mech","name":"Mech Master"},{"id":"meconcash","symbol":"mch","name":"Meconcash"},{"id":"medal-of-honour","symbol":"moh","name":"Medal of Honour"},{"id":"medamon","symbol":"mon","name":"Medamon"},{"id":"medano","symbol":"mdo","name":"Medano"},{"id":"medcarecoin","symbol":"mdcy","name":"MedCareCoin"},{"id":"media-eye","symbol":"eye","name":"MEDIA EYE"},{"id":"media-licensing-token","symbol":"mlt","name":"Media Licensing Token"},{"id":"media-network","symbol":"media","name":"Media Network"},{"id":"medibloc","symbol":"med","name":"Medibloc"},{"id":"medicalchain","symbol":"mtn","name":"Medicalchain"},{"id":"medical-token-currency","symbol":"mtc","name":"Doc.com"},{"id":"medicalveda","symbol":"mveda","name":"MedicalVeda"},{"id":"medic-coin","symbol":"medic","name":"Medic Coin"},{"id":"medieval-empires","symbol":"mee","name":"Medieval Empires"},{"id":"medifakt","symbol":"fakt","name":"Medifakt"},{"id":"medishares","symbol":"mds","name":"MediShares"},{"id":"medium","symbol":"mdm","name":"MEDIUM"},{"id":"medping","symbol":"mpg","name":"Medping"},{"id":"meebitsdao-pool","symbol":"mbbt","name":"MeebitsDAO Pool"},{"id":"meeb-master","symbol":"meeb","name":"Meeb Master"},{"id":"meeb-vault-nftx","symbol":"meeb","name":"MEEB Vault (NFTX)"},{"id":"meeds-dao","symbol":"meed","name":"Meeds DAO"},{"id":"meeiro","symbol":"mee","name":"Meeiro"},{"id":"meerkat-shares","symbol":"mshare","name":"Meerkat Shares"},{"id":"meetin-token","symbol":"meti","name":"Meetin Token"},{"id":"meetple","symbol":"mpt","name":"Meetple"},{"id":"meflex","symbol":"mef","name":"MEFLEX"},{"id":"mega-protocol","symbol":"mega","name":"Mega Protocol"},{"id":"megashibazilla","symbol":"msz","name":"MegaShibaZilla"},{"id":"megatech","symbol":"mgt","name":"Megatech"},{"id":"megatoken","symbol":"mega","name":"MegaToken"},{"id":"megaton-finance","symbol":"mega","name":"Megaton Finance"},{"id":"megaweapon","symbol":"$weapon","name":"Megaweapon"},{"id":"megaworld","symbol":"mega","name":"MegaWorld"},{"id":"meh","symbol":"meh","name":"meh"},{"id":"mei-flex","symbol":"mf","name":"Mei Flex"},{"id":"me-in","symbol":"mein","name":"Me-in"},{"id":"meishu","symbol":"meishu","name":"meishu"},{"id":"mekkacoin","symbol":"mek","name":"MekkaCoin"},{"id":"melalie","symbol":"mel","name":"MELX"},{"id":"meld","symbol":"meld","name":"MELD"},{"id":"meld-gold","symbol":"mcau","name":"Meld Gold"},{"id":"melecoin","symbol":"mlc","name":"Melecoin"},{"id":"melega","symbol":"marco","name":"Melega"},{"id":"meli-games","symbol":"meli","name":"Meli Games"},{"id":"melody","symbol":"melody","name":"Melody"},{"id":"melody-sgs","symbol":"sgs","name":"Melody SGS"},{"id":"melody-sns","symbol":"sns","name":"Melody SNS"},{"id":"melon","symbol":"mln","name":"Enzyme"},{"id":"melonx","symbol":"$mlnx","name":"MELONx"},{"id":"melos-studio","symbol":"melos","name":"Melos Studio"},{"id":"melo-token","symbol":"melo","name":"Melo"},{"id":"membrana-platform","symbol":"mbn","name":"Membrana"},{"id":"meme-ai","symbol":"memeai","name":"Meme AI"},{"id":"memebank","symbol":"mbk","name":"MeMeBank"},{"id":"memecard","symbol":"mmc","name":"MemeCard"},{"id":"memecoin","symbol":"mem","name":"Memecoin"},{"id":"memedao-ai","symbol":"mdai","name":"MemeDao.Ai"},{"id":"meme-dollar","symbol":"pina","name":"Meme Dollar"},{"id":"meme-elon-doge-floki","symbol":"memelon","name":"Meme Elon Doge Floki"},{"id":"memeflate","symbol":"mflate","name":"Memeflate"},{"id":"meme-inu","symbol":"meme","name":"Meme Inu"},{"id":"meme-kong","symbol":"mkong","name":"Meme Kong"},{"id":"meme-lordz","symbol":"$lordz","name":"Meme Lordz"},{"id":"meme-network","symbol":"meme","name":"Meme Network"},{"id":"memenopoly-money","symbol":"mnop","name":"Memenopoly Money"},{"id":"memepad","symbol":"mepad","name":"MemePad"},{"id":"meme-protocol","symbol":"meme","name":"Meme Protocol"},{"id":"meme-shib","symbol":"ms","name":"Meme Shib"},{"id":"meme-tao","symbol":"mtao","name":"MEME TAO"},{"id":"memetic","symbol":"meme","name":"Memetic"},{"id":"memeverse","symbol":"meme","name":"Memeverse"},{"id":"memewars","symbol":"mwar","name":"MemeWars"},{"id":"menapay","symbol":"mpay","name":"Menapay"},{"id":"mend","symbol":"mend","name":"Mend"},{"id":"menzy","symbol":"mnz","name":"Menzy"},{"id":"meowcoin","symbol":"mewc","name":"MeowCoin"},{"id":"merchant-token","symbol":"mto","name":"Merchant"},{"id":"merchdao","symbol":"mrch","name":"MerchDAO"},{"id":"mercor-finance","symbol":"mrcr","name":"Mercor Finance"},{"id":"mercurial","symbol":"mer","name":"Mercurial"},{"id":"mercury","symbol":"mer","name":"Mercury"},{"id":"merebel","symbol":"meri","name":"Merebel"},{"id":"merge","symbol":"merge","name":"Merge"},{"id":"merit-circle","symbol":"mc","name":"Merit Circle"},{"id":"merkle-network","symbol":"merkle","name":"Merkle Network"},{"id":"merrychristmas","symbol":"hohoho","name":"MerryChristmas [OLD]"},{"id":"merrychristmas-2","symbol":"hohoho","name":"MerryChristmas"},{"id":"merry-christmas-token","symbol":"mct","name":"Merry Christmas Token"},{"id":"meshswap-protocol","symbol":"mesh","name":"Meshswap Protocol"},{"id":"meso","symbol":"meso","name":"Meso"},{"id":"messier","symbol":"m87","name":"MESSIER"},{"id":"meta","symbol":"mta","name":"mStable Governance: Meta"},{"id":"meta-apes-peel","symbol":"peel","name":"Meta Apes PEEL"},{"id":"metababy","symbol":"baby","name":"Metababy"},{"id":"metabeat","symbol":"$beat","name":"MetaBeat"},{"id":"metabet","symbol":"mbet","name":"MetaBET"},{"id":"metablackout","symbol":"mbt","name":"MetaBlackout"},{"id":"metabolic","symbol":"mtbc","name":"Metabolic"},{"id":"metabomb","symbol":"mtb","name":"MetaBomb"},{"id":"metabrands","symbol":"mage","name":"MetaBrands"},{"id":"meta-bsc","symbol":"meta","name":"Meta BSC"},{"id":"metabusdcoin","symbol":"mbc","name":"MetaBUSDCoin"},{"id":"metacash","symbol":"meta","name":"MetaCash"},{"id":"metacelo","symbol":"cmeta","name":"MetaCelo"},{"id":"metacity","symbol":"mtc","name":"MetaCity"},{"id":"metacoin","symbol":"mtc","name":"Metacoin"},{"id":"metacontinental","symbol":"con","name":"MetaContinental"},{"id":"meta-course","symbol":"course","name":"Meta Course"},{"id":"metacraft","symbol":"mct","name":"Metacraft"},{"id":"meta-dance","symbol":"mdt","name":"META DANCE"},{"id":"metaderby","symbol":"dby","name":"Metaderby"},{"id":"metaderby-hoof","symbol":"hoof","name":"Metaderby Hoof"},{"id":"metadium","symbol":"meta","name":"Metadium"},{"id":"metadoctor","symbol":"medoc","name":"MetaDoctor"},{"id":"meta-doge","symbol":"metadoge","name":"Meta Doge"},{"id":"metadoge-bsc","symbol":"metadoge","name":"MetaDoge BSC"},{"id":"metadoge-v2","symbol":"metadogev2","name":"MetaDoge V2"},{"id":"metadubai","symbol":"mdb","name":"MetaDubai"},{"id":"metafabric","symbol":"fabric","name":"MetaFabric"},{"id":"metafastest","symbol":"metaf","name":"METAFASTEST"},{"id":"metafighter","symbol":"mf","name":"MetaFighter"},{"id":"metafinance","symbol":"mfi","name":"MetaFinance"},{"id":"meta_finance","symbol":"mf1","name":"Meta Finance"},{"id":"metafish","symbol":"fish","name":"Metafish"},{"id":"metafishing","symbol":"dgc","name":"MetaFishing [OLD]"},{"id":"metafishing-2","symbol":"dgc","name":"MetaFishing"},{"id":"metaflip","symbol":"metaflip","name":"MetaFlip"},{"id":"metafluence","symbol":"meto","name":"Metafluence"},{"id":"metafootball","symbol":"mtf","name":"MetaFootball"},{"id":"metagalaxy-land","symbol":"megaland","name":"Metagalaxy Land"},{"id":"metagame","symbol":"seed","name":"MetaGame"},{"id":"metagame-arena","symbol":"mga","name":"Metagame Arena"},{"id":"metagamehub-dao","symbol":"mgh","name":"MetaGameHub DAO"},{"id":"metagaming-guild","symbol":"mgg","name":"MetaGaming Guild"},{"id":"metagamz","symbol":"metag","name":"MetagamZ"},{"id":"metagear","symbol":"gear","name":"MetaGear"},{"id":"metagochi","symbol":"mgchi","name":"Metagochi"},{"id":"metagods","symbol":"mgod","name":"MetaGods"},{"id":"metahamster","symbol":"mham","name":"Metahamster"},{"id":"metahash","symbol":"mhc","name":"#MetaHash"},{"id":"metahero","symbol":"hero","name":"Metahero"},{"id":"metajuice","symbol":"vcoin","name":"Metajuice"},{"id":"metakings","symbol":"mtk","name":"Metakings"},{"id":"metal","symbol":"mtl","name":"Metal DAO"},{"id":"metaland-gameverse","symbol":"mst","name":"Monster"},{"id":"meta-launcher","symbol":"mtla","name":"Meta Launcher"},{"id":"metal-blockchain","symbol":"metal","name":"Metal Blockchain"},{"id":"metal-friends","symbol":"mtls","name":"Metal Friends"},{"id":"metalswap","symbol":"xmt","name":"MetalSwap"},{"id":"metamall","symbol":"mall","name":"MetaMall"},{"id":"metamars","symbol":"metam","name":"MetaMars"},{"id":"meta-masters-guild","symbol":"memag","name":"Meta Masters Guild"},{"id":"metamate","symbol":"mtm","name":"MetaMate"},{"id":"metamerce","symbol":"merce","name":"MetaMerce"},{"id":"metamic-e-sports-games","symbol":"meg","name":"MetaMic E-Sports Games"},{"id":"metamonkeyai","symbol":"mmai","name":"MetamonkeyAi"},{"id":"metamoon","symbol":"metamoon","name":"MetaMoon"},{"id":"metamui","symbol":"mmui","name":"MetaMUI"},{"id":"meta-musk","symbol":"meta","name":"Meta Musk"},{"id":"meta-mvrs","symbol":"mvrs","name":"Meta MVRS"},{"id":"meta-nebulas-ionz","symbol":"ionz","name":"IONZ"},{"id":"metanept","symbol":"nept","name":"Metanept"},{"id":"metan-evolutions","symbol":"metan","name":"Metan Evolutions"},{"id":"metaniagames","symbol":"metania","name":"MetaniaGames"},{"id":"metano","symbol":"metano","name":"Metano"},{"id":"metanyx","symbol":"metx","name":"Metanyx"},{"id":"metaoctagon","symbol":"motg","name":"MetaOctagon"},{"id":"metapets","symbol":"metapets","name":"MetaPets"},{"id":"metapioneers","symbol":"mpi","name":"MetaPioneers"},{"id":"metaplace","symbol":"mpc","name":"Metaplace"},{"id":"metaplanet-ai","symbol":"mplai","name":"MetaPlanet AI"},{"id":"metaplayers-gg","symbol":"fps","name":"MetaPlayers.gg"},{"id":"metaplex","symbol":"mplx","name":"Metaplex"},{"id":"meta-pool","symbol":"meta","name":"Meta Pool"},{"id":"metapuss","symbol":"mtp","name":"MetaPuss"},{"id":"metaq","symbol":"metaq","name":"MetaQ"},{"id":"metaracers","symbol":"mrs","name":"MetaRacers"},{"id":"metarare","symbol":"mtra","name":"MetaRare"},{"id":"metareset","symbol":"reset","name":"MetaReset"},{"id":"metarim","symbol":"rim","name":"MetaRim"},{"id":"metarix","symbol":"mtrx","name":"Metarix"},{"id":"metars-genesis","symbol":"mrs","name":"Metars Genesis"},{"id":"meta-ruffy","symbol":"mr","name":"MetaRuffy (MR)"},{"id":"meta-ruffy-old","symbol":"mr","name":"Meta Ruffy [OLD]"},{"id":"metarun","symbol":"mrun","name":"Metarun"},{"id":"metasafemoon","symbol":"metasfm","name":"MetaSafeMoon"},{"id":"meta-shark","symbol":"mts","name":"Meta Shark"},{"id":"meta-shiba","symbol":"mshiba","name":"Meta Shiba"},{"id":"metashooter","symbol":"mhunt","name":"MetaShooter"},{"id":"metasoccer","symbol":"msu","name":"MetaSoccer"},{"id":"metaspace","symbol":"mspace","name":"Metaspace"},{"id":"meta-space-2045","symbol":"mtw","name":"Meta Space 2045"},{"id":"meta-spatial","symbol":"spat","name":"Meta Spatial"},{"id":"metaspets","symbol":"msp","name":"MetaSpets"},{"id":"meta-sports","symbol":"msg","name":"Meta Sports"},{"id":"metasportstoken","symbol":"mst","name":"MetaSportsToken"},{"id":"metastocks","symbol":"mtsks","name":"MetaStocks"},{"id":"metastrike","symbol":"mts","name":"Metastrike"},{"id":"metaswap","symbol":"msc","name":"MetaSwap"},{"id":"metathings","symbol":"mett","name":"Metathings"},{"id":"meta-to-earn","symbol":"mte","name":"Meta to Earn"},{"id":"metatoken","symbol":"mtk","name":"MetaToken"},{"id":"metatrone","symbol":"met","name":"Metatrone"},{"id":"metaus","symbol":"mtu","name":"Metaus"},{"id":"meta-utopia","symbol":"land","name":"Meta Utopia"},{"id":"metavault-dao","symbol":"mvd","name":"Metavault DAO"},{"id":"metavault-trade","symbol":"mvx","name":"Metavault Trade"},{"id":"metaverse-dao","symbol":"mdao","name":"Metaverse DAO"},{"id":"metaverse-dog","symbol":"mvdg","name":"MetaVerse Dog"},{"id":"metaverse-dualchain-network-architecture","symbol":"dna","name":"Metaverse DNA"},{"id":"metaverse-etp","symbol":"etp","name":"Metaverse ETP"},{"id":"metaverse-exchange","symbol":"metacex","name":"Metaverse Exchange"},{"id":"metaverse-face","symbol":"mefa","name":"Metaverse Face"},{"id":"metaverse-hub","symbol":"mhub","name":"Metaverse Hub"},{"id":"metaverse-index","symbol":"mvi","name":"Metaverse Index"},{"id":"metaverse-index-token","symbol":"metai","name":"Metaverse Index Token"},{"id":"metaverse-m","symbol":"m","name":"MetaVerse-M"},{"id":"metaverse-miner","symbol":"meta","name":"Metaverse Miner"},{"id":"metaverse-network-pioneer","symbol":"neer","name":"Metaverse.Network Pioneer"},{"id":"metaverse-nft-index","symbol":"play","name":"Metaverse NFT Index"},{"id":"metaverser","symbol":"mtvt","name":"Metaverser"},{"id":"metaverse-vr","symbol":"mevr","name":"Metaverse VR"},{"id":"metaversex","symbol":"metax","name":"MetaverseX"},{"id":"metavisa","symbol":"mesa","name":"metavisa"},{"id":"metavpad","symbol":"metav","name":"MetaVPad"},{"id":"metawars","symbol":"wars","name":"MetaWars"},{"id":"metawar-token","symbol":"mtwr","name":"MetaWar Token"},{"id":"metawear","symbol":"wear","name":"MetaWear"},{"id":"metaworld","symbol":"mw","name":"MetaWorld"},{"id":"meta-world-game","symbol":"mtw","name":"Meta World Game"},{"id":"metax","symbol":"x1","name":"MetaX"},{"id":"metaxy","symbol":"mxy","name":"Metaxy"},{"id":"metazilla","symbol":"mz","name":"MetaZilla"},{"id":"meter","symbol":"mtrg","name":"Meter Governance"},{"id":"meter-governance-mapped-by-meter-io","symbol":"emtrg","name":"Meter Governance mapped by Meter.io"},{"id":"meter-stable","symbol":"mtr","name":"Meter Stable"},{"id":"metfi","symbol":"mfi","name":"MetFi [OLD]"},{"id":"metfi-2","symbol":"metfi","name":"MetFi"},{"id":"metfx-watch-to-earn","symbol":"mfx","name":"METFX Watch To Earn"},{"id":"method-fi","symbol":"mthd","name":"Method Finance"},{"id":"metis","symbol":"mts","name":"Metis MTS"},{"id":"metis-token","symbol":"metis","name":"Metis"},{"id":"metoshi","symbol":"meto","name":"Metoshi"},{"id":"metria","symbol":"metr","name":"Metria Network"},{"id":"metronome","symbol":"met","name":"Metronome"},{"id":"mettalex","symbol":"mtlx","name":"Mettalex"},{"id":"metti-inu","symbol":"metti","name":"Metti Inu"},{"id":"meverse","symbol":"mev","name":"MEVerse"},{"id":"mevfree","symbol":"mevfree","name":"MEVFree"},{"id":"mexican-peso-tether","symbol":"mxnt","name":"Mexican Peso Tether"},{"id":"mezz","symbol":"mezz","name":"MEZZ"},{"id":"mfet","symbol":"mfet","name":"MFET"},{"id":"miamicoin","symbol":"mia","name":"MiamiCoin"},{"id":"miaswap","symbol":"mia","name":"MiaSwap"},{"id":"mib-coin","symbol":"mib","name":"MIB Coin"},{"id":"mibr-fan-token","symbol":"mibr","name":"MIBR Fan Token"},{"id":"microbitcoin","symbol":"mbc","name":"MicroBitcoin"},{"id":"micro-bitcoin-finance","symbol":"mbtc","name":"Micro Bitcoin Finance"},{"id":"microchains-gov-token","symbol":"mcg","name":"MicroChains Gov Token"},{"id":"microcosm","symbol":"mic","name":"Microcosm"},{"id":"microcredittoken","symbol":"1mct","name":"MicroCredit"},{"id":"micromoney","symbol":"amm","name":"MicroMoney"},{"id":"micropets","symbol":"pets","name":"MicroPets"},{"id":"microsoft-tokenized-stock-defichain","symbol":"dmsft","name":"Microsoft Tokenized Stock Defichain"},{"id":"microtick","symbol":"tick","name":"Microtick"},{"id":"microtuber","symbol":"mct","name":"MicroTuber"},{"id":"microvisionchain","symbol":"space","name":"MicrovisionChain"},{"id":"mida","symbol":"mida","name":"MIDA"},{"id":"midas","symbol":"midas","name":"Midas"},{"id":"midas-miner","symbol":"mmi","name":"Midas Miner"},{"id":"mie-network","symbol":"mie","name":"MIE Network"},{"id":"miidas","symbol":"miidas","name":"Miidas"},{"id":"mikawa-inu","symbol":"mikawa","name":"Mikawa Inu"},{"id":"milady-vault-nftx","symbol":"milady","name":"Milady Vault (NFTX)"},{"id":"mileverse","symbol":"mvc","name":"MileVerse"},{"id":"military-finance","symbol":"mil","name":"Military Finance"},{"id":"militia-games","symbol":"milit","name":"Militia Games"},{"id":"milk","symbol":"milk","name":"Cool Cats Milk"},{"id":"milkai","symbol":"milkai","name":"MilkAI"},{"id":"milk-alliance","symbol":"mlk","name":"MiL.k Alliance"},{"id":"milk-and-butter","symbol":"mb","name":"Milk and Butter"},{"id":"milkshakeswap","symbol":"milk","name":"Milkshake Swap"},{"id":"milkyswap","symbol":"milky","name":"MilkySwap"},{"id":"milky-token","symbol":"milky","name":"Milky"},{"id":"millenniumclub","symbol":"mclb","name":"MillenniumClub Coin [OLD]"},{"id":"millenniumclub-coin-new","symbol":"mclb","name":"MillenniumClub Coin [NEW]"},{"id":"millennium-sapphire","symbol":"msto","name":"Millennium Sapphire"},{"id":"millimeter","symbol":"mm","name":"Millimeter"},{"id":"million","symbol":"mm","name":"Million"},{"id":"milliondollarbaby","symbol":"mdb","name":"Make DeFi Better"},{"id":"million-monke","symbol":"mimo","name":"Million Monke"},{"id":"millonarios-fc-fan-token","symbol":"mfc","name":"Millonarios FC Fan Token"},{"id":"milo-inu","symbol":"milo","name":"Milo Inu"},{"id":"mim","symbol":"swarm","name":"MIM"},{"id":"mimas-finance","symbol":"mimas","name":"Mimas Finance"},{"id":"mimatic","symbol":"mimatic","name":"MAI"},{"id":"mimblewimblecoin","symbol":"mwc","name":"MimbleWimbleCoin"},{"id":"mimir-token","symbol":"mimir","name":"Mimir"},{"id":"mimo-parallel-governance-token","symbol":"mimo","name":"Mimo Governance"},{"id":"mimosa","symbol":"mimo","name":"Mimosa"},{"id":"mina-protocol","symbol":"mina","name":"Mina Protocol"},{"id":"minato","symbol":"mnto","name":"Minato"},{"id":"mindfolk-wood","symbol":"$wood","name":"Mindfolk Wood"},{"id":"mind-games-cortex","symbol":"crx","name":"MIND Games CORTEX"},{"id":"mindol","symbol":"min","name":"MINDOL"},{"id":"minds","symbol":"minds","name":"Minds"},{"id":"mindsync","symbol":"mai","name":"Mindsync"},{"id":"mineable","symbol":"mnb","name":"Mineable"},{"id":"minebase","symbol":"mbase","name":"Minebase"},{"id":"mine-network","symbol":"mnet","name":"MINE Network"},{"id":"mineral","symbol":"mnr","name":"Mineral"},{"id":"minerblox","symbol":"mblox","name":"MinerBlox"},{"id":"minergate-token","symbol":"mg","name":"MinerGate"},{"id":"minerjoe","symbol":"gold","name":"MinerJoe"},{"id":"miners-of-kadenia","symbol":"mok","name":"Miners of Kadenia"},{"id":"minerva-wallet","symbol":"miva","name":"Minerva Wallet"},{"id":"mines-of-dalarnia","symbol":"dar","name":"Mines of Dalarnia"},{"id":"mini","symbol":"mini","name":"Mini"},{"id":"mini-baby-doge","symbol":"minibabydoge","name":"Mini Baby Doge"},{"id":"minibtc","symbol":"minibtc","name":"MiniBTC"},{"id":"minidoge","symbol":"minidoge","name":"MiniDOGE"},{"id":"minifootball","symbol":"minifootball","name":"Minifootball"},{"id":"miningnft","symbol":"mit","name":"MiningNFT"},{"id":"miniverse-dollar","symbol":"mvdollar","name":"MiniVerse Dollar"},{"id":"minix","symbol":"mnx","name":"MiniX"},{"id":"minswap","symbol":"min","name":"Minswap"},{"id":"mint-club","symbol":"mint","name":"Mint Club"},{"id":"mintcoin","symbol":"mint","name":"Mintcoin"},{"id":"minted","symbol":"mtd","name":"Minted"},{"id":"mintera","symbol":"mnte","name":"Mintera"},{"id":"minterest","symbol":"mnt","name":"Minterest"},{"id":"minter-hub","symbol":"hub","name":"Minter Hub"},{"id":"minter-network","symbol":"bip","name":"Minter Network"},{"id":"mintlayer","symbol":"ml","name":"Mintlayer"},{"id":"mint-marble","symbol":"mim","name":"Mint Marble"},{"id":"minto","symbol":"btcmt","name":"Minto"},{"id":"mintpad","symbol":"mint","name":"Mintpad"},{"id":"mintyswap","symbol":"mintys","name":"MintySwap"},{"id":"minu","symbol":"minu","name":"Minu"},{"id":"mirai-token","symbol":"mirai","name":"Mirai Labs"},{"id":"miraqle","symbol":"mql","name":"MiraQle"},{"id":"mirarc-chain","symbol":"mat","name":"MirArc Chain"},{"id":"mirocana","symbol":"miro","name":"Mirocana"},{"id":"mirrored-ether","symbol":"meth","name":"Mirrored Ether"},{"id":"mirror-finance","symbol":"mirror","name":"Mirror Finance"},{"id":"mirror-protocol","symbol":"mir","name":"Mirror Protocol"},{"id":"misbloc","symbol":"msb","name":"Misbloc"},{"id":"mission-helios","symbol":"helios","name":"Mission Helios"},{"id":"mist","symbol":"mist","name":"Mist"},{"id":"mithril","symbol":"mith","name":"Mithril"},{"id":"mithril-share","symbol":"mis","name":"Mithril Share"},{"id":"mithrilverse","symbol":"mithril","name":"Mithrilverse"},{"id":"mixin","symbol":"xin","name":"Mixin"},{"id":"mixmarvel","symbol":"mix","name":"MixMarvel"},{"id":"mixsome","symbol":"some","name":"Mixsome"},{"id":"mixtrust","symbol":"mxt","name":"MixTrust"},{"id":"miyazaki-inu","symbol":"miyazaki","name":"Miyazaki Inu"},{"id":"mizar","symbol":"mzr","name":"Mizar"},{"id":"mktcash","symbol":"mch","name":"Mktcash"},{"id":"mktcoin","symbol":"mkt","name":"MktCoin"},{"id":"mloky","symbol":"mloky","name":"MLOKY"},{"id":"mm72","symbol":"mm72","name":"MM72"},{"id":"mmacoin","symbol":"mma","name":"MMACoin"},{"id":"mma-gaming","symbol":"mma","name":"MMA Gaming"},{"id":"mmfinance","symbol":"mmf","name":"MMFinance (Cronos)"},{"id":"mmfinance-polygon","symbol":"mmf","name":"MMFinance (Polygon)"},{"id":"mmf-money","symbol":"burrow","name":"MMF Money"},{"id":"mmg-token","symbol":"mmg","name":"Mad Monkey Guild"},{"id":"mmocoin","symbol":"mmo","name":"MMOCoin"},{"id":"mms-cash","symbol":"mcash","name":"MMS Cash"},{"id":"mms-coin","symbol":"mmsc","name":"MMS Coin"},{"id":"mnmcoin","symbol":"mnmc","name":"MNMCoin"},{"id":"mo","symbol":"mo","name":"MO"},{"id":"moar","symbol":"moar","name":"Moar Finance"},{"id":"mobiecoin","symbol":"mbx","name":"MobieCoin"},{"id":"mobifi","symbol":"mofi","name":"MobiFi"},{"id":"mobilecoin","symbol":"mob","name":"MobileCoin"},{"id":"mobile-crypto-pay-coin","symbol":"mcpc","name":"Mobile Crypto Pay Coin"},{"id":"mobilego","symbol":"mgo","name":"MobileGo"},{"id":"mobility-coin","symbol":"mobic","name":"Mobility Coin"},{"id":"mobipad","symbol":"mbp","name":"Mobipad"},{"id":"mobist","symbol":"mitx","name":"Mobist"},{"id":"mobius","symbol":"mobi","name":"Mobius"},{"id":"mobius-finance","symbol":"mot","name":"Mobius Finance"},{"id":"mobius-money","symbol":"mobi","name":"Mobius Money"},{"id":"mobix","symbol":"mobx","name":"MOBIX"},{"id":"mobox","symbol":"mbox","name":"Mobox"},{"id":"moby-dick-2","symbol":"moby","name":"Moby Dick"},{"id":"mochi","symbol":"mochi","name":"Mochi"},{"id":"mochi-inu","symbol":"mochi","name":"Mochi Inu"},{"id":"mochi-market","symbol":"moma","name":"Mochi Market"},{"id":"mocossi-planet","symbol":"mcos","name":"Mocossi Planet"},{"id":"moda-dao","symbol":"moda","name":"MODA DAO"},{"id":"modden","symbol":"mddn","name":"Modden"},{"id":"modefi","symbol":"mod","name":"Modefi"},{"id":"modex","symbol":"modex","name":"Modex"},{"id":"modular-wallet","symbol":"mod","name":"Modular Wallet"},{"id":"moeda-loyalty-points","symbol":"mda","name":"Moeda Loyalty Points"},{"id":"moeta","symbol":"moeta","name":"Moeta"},{"id":"mogul-productions","symbol":"stars","name":"Mogul Productions"},{"id":"mojito","symbol":"mojo","name":"Mojito"},{"id":"mojitoswap","symbol":"mjt","name":"MojitoSwap"},{"id":"molecular-future","symbol":"mof","name":"Molecular Future"},{"id":"mollector","symbol":"mol","name":"Mollector"},{"id":"moments","symbol":"mmt","name":"Moments Market"},{"id":"momentum-2","symbol":"mass","name":"Momentum"},{"id":"mommy-doge","symbol":"mommydoge","name":"Mommy Doge"},{"id":"momo-key","symbol":"key","name":"MoMo Key"},{"id":"mona","symbol":"mona","name":"Monaco Planet"},{"id":"monaco","symbol":"mco","name":"MCO"},{"id":"monacoin","symbol":"mona","name":"MonaCoin"},{"id":"monavale","symbol":"mona","name":"Monavale"},{"id":"mondayclub","symbol":"monday","name":"MondayClub"},{"id":"mondo-community-coin","symbol":"mndcc","name":"Mondo Community Coin"},{"id":"monerium-eur-money","symbol":"eure","name":"Monerium EUR emoney"},{"id":"monero","symbol":"xmr","name":"Monero"},{"id":"monero-classic-xmc","symbol":"xmc","name":"Monero-Classic"},{"id":"monerov","symbol":"xmv","name":"MoneroV"},{"id":"mones","symbol":"mones","name":"Mones"},{"id":"moneta","symbol":"moneta","name":"Moneta"},{"id":"monetas","symbol":"mntg","name":"Monetas [OLD]"},{"id":"monetas-2","symbol":"mntg","name":"Monetas"},{"id":"monetha","symbol":"mth","name":"Monetha"},{"id":"monet-society","symbol":"monet","name":"Monet Society"},{"id":"moneybrain-bips","symbol":"bips","name":"Moneybrain BiPS"},{"id":"moneybyte","symbol":"mon","name":"Moneybyte"},{"id":"moneyhero","symbol":"myh","name":"Moneyhero"},{"id":"moneyswap","symbol":"mswap","name":"MoneySwap"},{"id":"moneytree","symbol":"money","name":"MoneyTree"},{"id":"monfter","symbol":"mon","name":"Monfter"},{"id":"mongol-nft","symbol":"mnft","name":"Mongol NFT"},{"id":"mongoose","symbol":"mongoose","name":"Mongoose"},{"id":"mongoosecoin","symbol":"mongoose","name":"MongooseCoin"},{"id":"moniwar","symbol":"mowa","name":"Moniwar"},{"id":"monk","symbol":"monk","name":"Monk"},{"id":"monkex","symbol":"monkex","name":"Monkex"},{"id":"monkeyball","symbol":"mbs","name":"MonkeyLeague"},{"id":"monkey-king","symbol":"wukong","name":"Monkey King"},{"id":"monnfts","symbol":"mon","name":"MONNFTS"},{"id":"monnos","symbol":"mns","name":"Monnos"},{"id":"monomoney","symbol":"mono","name":"MonoMoney"},{"id":"mononoke-inu","symbol":"mononoke-inu","name":"Mononoke Inu"},{"id":"monopoly-meta","symbol":"mpm","name":"Monopoly Meta"},{"id":"monopoly-millionaire-control","symbol":"mmc","name":"Monopoly Millionaire Control"},{"id":"monopoly-millionaire-game","symbol":"mmg","name":"Monopoly Millionaire Game"},{"id":"monox","symbol":"mono","name":"MonoX"},{"id":"monsoon-finance","symbol":"mcash","name":"Monsoon Finance"},{"id":"monsta-infinite","symbol":"moni","name":"Monsta Infinite"},{"id":"monster-ball","symbol":"mfb","name":"Monster Ball"},{"id":"monster-cash-share","symbol":"mss","name":"Monster Slayer Share"},{"id":"monster-galaxy","symbol":"ggm","name":"Monster Galaxy"},{"id":"monster-of-god","symbol":"monx","name":"Monster of God"},{"id":"monsterquest","symbol":"mqst","name":"MonsterQuest"},{"id":"monsterra","symbol":"mstr","name":"Monsterra"},{"id":"monsterra-mag","symbol":"mag","name":"Monsterra MAG"},{"id":"monsters-clan","symbol":"mons","name":"Monsters Clan"},{"id":"monstock","symbol":"mon","name":"Monstock"},{"id":"moochii","symbol":"moochii","name":"Moochii"},{"id":"mooi-network","symbol":"mooi","name":"MOOI Network"},{"id":"moola-celo-atoken","symbol":"mcelo","name":"Moola CELO AToken"},{"id":"moola-celo-dollars","symbol":"mcusd","name":"Moola Celo Dollars"},{"id":"moola-interest-bearing-creal","symbol":"mcreal","name":"Moola interest bearing CREAL"},{"id":"moola-market","symbol":"moo","name":"Moola Market"},{"id":"moomonster","symbol":"moo","name":"MooMonster"},{"id":"moon","symbol":"moon","name":"r/CryptoCurrency Moons"},{"id":"moonai","symbol":"mooi","name":"Moonaï"},{"id":"moonarch","symbol":"moonarch","name":"Moonarch"},{"id":"moonbeam","symbol":"glmr","name":"Moonbeam"},{"id":"moonbeans","symbol":"beans","name":"MoonBeans"},{"id":"moonbear-finance","symbol":"mbf","name":"MoonBear.Finance"},{"id":"moonbirds-nft-index-by-mexc","symbol":"nmoon","name":"Moonbirds NFT Index by MEXC"},{"id":"mooncake","symbol":"moon","name":"MoonCake"},{"id":"mooncat-vault-nftx","symbol":"mooncat","name":"MOONCAT Vault (NFTX)"},{"id":"mooncoin","symbol":"moon","name":"Mooncoin"},{"id":"moondogs","symbol":"woof","name":"Moondogs"},{"id":"moonedge","symbol":"mooned","name":"MoonEdge"},{"id":"mooner","symbol":"mnr","name":"Mooner"},{"id":"mooney","symbol":"mooney","name":"Moon DAO"},{"id":"moon-eye","symbol":"me","name":"Moon Eye"},{"id":"moonfarm-finance","symbol":"mfo","name":"MoonFarm Finance"},{"id":"moongame","symbol":"mgt","name":"Moongame"},{"id":"mooni","symbol":"mooni","name":"Mooni"},{"id":"moonienft","symbol":"mny","name":"MoonieNFT"},{"id":"moonions","symbol":"moonion","name":"Moonions"},{"id":"moonlana","symbol":"mola","name":"MoonLana"},{"id":"moonlift","symbol":"mltpx","name":"Moonlift Capital"},{"id":"moonlight-metaverse","symbol":"$mlm","name":"Moonlight Metaverse"},{"id":"moonlight-token","symbol":"moonlight","name":"Moonlight"},{"id":"moon-maker-protocol","symbol":"mmp","name":"Moon Maker Protocol"},{"id":"moon-nation-game","symbol":"mng","name":"Moon Nation Game"},{"id":"moonpaw","symbol":"moonpaw","name":"MoonPaw"},{"id":"moonpot","symbol":"pots","name":"Moonpot"},{"id":"moonpot-finance","symbol":"moonpot","name":"MoonPot Finance"},{"id":"moon-rabbit","symbol":"aaa","name":"Moon Rabbit"},{"id":"moonretriever","symbol":"fetch","name":"MoonRetriever"},{"id":"moonrise","symbol":"moonrise","name":"MoonRise"},{"id":"moonriver","symbol":"movr","name":"Moonriver"},{"id":"moonrock-v2","symbol":"rock","name":"MoonRock V2"},{"id":"moonscape","symbol":"mscp","name":"Moonscape"},{"id":"moonsdust","symbol":"moond","name":"MoonsDust"},{"id":"moonshot","symbol":"moonshot","name":"Moonshot [OLD]"},{"id":"moonshots-farm","symbol":"bones","name":"Moonshots Farm"},{"id":"moonstarter","symbol":"mnst","name":"MoonStarter"},{"id":"moonswap","symbol":"moon","name":"MoonSwap"},{"id":"moon-token","symbol":"dodb","name":"DODbase"},{"id":"moon-tropica","symbol":"cah","name":"Moon Tropica"},{"id":"moonwell","symbol":"mfam","name":"Moonwell Apollo"},{"id":"moonwell-artemis","symbol":"well","name":"Moonwell"},{"id":"moonwolf-io","symbol":"wolf","name":"moonwolf.io"},{"id":"mops","symbol":"mops","name":"Mops"},{"id":"moreal","symbol":"mor","name":"Moreal"},{"id":"moremoney-usd","symbol":"money","name":"Moremoney USD"},{"id":"more-token","symbol":"more","name":"Moremoney Finance"},{"id":"mork","symbol":"mork","name":"MORK"},{"id":"morpher","symbol":"mph","name":"Morpher"},{"id":"morpheus-labs","symbol":"mitx","name":"Morpheus Labs"},{"id":"morpheus-network","symbol":"mnw","name":"Morpheus Network"},{"id":"morpheus-token","symbol":"pills","name":"Morpheus Swap"},{"id":"morpho-network","symbol":"morpho","name":"Morpho Network"},{"id":"morphswap","symbol":"ms","name":"Morphswap"},{"id":"mosolid","symbol":"mosolid","name":"moSOLID"},{"id":"mosquitos-finance","symbol":"suckr","name":"Mosquitos Finance"},{"id":"moss-carbon-credit","symbol":"mco2","name":"Moss Carbon Credit"},{"id":"moss-governance","symbol":"moss","name":"Moss Governance"},{"id":"mossland","symbol":"moc","name":"Mossland"},{"id":"motacoin","symbol":"mota","name":"MotaCoin"},{"id":"mother-earth","symbol":"mot","name":"Mother Earth"},{"id":"mother-of-memes","symbol":"mom","name":"Mother of Memes"},{"id":"motion-motn","symbol":"motn","name":"MOTION"},{"id":"motionwreck-games","symbol":"mwg","name":"MotionWreck Games"},{"id":"motiv-protocol","symbol":"mov","name":"MOTIV Protocol"},{"id":"motocoin","symbol":"moto","name":"Motocoin"},{"id":"motogp-fan-token","symbol":"mgpt","name":"MotoGP Fan Token"},{"id":"motoverse","symbol":"mile","name":"Motoverse"},{"id":"mound-token","symbol":"mnd","name":"Mound"},{"id":"mouse-haunt","symbol":"mht","name":"Mouse Haunt"},{"id":"mouseworm","symbol":"mouseworm","name":"MouseWorm"},{"id":"movecash","symbol":"mca","name":"MoveCash"},{"id":"move-dollar","symbol":"mod","name":"Move Dollar"},{"id":"move-network","symbol":"movd","name":"MOVE Network"},{"id":"moverich","symbol":"mvrc","name":"MoveRich"},{"id":"mover-xyz","symbol":"mover","name":"Mover.xyz"},{"id":"movey","symbol":"movey","name":"Movey"},{"id":"movez","symbol":"movez","name":"MoveZ"},{"id":"moviebloc","symbol":"mbl","name":"MovieBloc"},{"id":"movingon-finance","symbol":"movon","name":"MovingOn Finance"},{"id":"movn","symbol":"mov","name":"MOVN"},{"id":"mp3","symbol":"mp3","name":"MP3"},{"id":"mpx","symbol":"mpx","name":"Morphex"},{"id":"mrweb-finance-2","symbol":"ama","name":"MrWeb Finance"},{"id":"msgsender","symbol":"msg","name":"MsgSender"},{"id":"mshare","symbol":"mshare","name":"MShare"},{"id":"msol","symbol":"msol","name":"Marinade staked SOL"},{"id":"mstation","symbol":"mst","name":"MStation"},{"id":"mtg-token","symbol":"mtg","name":"MTG Token"},{"id":"mtop","symbol":"mtop","name":"MTOP"},{"id":"mt-pelerin-shares","symbol":"mps","name":"Mt Pelerin Shares"},{"id":"mttcoin","symbol":"mttcoin","name":"MTTCoin"},{"id":"mu-coin","symbol":"mu","name":"Mu Coin"},{"id":"mudra-exchange","symbol":"mudra","name":"Mudra"},{"id":"mudra-mdr","symbol":"mdr","name":"Mudra MDR"},{"id":"muesliswap-milk","symbol":"milk","name":"MuesliSwap MILK"},{"id":"muesliswap-yield-token","symbol":"myield","name":"MuesliSwap Yield"},{"id":"mugen-finance","symbol":"mgn","name":"Mugen Finance"},{"id":"mu-gold","symbol":"mug","name":"Mu Gold"},{"id":"mu-inu","symbol":"muinu","name":"Mu Inu"},{"id":"multibtc","symbol":"multibtc","name":"MultiBTC"},{"id":"multichain","symbol":"multi","name":"Multichain"},{"id":"multi-chain-capital-2","symbol":"mcc","name":"Multi-Chain Capital"},{"id":"multipad","symbol":"mpad","name":"MultiPad"},{"id":"multiplanetary-inus","symbol":"inus","name":"MultiPlanetary Inus"},{"id":"multisys","symbol":"myus","name":"Multisys"},{"id":"multivac","symbol":"mtv","name":"MultiVAC"},{"id":"multiverse","symbol":"ai","name":"Multiverse"},{"id":"multiverse-capital","symbol":"mvc","name":"Multiverse Capital"},{"id":"mu-meme","symbol":"mume","name":"Mu Meme"},{"id":"mummy-finance","symbol":"mmy","name":"Mummy Finance"},{"id":"munch-token","symbol":"munch","name":"Munch"},{"id":"mundocrypto","symbol":"mct","name":"Mundocrypto"},{"id":"mundo-token","symbol":"$mundo","name":"MUNDO"},{"id":"muni","symbol":"muni","name":"MUNI"},{"id":"murasaki","symbol":"mura","name":"Murasaki"},{"id":"mus","symbol":"mus","name":"Musashi Finance"},{"id":"musd","symbol":"musd","name":"mStable USD"},{"id":"muse-2","symbol":"muse","name":"Muse DAO"},{"id":"muse-ent-nft","symbol":"msct","name":"Muse ENT NFT"},{"id":"museum-of-crypto-art","symbol":"moca","name":"Museum of Crypto Art"},{"id":"mushe","symbol":"xmu","name":"Mushe"},{"id":"musicai","symbol":"musicai","name":"MusicAI"},{"id":"musicfi","symbol":"mf","name":"MusicFi"},{"id":"musicn","symbol":"mint","name":"MusicN"},{"id":"musk-dao","symbol":"musk","name":"MUSK DAO"},{"id":"musk-doge","symbol":"mkd","name":"Musk Doge"},{"id":"musk-gold","symbol":"musk","name":"MUSK Gold"},{"id":"musk-melon","symbol":"melon","name":"Musk Melon"},{"id":"musk-metaverse","symbol":"metamusk","name":"Musk Metaverse"},{"id":"must","symbol":"must","name":"Must"},{"id":"mutant-froggo","symbol":"froggo","name":"Mutant Froggo"},{"id":"mute","symbol":"mute","name":"Mute"},{"id":"muu-inu","symbol":"$muu","name":"MUU"},{"id":"muuu","symbol":"muuu","name":"Muuu Finance"},{"id":"muverse","symbol":"mu","name":"Muverse"},{"id":"mvpad","symbol":"mvd","name":"MvPad"},{"id":"mvs-multiverse","symbol":"mvs","name":"MVS Multiverse"},{"id":"mxc","symbol":"mxc","name":"MXC"},{"id":"mxgp-fan-token","symbol":"mxgp","name":"MXGP Fan Token"},{"id":"mxm","symbol":"mxm","name":"MXM"},{"id":"mxmboxceus-token","symbol":"mbe","name":"MxmBoxcEus Token"},{"id":"mx-token","symbol":"mx","name":"MX"},{"id":"mx-token-2","symbol":"mxt","name":"MX TOKEN"},{"id":"mybit-token","symbol":"myb","name":"MyBit"},{"id":"mybricks","symbol":"bricks","name":"MyBricks"},{"id":"myce","symbol":"yce","name":"MYCE"},{"id":"mycelium","symbol":"myc","name":"Mycelium"},{"id":"my-ceremonial-event","symbol":"myce","name":"MY Ceremonial Event"},{"id":"my-defi-legends","symbol":"dlegends","name":"My DeFi Legends"},{"id":"my-defi-pet","symbol":"dpet","name":"My DeFi Pet"},{"id":"my-identity-coin","symbol":"myid","name":"My Identity Coin"},{"id":"my-liquidity-partner","symbol":"mlp","name":"My Liquidity Partner"},{"id":"my-master-war","symbol":"mat","name":"My Master War"},{"id":"mymessage","symbol":"mesa","name":"myMessage"},{"id":"my-metatrader","symbol":"mmt","name":"My MetaTrader"},{"id":"my-neighbor-alice","symbol":"alice","name":"My Neighbor Alice"},{"id":"myntpay","symbol":"mynt","name":"MyntPay"},{"id":"myobu","symbol":"myobu","name":"Myōbu"},{"id":"mypiggiesbank","symbol":"piggie","name":"MyPiggiesBank"},{"id":"mypoints-e-commerce","symbol":"mypo","name":"MyPoints E-Commerce"},{"id":"myria","symbol":"myria","name":"Myria"},{"id":"myriadcoin","symbol":"xmy","name":"Myriad"},{"id":"myriad-social","symbol":"myria","name":"Myriad Social"},{"id":"mysterium","symbol":"myst","name":"Mysterium"},{"id":"myteamcoin","symbol":"myc","name":"Myteamcoin"},{"id":"mytheria","symbol":"myra","name":"Mytheria"},{"id":"mythic-ore","symbol":"more","name":"Mythic Ore"},{"id":"mythos","symbol":"myth","name":"Mythos"},{"id":"mytoken","symbol":"mt","name":"MyToken"},{"id":"mytvchain","symbol":"mytv","name":"MyTVchain"},{"id":"n00dle","symbol":"n00d","name":"n00dle"},{"id":"nabob","symbol":"nabob","name":"Nabob"},{"id":"nabox","symbol":"nabox","name":"Nabox"},{"id":"nacho-finance","symbol":"nacho","name":"Nacho Finance"},{"id":"nada-protocol-token","symbol":"nada","name":"NADA Protocol Token"},{"id":"nafter","symbol":"naft","name":"Nafter"},{"id":"nafty","symbol":"nafty","name":"Nafty"},{"id":"naga","symbol":"ngc","name":"NAGA"},{"id":"nahmii","symbol":"nii","name":"Nahmii"},{"id":"naka-bodhi-token","symbol":"nbot","name":"Naka Bodhi"},{"id":"nakamoto-games","symbol":"naka","name":"Nakamoto Games"},{"id":"name-changing-token","symbol":"nct","name":"Name Change"},{"id":"namecoin","symbol":"nmc","name":"Namecoin"},{"id":"nami-corporation-token","symbol":"nami","name":"Nami Corporation"},{"id":"nano","symbol":"xno","name":"Nano"},{"id":"nanobyte","symbol":"nbt","name":"NanoByte"},{"id":"nano-dogecoin","symbol":"indc","name":"Nano Dogecoin"},{"id":"nanometer-bitcoin","symbol":"nmbtc","name":"NanoMeter Bitcoin"},{"id":"naos-finance","symbol":"naos","name":"NAOS Finance"},{"id":"napoleon-x","symbol":"npx","name":"Napoleon X"},{"id":"napoli-fan-token","symbol":"nap","name":"Napoli Fan Token"},{"id":"narfex","symbol":"nrfx","name":"Narfex"},{"id":"nasdacoin","symbol":"nsd","name":"Nasdacoin"},{"id":"nasdex-token","symbol":"nsdx","name":"NASDEX"},{"id":"natas-token","symbol":"natas","name":"NaTaS Token"},{"id":"natiol","symbol":"nai","name":"Natiol"},{"id":"nation3","symbol":"nation","name":"Nation3"},{"id":"native-utility-token","symbol":"nut","name":"Native Utility"},{"id":"natural-farm-union-protocol","symbol":"nfup","name":"Natural Farm Union Protocol"},{"id":"nature-based-offset","symbol":"nbo","name":"Nature Based Offset"},{"id":"natus-vincere-fan-token","symbol":"navi","name":"Natus Vincere Fan Token"},{"id":"nav-coin","symbol":"nav","name":"Navcoin"},{"id":"navibration","symbol":"navi","name":"Navibration"},{"id":"naxar","symbol":"naxar","name":"Naxar"},{"id":"nayuta-coin","symbol":"nc","name":"Nayuta Coin"},{"id":"ndau","symbol":"ndau","name":"Ndau"},{"id":"ndb","symbol":"ndb","name":"NDB"},{"id":"near","symbol":"near","name":"NEAR Protocol"},{"id":"nearpad","symbol":"pad","name":"Pad.Fi"},{"id":"nearstarter","symbol":"nstart","name":"NEARStarter"},{"id":"neblio","symbol":"nebl","name":"Neblio"},{"id":"nebulas","symbol":"nas","name":"Nebulas"},{"id":"neeo","symbol":"neeo","name":"NEEO"},{"id":"nef-rune-rune-game","symbol":"nef","name":"NEF Rune (Rune.Game)"},{"id":"neftipedia","symbol":"nft","name":"NEFTiPEDiA"},{"id":"nefty","symbol":"nefty","name":"NeftyBlocks"},{"id":"neighbourhoods","symbol":"nht","name":"Neighbourhoods"},{"id":"neko","symbol":"neko","name":"NEKO"},{"id":"nekocoin","symbol":"nekos","name":"Nekocoin"},{"id":"nelore-coin","symbol":"nlc","name":"Nelore Coin"},{"id":"nem","symbol":"xem","name":"NEM"},{"id":"nemesis","symbol":"nms","name":"Nemesis"},{"id":"nemesis-dao","symbol":"nmsp","name":"Nemesis PRO"},{"id":"nemo","symbol":"nemo","name":"NEMO"},{"id":"neo","symbol":"neo","name":"NEO"},{"id":"neocortexai","symbol":"corai","name":"NeoCortexAI"},{"id":"neofi","symbol":"neofi","name":"NeoFi"},{"id":"neon","symbol":"neon","name":"Neon"},{"id":"neon-exchange","symbol":"nex","name":"Nash"},{"id":"neonomad-finance","symbol":"nni","name":"Neonomad Finance"},{"id":"neopin","symbol":"npt","name":"Neopin"},{"id":"neorbit","symbol":"safo","name":"SAFEONE CHAIN"},{"id":"neos-credits","symbol":"ncr","name":"Neos Credits"},{"id":"neo-tokyo","symbol":"bytes","name":"Neo Tokyo"},{"id":"neoworld-cash","symbol":"nash","name":"NeoWorld Cash"},{"id":"neoxa","symbol":"neox","name":"Neoxa"},{"id":"neptune-mutual","symbol":"npm","name":"Neptune Mutual"},{"id":"nerian-network","symbol":"nerian","name":"Nerian Network"},{"id":"nero","symbol":"npt","name":"Nero"},{"id":"nerva","symbol":"xnv","name":"Nerva"},{"id":"nerve-finance","symbol":"nrv","name":"Nerve Finance"},{"id":"nerveflux","symbol":"nerve","name":"NerveFlux"},{"id":"nervenetwork","symbol":"nvt","name":"NerveNetwork"},{"id":"nervos-network","symbol":"ckb","name":"Nervos Network"},{"id":"nest","symbol":"nest","name":"Nest Protocol"},{"id":"nest-arcade","symbol":"nesta","name":"Nest Arcade"},{"id":"nestegg-coin","symbol":"egg","name":"NestEgg Coin"},{"id":"nesten","symbol":"nit","name":"Nesten"},{"id":"nestree","symbol":"egg","name":"Nestree"},{"id":"neta","symbol":"neta","name":"NETA"},{"id":"netcoin","symbol":"net","name":"Netcoin"},{"id":"netcoincapital","symbol":"ncc","name":"Netcoincapital"},{"id":"netflix-tokenized-stock-defichain","symbol":"dnflx","name":"Netflix Tokenized Stock Defichain"},{"id":"nether","symbol":"ntr","name":"Nether"},{"id":"netm","symbol":"ntm","name":"Netm"},{"id":"neton","symbol":"nto","name":"Neton"},{"id":"netswap","symbol":"nett","name":"Netswap"},{"id":"netvrk","symbol":"ntvrk","name":"Netvrk"},{"id":"network-capital-token","symbol":"netc","name":"Network Capital Token"},{"id":"netzero","symbol":"nzero","name":"NETZERO"},{"id":"neumark","symbol":"neu","name":"Neumark"},{"id":"neural-ai","symbol":"neuralai","name":"Neural AI"},{"id":"neural-radiance-field","symbol":"nerf","name":"Neural Radiance Field"},{"id":"neuroni-ai","symbol":"neuroni","name":"Neuroni AI"},{"id":"neurotoken","symbol":"ntk","name":"Neuro NTK"},{"id":"neutra-finance","symbol":"neu","name":"Neutra Finance"},{"id":"neutrino","symbol":"xtn","name":"Neutrino Index Token"},{"id":"neutrino-system-base-token","symbol":"nsbt","name":"Neutrino System Base"},{"id":"neutron","symbol":"ntrn","name":"Neutron"},{"id":"neutron-1","symbol":"ntrn","name":"Neutron Coin"},{"id":"neuy","symbol":"neuy","name":"NEUY"},{"id":"nevacoin","symbol":"neva","name":"NevaCoin"},{"id":"newb-farm","symbol":"newb","name":"NewB.Farm"},{"id":"new-bitshares","symbol":"nbs","name":"New BitShares"},{"id":"newdex-token","symbol":"dex","name":"Newdex"},{"id":"new-earth-order-money","symbol":"neom","name":"New Earth Order Money"},{"id":"new-frontier-presents","symbol":"nfp","name":"New Frontier Presents"},{"id":"new-landbox","symbol":"land","name":"LandBox"},{"id":"newm","symbol":"newm","name":"NEWM"},{"id":"new-order","symbol":"newo","name":"New Order"},{"id":"new-paradigm-assets-solution","symbol":"npas","name":"New Paradigm Assets Solution"},{"id":"newscrypto-coin","symbol":"nwc","name":"Newscrypto Coin"},{"id":"newton","symbol":"ntn","name":"Newton"},{"id":"newton-project","symbol":"new","name":"Newton Project"},{"id":"newtowngaming","symbol":"ntg","name":"NEWTOWNGAMING"},{"id":"new-world-order","symbol":"state","name":"New World Order"},{"id":"new-year-token","symbol":"nyt","name":"New Year"},{"id":"newyorkcoin","symbol":"nyc","name":"NewYorkCoin"},{"id":"newyork-exchange","symbol":"nye","name":"NewYork Exchange"},{"id":"nexacoin","symbol":"nexa","name":"Nexa"},{"id":"nexalt","symbol":"xlt","name":"Nexalt"},{"id":"nexdax","symbol":"nt","name":"NexDAX"},{"id":"nexo","symbol":"nexo","name":"NEXO"},{"id":"nexon","symbol":"nxn","name":"Nexon"},{"id":"nextdao","symbol":"nax","name":"NextDAO"},{"id":"next-earth","symbol":"nxtt","name":"Next Earth"},{"id":"nextexchange","symbol":"next","name":"NEXT"},{"id":"next-level","symbol":"nxl","name":"Next Level"},{"id":"next-token","symbol":"nxt","name":"Next NXT"},{"id":"nextype-finance","symbol":"nt","name":"NEXTYPE Finance"},{"id":"nexum","symbol":"nexm","name":"Nexum"},{"id":"nexus","symbol":"nxs","name":"Nexus"},{"id":"nexus-asa","symbol":"gp","name":"Nexus ASA"},{"id":"nexus-dubai","symbol":"nxd","name":"Nexus Dubai"},{"id":"nexuspad","symbol":"nexus","name":"Nexuspad"},{"id":"nexus-token","symbol":"nexus","name":"Nexus Crypto Services"},{"id":"nezuko-inu","symbol":"nezuko","name":"Nezuko Inu"},{"id":"nft11","symbol":"nft11","name":"NFT11"},{"id":"nft2stake","symbol":"nft2$","name":"NFT2STAKE"},{"id":"nft-alley","symbol":"alley","name":"NFT Alley"},{"id":"nft-art-finance","symbol":"nftart","name":"NFT Art Finance"},{"id":"nftascii","symbol":"nftascii","name":"NFTASCII"},{"id":"nftb","symbol":"nftb","name":"NFTb"},{"id":"nftblackmarket","symbol":"nbm","name":"NFTBlackmarket"},{"id":"nftbomb","symbol":"nbp","name":"NFTBomb"},{"id":"nftbooks","symbol":"nftbs","name":"NFTBooks"},{"id":"nft-champions","symbol":"champ","name":"NFT Champions"},{"id":"nftcloud","symbol":"cloud","name":"NFTCloud"},{"id":"nftdao","symbol":"nao","name":"NFTDAO"},{"id":"nftdeli","symbol":"deli","name":"NFTDeli"},{"id":"nftearth","symbol":"nfte","name":"NFTEarth"},{"id":"nfteyez","symbol":"eye","name":"NftEyez"},{"id":"nftfundart","symbol":"nfa","name":"NFTFundArt"},{"id":"nftfy","symbol":"nftfy","name":"Nftfy"},{"id":"nft-global-platform","symbol":"nftg","name":"NFT Global Platform"},{"id":"nftify","symbol":"n1","name":"NFTify"},{"id":"nft-index","symbol":"nfti","name":"NFT Index"},{"id":"nftlaunch","symbol":"nftl","name":"NFTLaunch"},{"id":"nftlootbox","symbol":"loot","name":"LootBox.io"},{"id":"nft-maker","symbol":"$nmkr","name":"NMKR"},{"id":"nftmall","symbol":"gem","name":"NFTmall"},{"id":"nftmart-token","symbol":"nmt","name":"NFTMart"},{"id":"nftpad","symbol":"nftpad","name":"NFTPad"},{"id":"nft-protocol","symbol":"nft","name":"NFT Protocol"},{"id":"nftpunk-finance","symbol":"nftpunk","name":"NFTPunk.Finance"},{"id":"nftrade","symbol":"nftd","name":"NFTrade"},{"id":"nft-soccer-games","symbol":"nfsg","name":"NFT Soccer Games"},{"id":"nft-stars","symbol":"nfts","name":"NFT Stars"},{"id":"nft-starter","symbol":"nst","name":"NFT Starter"},{"id":"nftstyle","symbol":"nftstyle","name":"NFTStyle"},{"id":"nft-tech","symbol":"nftt","name":"NFT Tech"},{"id":"nft-tone","symbol":"tone","name":"NFT Tone"},{"id":"nft-worlds","symbol":"wrld","name":"NFT Worlds"},{"id":"nftx","symbol":"nftx","name":"NFTX"},{"id":"nfty-token","symbol":"nfty","name":"NFTY"},{"id":"ngatiger","symbol":"nga","name":"NGATiger"},{"id":"niftify","symbol":"nift","name":"Niftify"},{"id":"nifty-league","symbol":"nftl","name":"Nifty League"},{"id":"niftypays","symbol":"nifty","name":"NiftyPays"},{"id":"nifty-token","symbol":"nfty","name":"NFTY DeFi Protocol"},{"id":"nightingale-token","symbol":"ngit","name":"Nightingale Token"},{"id":"nightverse-game","symbol":"nvg","name":"NightVerse Game"},{"id":"niifi","symbol":"niifi","name":"NiiFi"},{"id":"nikplace","symbol":"nik","name":"Nikplace"},{"id":"nil-dao","symbol":"nil","name":"Nil DAO"},{"id":"nimbus-utility","symbol":"nimb","name":"Nimbus Utility"},{"id":"nimiq-2","symbol":"nim","name":"Nimiq"},{"id":"ninenoble","symbol":"nnn","name":"Ninenoble"},{"id":"ninja-panda-inu","symbol":"npi","name":"Ninja Panda Inu"},{"id":"ninja-protocol","symbol":"ninja","name":"Ninja Protocol"},{"id":"ninja-squad","symbol":"nst","name":"Ninja Squad"},{"id":"ninky","symbol":"ninky","name":"Idle Ninja Online"},{"id":"ninneko","symbol":"nino","name":"Ninneko"},{"id":"nintia-estate","symbol":"ninti","name":"Nintia Estate"},{"id":"niob","symbol":"niob","name":"NIOB"},{"id":"niobio-cash","symbol":"nbr","name":"Niobio"},{"id":"niobium-coin","symbol":"nbc","name":"Niobium Coin"},{"id":"nippon-lagoon","symbol":"nlc","name":"Nippon Lagoon"},{"id":"niros","symbol":"niros","name":"Niros"},{"id":"nirvana-ana","symbol":"ana","name":"Nirvana ANA"},{"id":"nirvana-chain","symbol":"nac","name":"Nirvana Chain"},{"id":"nirvana-meta-mnu-chain","symbol":"mnu","name":"Nirvana Meta MNU Chain"},{"id":"nirvana-nirv","symbol":"nirv","name":"Nirvana NIRV"},{"id":"nirvana-prana","symbol":"prana","name":"Nirvana prANA"},{"id":"nitro","symbol":"nitro","name":"Nitro"},{"id":"nitro-cartel","symbol":"trove","name":"Arbitrove Governance Token"},{"id":"nitroex","symbol":"ntx","name":"NitroEX"},{"id":"nitrofloki","symbol":"nifloki","name":"NitroFloki"},{"id":"nitro-league","symbol":"nitro","name":"Nitro League"},{"id":"nitro-network","symbol":"ncash","name":"Nitro Network"},{"id":"nitroshiba","symbol":"nishib","name":"NitroShiba"},{"id":"nix-bridge-token","symbol":"voice","name":"Voice"},{"id":"nkcl-classic","symbol":"nkclc","name":"NKCL Classic"},{"id":"nkn","symbol":"nkn","name":"NKN"},{"id":"nnsdao-protocol","symbol":"ndp","name":"NnsDAO Protocol"},{"id":"noah-s-ark-coin","symbol":"nac","name":"Noah's Ark Coin"},{"id":"noa-play","symbol":"noa","name":"NOA PLAY"},{"id":"nobility","symbol":"nbl","name":"Nobility"},{"id":"noderunners","symbol":"ndr","name":"Node Runners"},{"id":"nodeseeds","symbol":"nds","name":"Nodeseeds"},{"id":"nodestats","symbol":"ns","name":"Nodestats"},{"id":"nodetrade","symbol":"mnx","name":"Nodetrade"},{"id":"nodle-network","symbol":"nodl","name":"Nodle Network"},{"id":"nody","symbol":"nody","name":"Nody"},{"id":"noe-crypto-bank","symbol":"noe","name":"NOE GLOBAL"},{"id":"nogoaltoken","symbol":"ino","name":"NoGoal"},{"id":"noia-network","symbol":"noia","name":"Syntropy"},{"id":"noir-phygital","symbol":"noir","name":"Noir Phygital"},{"id":"noku","symbol":"noku","name":"Noku"},{"id":"nole-npc","symbol":"npc","name":"NPC DAO"},{"id":"nolimitcoin","symbol":"nlc","name":"NoLimitCoin"},{"id":"nomad-exiles","symbol":"pride","name":"Nomad Exiles"},{"id":"nominex","symbol":"nmx","name":"Nominex"},{"id":"non-fungible-yearn","symbol":"nfy","name":"Non-Fungible Yearn"},{"id":"no-one","symbol":"noone","name":"No One"},{"id":"nora-token","symbol":"nra","name":"Nora"},{"id":"nord-finance","symbol":"nord","name":"Nord Finance"},{"id":"norigo","symbol":"go!","name":"NoriGO!"},{"id":"nosana","symbol":"nos","name":"Nosana"},{"id":"nostra","symbol":"nos","name":"Nostra"},{"id":"nostra-uno","symbol":"uno","name":"UNO"},{"id":"nosturis","symbol":"ntrs","name":"Nosturis"},{"id":"nota","symbol":"usnota","name":"NOTA"},{"id":"notable","symbol":"nbl","name":"Notable"},{"id":"note","symbol":"note","name":"Note"},{"id":"not-financial-advice","symbol":"nfai","name":"Not Financial Advice"},{"id":"nothing","symbol":"nada","name":"Nothing"},{"id":"notional-finance","symbol":"note","name":"Notional Finance"},{"id":"novacoin","symbol":"nvc","name":"Novacoin"},{"id":"nova-finance","symbol":"nova","name":"Nova Finance"},{"id":"novara-calcio-fan-token","symbol":"nov","name":"Novara Calcio Fan Token"},{"id":"novem-gold","symbol":"nnn","name":"Novem Gold"},{"id":"novem-pro","symbol":"nvm","name":"Novem Pro"},{"id":"nowai","symbol":"$nowai","name":"NOWAI"},{"id":"nowar","symbol":"nowar","name":"Nowar"},{"id":"nowarshiba","symbol":"nshiba","name":"Nowarshiba"},{"id":"npc-coin","symbol":"npc","name":"NPC Coin"},{"id":"npick-block","symbol":"npick","name":"NPick Block"},{"id":"n-protocol","symbol":"n","name":"N Protocol"},{"id":"nshare","symbol":"nshare","name":"NSHARE"},{"id":"nsights","symbol":"nsi","name":"nSights"},{"id":"nsur-coin","symbol":"nsur","name":"NSUR Coin"},{"id":"nsure-network","symbol":"nsure","name":"Nsure Network"},{"id":"nucleon-space","symbol":"nut","name":"Nucleon"},{"id":"nucleon-xcfx","symbol":"xcfx","name":"Nucleon xCFX"},{"id":"nucleus-vision","symbol":"ncash","name":"Nucleus Vision"},{"id":"nuco-cloud","symbol":"ncdt","name":"Nuco.Cloud"},{"id":"nucypher","symbol":"nu","name":"NuCypher"},{"id":"nudes","symbol":"nudes","name":"NUDES"},{"id":"nugencoin","symbol":"nugen","name":"Nugencoin"},{"id":"nukplan","symbol":"nkpl","name":"Nukplan"},{"id":"nuls","symbol":"nuls","name":"Nuls"},{"id":"number-1-token","symbol":"nr1","name":"Number 1"},{"id":"numbers-protocol","symbol":"num","name":"NUM Token"},{"id":"numeraire","symbol":"nmr","name":"Numeraire"},{"id":"numi-shards","symbol":"numi","name":"Numi Shards"},{"id":"numisme","symbol":"nume","name":"NumisMe"},{"id":"numitor","symbol":"numi","name":"Numitor"},{"id":"nuna","symbol":"nuna","name":"Nuna"},{"id":"nunet","symbol":"ntx","name":"NuNet"},{"id":"nunu-spirits","symbol":"nnt","name":"Nunu Spirits"},{"id":"nurifootball","symbol":"nrfb","name":"NuriFootBall"},{"id":"nusa-finance","symbol":"nusa","name":"NUSA"},{"id":"nusd","symbol":"susd","name":"sUSD"},{"id":"nusd-hotbit","symbol":"nusd","name":"nUSD (HotBit)"},{"id":"nutgain","symbol":"nutgv2","name":"NUTGAIN"},{"id":"nutsdao","symbol":"nuts","name":"NutsDAO"},{"id":"nvidia-tokenized-stock-defichain","symbol":"dnvda","name":"Nvidia Tokenized Stock Defichain"},{"id":"nvirworld","symbol":"nvir","name":"NvirWorld"},{"id":"nxd-next","symbol":"nxdt","name":"NXD Next"},{"id":"nxm","symbol":"nxm","name":"Nexus Mutual"},{"id":"nxt","symbol":"nxt","name":"NXT"},{"id":"nxusd","symbol":"nxusd","name":"NXUSD"},{"id":"nyancoin","symbol":"kat","name":"KatKoyn"},{"id":"nycccoin","symbol":"nyc","name":"NewYorkCityCoin"},{"id":"nym","symbol":"nym","name":"Nym"},{"id":"nyzo","symbol":"nyzo","name":"Nyzo"},{"id":"o3-swap","symbol":"o3","name":"O3 Swap"},{"id":"o5o","symbol":"o5o","name":"O5O"},{"id":"oasis-network","symbol":"rose","name":"Oasis Network"},{"id":"oasys","symbol":"oas","name":"Oasys"},{"id":"oath","symbol":"oath","name":"OATH"},{"id":"obortech","symbol":"obot","name":"Obortech"},{"id":"obrok","symbol":"obrok","name":"OBRok"},{"id":"observer-coin","symbol":"obsr","name":"Observer"},{"id":"obsidium","symbol":"obs","name":"Obsidium"},{"id":"obtoken","symbol":"obt","name":"OB"},{"id":"ocavu-network","symbol":"ocavu","name":"Ocavu Network"},{"id":"occamfi","symbol":"occ","name":"OccamFi"},{"id":"occamx","symbol":"ocx","name":"OccamX"},{"id":"oceanex","symbol":"oce","name":"OceanEX"},{"id":"oceanland","symbol":"oland","name":"OceanLand"},{"id":"ocean-protocol","symbol":"ocean","name":"Ocean Protocol"},{"id":"oceans-finance-v2","symbol":"oceansv2","name":"Oceans Finance"},{"id":"oceans-swap","symbol":"odex","name":"Oceans Swap"},{"id":"ociswap","symbol":"oci","name":"Ociswap"},{"id":"oc-protocol","symbol":"ocp","name":"OC Protocol"},{"id":"octafarm","symbol":"octf","name":"Octafarm"},{"id":"octane-protocol-token","symbol":"octane","name":"Octane Protocol"},{"id":"octaplex-network","symbol":"plx","name":"Octaplex Network"},{"id":"octaspace","symbol":"octa","name":"OctaSpace"},{"id":"octofi","symbol":"octo","name":"OctoFi"},{"id":"octo-gaming","symbol":"otk","name":"Octokn"},{"id":"octopus-network","symbol":"oct","name":"Octopus Network"},{"id":"octopus-protocol","symbol":"ops","name":"Octopus Protocol"},{"id":"octorand","symbol":"octo","name":"Octorand"},{"id":"octus-bridge","symbol":"bridge","name":"Octus Bridge"},{"id":"octus-social-media-market","symbol":"octsmm","name":"Octus Social Media Market"},{"id":"oddz","symbol":"oddz","name":"Oddz"},{"id":"odem","symbol":"ode","name":"ODEM"},{"id":"odin-protocol","symbol":"odin","name":"Odin Protocol"},{"id":"odop","symbol":"odop","name":"oDOP"},{"id":"oduwa-coin","symbol":"owc","name":"Oduwa Coin"},{"id":"odyssey","symbol":"ocn","name":"Odyssey"},{"id":"odysseywallet","symbol":"odys","name":"OdysseyWallet"},{"id":"oec-bch","symbol":"bchk","name":"OEC BCH"},{"id":"oec-binance-coin","symbol":"bnb","name":"OEC Binance Coin"},{"id":"oec-btc","symbol":"btck","name":"OEC BTC"},{"id":"oec-chainlink","symbol":"linkk","name":"OEC Chainlink"},{"id":"oec-dai","symbol":"daik","name":"OEC DAI"},{"id":"oec-dot","symbol":"dotk","name":"OEC DOT"},{"id":"oec-etc","symbol":"etck","name":"OEC ETC"},{"id":"oec-eth","symbol":"ethk","name":"OEC ETH"},{"id":"oec-fil","symbol":"filk","name":"OEC FIL"},{"id":"oec-ltc","symbol":"ltck","name":"OEC LTC"},{"id":"oec-shib","symbol":"shibk","name":"OEC SHIB"},{"id":"oec-token","symbol":"okt","name":"OKT Chain"},{"id":"oec-tron","symbol":"trxk","name":"OEC Tron"},{"id":"oec-uni","symbol":"unik","name":"OEC UNI"},{"id":"ofero","symbol":"ofe","name":"Ofero"},{"id":"official-crypto-cowboy-token","symbol":"occt","name":"Official Crypto Cowboy"},{"id":"offshift","symbol":"xft","name":"Offshift"},{"id":"offshift-anonusd","symbol":"anonusd","name":"Offshift anonUSD"},{"id":"ofi-cash","symbol":"ofi","name":"OFI.cash"},{"id":"og-fan-token","symbol":"og","name":"OG Fan Token"},{"id":"oggy-inu","symbol":"oggy","name":"Oggy Inu"},{"id":"oh-finance","symbol":"oh","name":"Oh! Finance"},{"id":"ohmd","symbol":"$wsohmd","name":"OHMD"},{"id":"oho-blockchain","symbol":"oho","name":"OHO Blockchain"},{"id":"oikos","symbol":"oks","name":"Oikos"},{"id":"oiler","symbol":"oil","name":"Oiler"},{"id":"oin-finance","symbol":"oin","name":"OIN Finance"},{"id":"oiocoin","symbol":"oioc","name":"OIOCoin"},{"id":"ojamu","symbol":"oja","name":"Ojamu"},{"id":"okage-inu","symbol":"okage","name":"Okage Inu"},{"id":"okaleido","symbol":"oka","name":"Okaleido"},{"id":"okami-lana","symbol":"okana","name":"Okami Lana"},{"id":"okb","symbol":"okb","name":"OKB"},{"id":"okcash","symbol":"ok","name":"Okcash"},{"id":"okex-fly","symbol":"okfly","name":"Okex Fly"},{"id":"okeycoin","symbol":"okey","name":"OKEYCOIN"},{"id":"okidoki-social","symbol":"doki","name":"Okidoki Social"},{"id":"ok-lets-go","symbol":"oklg","name":"ok.lets.go."},{"id":"okletsplay","symbol":"oklp","name":"OkLetsPlay"},{"id":"okratech-token","symbol":"ort","name":"Okratech"},{"id":"okse","symbol":"okse","name":"Okse"},{"id":"okuru","symbol":"xot","name":"Okuru"},{"id":"okx-staked-dot1","symbol":"okdot1","name":"OKX Staked DOT1"},{"id":"okx-staked-dot2","symbol":"okdot2","name":"OKX Staked DOT2"},{"id":"old-bitcoin","symbol":"bc","name":"Old Bitcoin"},{"id":"olecoin","symbol":"ole","name":"OleCoin"},{"id":"olive","symbol":"olv","name":"OLIVE"},{"id":"olivecash","symbol":"olive","name":"Olive Cash"},{"id":"oloid","symbol":"oloid","name":"OLOID"},{"id":"olympus","symbol":"ohm","name":"Olympus"},{"id":"olympus-v1","symbol":"ohm","name":"Olympus v1"},{"id":"olyverse","symbol":"oly","name":"Olyverse"},{"id":"omax-token","symbol":"omax","name":"Omax"},{"id":"ombre","symbol":"omb","name":"Ombre"},{"id":"omchain","symbol":"omc","name":"Omchain"},{"id":"omega","symbol":"omega","name":"OMEGA"},{"id":"omega-network","symbol":"omn","name":"Omega Network"},{"id":"omega-particle","symbol":"omp","name":"Omega Particle"},{"id":"omisego","symbol":"omg","name":"OMG Network"},{"id":"ommniverse","symbol":"ommi","name":"Ommniverse"},{"id":"omni","symbol":"omni","name":"Omni"},{"id":"omniaverse","symbol":"omnia","name":"OmniaVerse"},{"id":"omni-consumer-protocol","symbol":"ocp","name":"Omni Consumer Protocol"},{"id":"omnidex","symbol":"charm","name":"OmniDex"},{"id":"omnisea","symbol":"osea","name":"Omnisea"},{"id":"omniwhirl","symbol":"whirl","name":"OmniWhirl"},{"id":"omo-exchange","symbol":"omo","name":"OMO Exchange"},{"id":"omotenashicoin","symbol":"mtns","name":"OmotenashiCoin"},{"id":"onbuff","symbol":"onit","name":"ONBUFF"},{"id":"onchain-trade","symbol":"ot","name":"Onchain Trade"},{"id":"onchain-trade-protocol","symbol":"ot","name":"Onchain Trade Protocol"},{"id":"one","symbol":"one","name":"One"},{"id":"one-basis-cash","symbol":"obs","name":"One Basis Cash"},{"id":"onebtc","symbol":"onebtc","name":"Legacy oneBTC"},{"id":"one-cash","symbol":"onc","name":"One Cash"},{"id":"onedex","symbol":"one","name":"OneDex"},{"id":"one-hundred-million-inu","symbol":"ohmi","name":"One Hundred Million Inu"},{"id":"oneichi","symbol":"oneichi","name":"oneICHI"},{"id":"one-ledger","symbol":"olt","name":"OneLedger"},{"id":"onemoon","symbol":"onemoon","name":"OneMoon"},{"id":"one-piece","symbol":"onepiece","name":"ONE PIECE"},{"id":"onerare","symbol":"orare","name":"OneRare"},{"id":"onering","symbol":"ring","name":"OneRing"},{"id":"oneroot-network","symbol":"rnt","name":"OneRoot Network"},{"id":"one-share","symbol":"ons","name":"One Share"},{"id":"onespace","symbol":"1sp","name":"Onespace"},{"id":"oneswap-dao-token","symbol":"ones","name":"OneSwap DAO"},{"id":"one-world-coin","symbol":"owo","name":"One World Coin"},{"id":"ong","symbol":"ong","name":"Ontology Gas"},{"id":"onigiri","symbol":"onigiri","name":"Onigiri"},{"id":"onigiri-neko","symbol":"onigi","name":"Onigiri Neko"},{"id":"oni-token","symbol":"oni","name":"ONINO"},{"id":"online-cold-wallet","symbol":"ocw","name":"Online Cold Wallet"},{"id":"only1","symbol":"like","name":"Only1"},{"id":"onomy-protocol","symbol":"nom","name":"Onomy Protocol"},{"id":"onooks","symbol":"ooks","name":"Onooks"},{"id":"onston","symbol":"onston","name":"Onston"},{"id":"ontology","symbol":"ont","name":"Ontology"},{"id":"onus","symbol":"onus","name":"ONUS"},{"id":"onx-finance","symbol":"onx","name":"OnX Finance"},{"id":"onyx","symbol":"onyx","name":"Onyx"},{"id":"onyxdao","symbol":"onyx","name":"OnyxDAO"},{"id":"oobit","symbol":"obt","name":"Oobit"},{"id":"oogear","symbol":"og","name":"Oogear"},{"id":"oogi","symbol":"oogi","name":"OOGI"},{"id":"ookeenga","symbol":"okg","name":"Ookeenga"},{"id":"ooki","symbol":"ooki","name":"Ooki"},{"id":"oolongswap","symbol":"olo","name":"OolongSwap"},{"id":"oort-digital","symbol":"oort","name":"Oort Digital"},{"id":"ooze","symbol":"ooze","name":"Ooze"},{"id":"opacity","symbol":"opct","name":"Opacity"},{"id":"opalcoin","symbol":"auop","name":"Opalcoin"},{"id":"openai-erc","symbol":"openai erc","name":"OpenAI ERC"},{"id":"openalexa-protocol","symbol":"oap","name":"OpenAlexa Protocol"},{"id":"openanx","symbol":"oax","name":"OAX"},{"id":"openblox","symbol":"obx","name":"OpenBlox"},{"id":"opendao","symbol":"sos","name":"OpenDAO"},{"id":"open-governance-token","symbol":"open","name":"OPEN Governance"},{"id":"openleverage","symbol":"ole","name":"OpenLeverage"},{"id":"openlink","symbol":"olink","name":"OpenLink"},{"id":"openlive-nft","symbol":"opv","name":"OpenLive NFT"},{"id":"open-meta-trade","symbol":"omt","name":"Open Meta Trade"},{"id":"openocean","symbol":"ooe","name":"OpenOcean"},{"id":"open-platform","symbol":"open","name":"Open Platform"},{"id":"open-proprietary-protocol","symbol":"opp","name":"Open Proprietary Protocol"},{"id":"openstream-world","symbol":"osw","name":"OpenStream World"},{"id":"openswap","symbol":"oswap","name":"OpenSwap"},{"id":"openswap-token","symbol":"openx","name":"OpenSwap.One"},{"id":"openx-locked-velo","symbol":"opxvevelo","name":"OpenX Locked Velo"},{"id":"openxswap","symbol":"openx","name":"OpenXSwap"},{"id":"openxswap-gov-token","symbol":"xopenx","name":"OpenXSwap Gov. Token"},{"id":"operon-origins","symbol":"oro","name":"Operon Origins"},{"id":"opes-wrapped-pe","symbol":"wpe","name":"OPES (Wrapped PE)"},{"id":"opipets","symbol":"opip","name":"OpiPets"},{"id":"opium","symbol":"opium","name":"Opium"},{"id":"oppa","symbol":"oppa","name":"OPPA"},{"id":"oppa-token","symbol":"oppa","name":"OPPA Token"},{"id":"optical-bitcoin","symbol":"obtc","name":"Optical Bitcoin"},{"id":"opticash","symbol":"opch","name":"Opticash"},{"id":"optimism","symbol":"op","name":"Optimism"},{"id":"optimus","symbol":"optcm","name":"Optimus"},{"id":"optimus-ai","symbol":"opti","name":"Optimus AI"},{"id":"optimus-inu","symbol":"opinu","name":"Optimus Inu"},{"id":"optimus-opt","symbol":"opt","name":"Optimus OPT"},{"id":"optimus-opt2","symbol":"opt2","name":"Optimus OPT2"},{"id":"optimus-opt3","symbol":"opt3","name":"Optimus OPT3"},{"id":"option-panda-platform","symbol":"opa","name":"Option Panda Platform"},{"id":"option-room","symbol":"room","name":"OptionRoom"},{"id":"opulous","symbol":"opul","name":"Opulous"},{"id":"opx-finance","symbol":"opx","name":"OPX Finance"},{"id":"opxsliz","symbol":"opxvesliz","name":"opxSliz"},{"id":"opyn-squeeth","symbol":"osqth","name":"Opyn Squeeth"},{"id":"oraclechain","symbol":"oct","name":"OracleChain"},{"id":"oracle-dao","symbol":"orc","name":"Oracle Dao"},{"id":"oracleswap","symbol":"oracle","name":"OracleSwap"},{"id":"oracolxor","symbol":"xor","name":"Oracolxor"},{"id":"oragonx","symbol":"orgn","name":"OragonX"},{"id":"oraichain-token","symbol":"orai","name":"Oraichain"},{"id":"oraidex","symbol":"oraix","name":"OraiDEX"},{"id":"orakuru","symbol":"ork","name":"Orakuru"},{"id":"orao-network","symbol":"orao","name":"ORAO Network"},{"id":"orbeon-protocol","symbol":"orbn","name":"Orbeon Protocol"},{"id":"orbis","symbol":"orbc","name":"Orbis"},{"id":"orbitau-taureum","symbol":"taum","name":"Orbitau Taureum"},{"id":"orbit-bridge-klaytn-belt","symbol":"obelt","name":"Orbit Bridge Klaytn BELT"},{"id":"orbit-bridge-klaytn-binance-coin","symbol":"obnb","name":"Orbit Bridge Klaytn Binance Coin"},{"id":"orbit-bridge-klaytn-ethereum","symbol":"oeth","name":"Orbit Bridge Klaytn Ethereum"},{"id":"orbit-bridge-klaytn-handy","symbol":"ohandy","name":"Orbit Bridge Klaytn Handy"},{"id":"orbit-bridge-klaytn-matic","symbol":"omatic","name":"Orbit Bridge Klaytn MATIC"},{"id":"orbit-bridge-klaytn-orbit-chain","symbol":"oorc","name":"Orbit Bridge Klaytn Orbit Chain"},{"id":"orbit-bridge-klaytn-ripple","symbol":"oxrp","name":"Orbit Bridge Klaytn Ripple"},{"id":"orbit-bridge-klaytn-usdc","symbol":"ousdc","name":"Orbit Bridge Klaytn USDC"},{"id":"orbit-bridge-klaytn-usd-tether","symbol":"ousdt","name":"Orbit Bridge Klaytn USD Tether"},{"id":"orbit-bridge-klaytn-wrapped-btc","symbol":"owbtc","name":"Orbit Bridge Klaytn Wrapped BTC"},{"id":"orbit-chain","symbol":"orc","name":"Orbit Chain"},{"id":"orbitcoin","symbol":"orb","name":"Orbitcoin"},{"id":"orbit-token","symbol":"orbit","name":"First On The Moon ORBIT"},{"id":"orbler","symbol":"orbr","name":"Orbler"},{"id":"orbs","symbol":"orbs","name":"Orbs"},{"id":"orca","symbol":"orca","name":"Orca"},{"id":"orca-avai","symbol":"avai","name":"Orca AVAI"},{"id":"orcadao","symbol":"orca","name":"Orca DAO"},{"id":"orchid-protocol","symbol":"oxt","name":"Orchid Protocol"},{"id":"orclands-metaverse","symbol":"orc","name":"Orclands Metaverse"},{"id":"ordinal-btc","symbol":"obtc","name":"Ordinal BTC"},{"id":"ordinal-doge","symbol":"odoge","name":"Ordinal Doge"},{"id":"ordinals-finance","symbol":"ofi","name":"Ordinals Finance"},{"id":"ordinex","symbol":"ord","name":"ordinex"},{"id":"oreoswap","symbol":"oreo","name":"OreoSwap"},{"id":"ore-token","symbol":"ore","name":"ORE"},{"id":"origen-defi","symbol":"origen","name":"Origen DEFI"},{"id":"original-crypto-coin","symbol":"tusc","name":"The Universal Settlement Coin"},{"id":"origindao","symbol":"og","name":"OriginDAO"},{"id":"origin-dollar","symbol":"ousd","name":"Origin Dollar"},{"id":"origin-dollar-governance","symbol":"ogv","name":"Origin Dollar Governance"},{"id":"origin-protocol","symbol":"ogn","name":"Origin Protocol"},{"id":"origin-sport","symbol":"ors","name":"Origin Sport"},{"id":"origintrail","symbol":"trac","name":"OriginTrail"},{"id":"origo","symbol":"ogo","name":"Origo"},{"id":"origyn-foundation","symbol":"ogy","name":"ORIGYN Foundation"},{"id":"orion-money","symbol":"orion","name":"Orion Money"},{"id":"orion-protocol","symbol":"orn","name":"Orion Protocol"},{"id":"orkan","symbol":"ork","name":"Orkan"},{"id":"orlando-chain","symbol":"orl","name":"Orlando Chain"},{"id":"ormeus-cash","symbol":"omc","name":"Ormeus Cash"},{"id":"ormeuscoin","symbol":"orme","name":"Ormeus Coin"},{"id":"ormeus-ecosystem","symbol":"eco","name":"Ormeus Ecosystem"},{"id":"oro","symbol":"oro","name":"ORO"},{"id":"osis","symbol":"osis","name":"OSIS"},{"id":"osk","symbol":"osk","name":"OSK"},{"id":"osmiumcoin","symbol":"os76","name":"OsmiumCoin"},{"id":"osmosis","symbol":"osmo","name":"Osmosis"},{"id":"otcbtc-token","symbol":"otb","name":"OTCBTC"},{"id":"otherdao","symbol":"othr","name":"OtherDAO"},{"id":"otocash","symbol":"oto","name":"OTOCASH"},{"id":"otterclam","symbol":"clam","name":"OtterClam"},{"id":"ouro-governance-share","symbol":"ogs","name":"Ouro Governance Share"},{"id":"ouse","symbol":"ouse","name":"Ouse"},{"id":"ousg","symbol":"ousg","name":"OUSG"},{"id":"outdefine","symbol":"outdefine","name":"Outdefine"},{"id":"outer-ring","symbol":"gq","name":"Outer Ring MMO"},{"id":"outrace","symbol":"ore","name":"Outrace"},{"id":"ovato","symbol":"ovo","name":"Ovato"},{"id":"overlay-protocol","symbol":"ovl","name":"Overlay Protocol"},{"id":"overnight-dai","symbol":"dai+","name":"DAI+"},{"id":"ovols-floor-index","symbol":"$ovol","name":"Ovols Floor Index"},{"id":"ovo-nft-platform","symbol":"ovo","name":"OVO"},{"id":"ovr","symbol":"ovr","name":"Ovr"},{"id":"owldao","symbol":"owl","name":"OwlDAO"},{"id":"owloper","symbol":"owl","name":"Owloper Owl"},{"id":"ownly","symbol":"own","name":"Ownly"},{"id":"own-token","symbol":"own","name":"OWN Token"},{"id":"oxai-com","symbol":"oxai","name":"OxAI.com"},{"id":"oxbitcoin","symbol":"0xbtc","name":"0xBitcoin"},{"id":"oxbull-solana","symbol":"oxs","name":"Oxbull Solana"},{"id":"oxbull-tech-2","symbol":"oxb","name":"Oxbull Tech"},{"id":"oxsolid","symbol":"oxsolid","name":"oxSOLID"},{"id":"oxygen","symbol":"oxy","name":"Oxygen"},{"id":"oxymetatoken","symbol":"omt","name":"OxyMetaToken"},{"id":"oxyo2","symbol":"ox2","name":"OxyO2"},{"id":"ozonechain","symbol":"ozone","name":"Ozonechain"},{"id":"p2p-solutions-foundation","symbol":"p2ps","name":"P2P solutions foundation"},{"id":"p2p-taxi","symbol":"p2ptxt","name":"p2p taxi"},{"id":"paccoin","symbol":"pac","name":"PAC Protocol"},{"id":"pacific","symbol":"paf","name":"Pacific"},{"id":"pack","symbol":"pack","name":"Pack"},{"id":"packageportal","symbol":"port","name":"PackagePortal"},{"id":"packetchain","symbol":"ptcl","name":"Packetchain"},{"id":"packswap","symbol":"pact","name":"PactSwap"},{"id":"pacoca","symbol":"pacoca","name":"Pacoca"},{"id":"pagan-gods-fur-token","symbol":"fur","name":"Pagan Gods Fur"},{"id":"paid-network","symbol":"paid","name":"PAID Network"},{"id":"paint","symbol":"paint","name":"MurAll"},{"id":"paint-swap","symbol":"brush","name":"Paint Swap"},{"id":"pakcoin","symbol":"pak","name":"Pakcoin"},{"id":"palace","symbol":"paa","name":"Palace"},{"id":"paladin","symbol":"pal","name":"Paladin"},{"id":"palantir-tokenized-stock-defichain","symbol":"dpltr","name":"Palantir Tokenized Stock Defichain"},{"id":"palette","symbol":"plt","name":"Palette"},{"id":"palgold","symbol":"palg","name":"PalGold"},{"id":"pallapay","symbol":"palla","name":"Pallapay"},{"id":"palmpay","symbol":"palm","name":"PalmPay"},{"id":"palmswap","symbol":"palm","name":"PalmSwap"},{"id":"pana-dao","symbol":"pana","name":"PANA"},{"id":"pancake-bunny","symbol":"bunny","name":"Pancake Bunny"},{"id":"pancake-games","symbol":"gcake","name":"Pancake Games"},{"id":"pancake-hunny","symbol":"hunny","name":"Hunny Finance"},{"id":"pancakelock","symbol":"plock","name":"PancakeLock"},{"id":"pancakeswap-token","symbol":"cake","name":"PancakeSwap"},{"id":"pancaketools","symbol":"tcake","name":"PancakeTools"},{"id":"pandacoin","symbol":"pnd","name":"Pandacoin"},{"id":"panda-coin","symbol":"panda","name":"Panda Coin"},{"id":"pandadao","symbol":"panda","name":"PandaDAO"},{"id":"pandai","symbol":"pandai","name":"PandAI"},{"id":"pando","symbol":"pando","name":"Pando"},{"id":"pandora-cash","symbol":"pcash","name":"Pandora Cash"},{"id":"pandora-protocol","symbol":"pndr","name":"Pandora Finance"},{"id":"pandora-spirit","symbol":"psr","name":"Pandora Spirit"},{"id":"pando-token","symbol":"ptx","name":"PandoProject"},{"id":"pando-usd","symbol":"pusd","name":"Pando USD"},{"id":"pangea-governance-token","symbol":"stone","name":"PANGEA GOVERNANCE TOKEN"},{"id":"pangolin","symbol":"png","name":"Pangolin"},{"id":"pangolin-flare","symbol":"pfl","name":"Pangolin Flare"},{"id":"pangolin-hedera","symbol":"pbar","name":"Pangolin Hedera"},{"id":"pangolin-songbird","symbol":"psb","name":"Pangolin Songbird"},{"id":"panicswap","symbol":"panic","name":"PanicSwap"},{"id":"pankuku","symbol":"kuku","name":"panKUKU"},{"id":"pantheon-x","symbol":"xpn","name":"PANTHEON X"},{"id":"panther","symbol":"zkp","name":"Panther Protocol"},{"id":"pantomime","symbol":"panto","name":"Pantomime"},{"id":"pantos","symbol":"pan","name":"Pantos"},{"id":"panvala-pan","symbol":"pan","name":"Panvala Pan"},{"id":"papa-doge","symbol":"papadoge","name":"Papa Doge"},{"id":"paper-dab1cd41-029d-4207-b87f-fd98d6fe737c","symbol":"$paper","name":"$PAPER"},{"id":"paper-dao","symbol":"ppr","name":"Paper DAO"},{"id":"paper-fantom","symbol":"paper","name":"Paper"},{"id":"pappay","symbol":"pappay","name":"Pappay"},{"id":"parachute","symbol":"par","name":"Parachute"},{"id":"paradigm-zero","symbol":"pz","name":"Paradigm Zero"},{"id":"paradise-defi","symbol":"pdf","name":"Paradise Defi"},{"id":"paradisefi","symbol":"eden","name":"ParadiseFi"},{"id":"paradox-metaverse","symbol":"paradox","name":"Paradox Metaverse"},{"id":"paragen","symbol":"rgen","name":"Paragen"},{"id":"paragonsdao","symbol":"pdt","name":"ParagonsDAO"},{"id":"paralink-network","symbol":"para","name":"Paralink Network"},{"id":"parallel-finance","symbol":"para","name":"Parallel Finance"},{"id":"paras","symbol":"paras","name":"Paras"},{"id":"parasol-finance","symbol":"psol","name":"Parasol Finance"},{"id":"parastate","symbol":"state","name":"ParaState"},{"id":"paraswap","symbol":"psp","name":"ParaSwap"},{"id":"paratoken-2","symbol":"para","name":"Para"},{"id":"parex","symbol":"prx","name":"Parex"},{"id":"paribu-net","symbol":"prb","name":"Paribu Net"},{"id":"paribus","symbol":"pbx","name":"Paribus"},{"id":"paris-saint-germain-fan-token","symbol":"psg","name":"Paris Saint-Germain Fan Token"},{"id":"parma-calcio-1913-fan-token","symbol":"parma","name":"Parma Calcio 1913 Fan Token"},{"id":"parrotly","symbol":"pbirb","name":"Parrotly"},{"id":"parrot-protocol","symbol":"prt","name":"Parrot Protocol"},{"id":"parrot-usd","symbol":"pai","name":"Parrot USD"},{"id":"parsiq","symbol":"prq","name":"PARSIQ"},{"id":"parsl","symbol":"seed","name":"Parsl"},{"id":"par-stablecoin","symbol":"par","name":"Parallel"},{"id":"particl","symbol":"part","name":"Particl"},{"id":"particle-2","symbol":"prtcle","name":"Particle"},{"id":"particle-technology","symbol":"part","name":"Particle Technology"},{"id":"partneroid","symbol":"ptr","name":"Partner Coin"},{"id":"partyfi","symbol":"pfi","name":"PartyFi"},{"id":"pascalcoin","symbol":"pasc","name":"Pascal"},{"id":"pasha","symbol":"pasha","name":"Pasha"},{"id":"passive-income","symbol":"psi","name":"Passive Income"},{"id":"pastel","symbol":"psl","name":"Pastel"},{"id":"pathdao","symbol":"path","name":"PathDAO"},{"id":"pathfundv2","symbol":"path","name":"PathFundV2"},{"id":"patientory","symbol":"ptoy","name":"Patientory"},{"id":"patron","symbol":"pat","name":"Patron"},{"id":"paul-token","symbol":"paul","name":"PAUL"},{"id":"pavia","symbol":"pavia","name":"Pavia"},{"id":"paw","symbol":"paw","name":"Paw"},{"id":"pawn-my-nft","symbol":"pnft","name":"Pawn My NFT"},{"id":"paws-funds","symbol":"paws","name":"Paws Funds"},{"id":"pawswap","symbol":"paw","name":"PAWSWAP"},{"id":"pawthereum","symbol":"pawth","name":"Pawthereum"},{"id":"pawtocol","symbol":"upi","name":"Pawtocol"},{"id":"paw-v2","symbol":"paw","name":"Paw V2"},{"id":"pawzone","symbol":"paw","name":"PAWZONE"},{"id":"pax-gold","symbol":"paxg","name":"PAX Gold"},{"id":"paxos-standard","symbol":"usdp","name":"Pax Dollar"},{"id":"pax-world","symbol":"paxw","name":"pax.world"},{"id":"payaccept","symbol":"payt","name":"PayAccept"},{"id":"payb","symbol":"payb","name":"PayB"},{"id":"paybandcoin","symbol":"pybc","name":"PaybandCoin"},{"id":"paybit","symbol":"paybit","name":"PayBit"},{"id":"paybolt","symbol":"pay","name":"PayBolt"},{"id":"paycent","symbol":"pyn","name":"Paycent"},{"id":"paycer-protocol","symbol":"pcr","name":"Paycer Protocol"},{"id":"pay-coin","symbol":"pci","name":"Paycoin"},{"id":"paygo","symbol":"paygo","name":"PayGo"},{"id":"pay-it-now","symbol":"pin","name":"Pay It Now"},{"id":"paymastercoin","symbol":"pmc","name":"PayMasterCoin"},{"id":"paynet-coin","symbol":"payn","name":"PAYNET"},{"id":"paypolitan-token","symbol":"epan","name":"Paypolitan"},{"id":"payrue","symbol":"propel","name":"PayRue"},{"id":"payz-payments","symbol":"payz","name":"Payz Payments"},{"id":"pbtc35a","symbol":"pbtc35a","name":"pBTC35A"},{"id":"pchain","symbol":"pi","name":"Plian"},{"id":"pdbc-defichain","symbol":"dpdbc","name":"PDBC Defichain"},{"id":"pdx-coin","symbol":"pdx","name":"PDX Coin"},{"id":"peace-dao","symbol":"peace","name":"Peace DAO"},{"id":"peace-token","symbol":"pet","name":"Peace"},{"id":"peachfolio","symbol":"pchf","name":"Peachfolio"},{"id":"pea-farm","symbol":"pea","name":"Pea Farm"},{"id":"peak-finance","symbol":"peak","name":"Peak Finance"},{"id":"peak-token","symbol":"pktk","name":"Peak Token"},{"id":"peanut","symbol":"nux","name":"Peanut"},{"id":"pear","symbol":"pear","name":"Pear"},{"id":"peardao","symbol":"pex","name":"PearDAO"},{"id":"pearl-finance","symbol":"pearl","name":"Pearl Finance"},{"id":"pecora-network","symbol":"pen","name":"Pecora Network"},{"id":"peercoin","symbol":"ppc","name":"Peercoin"},{"id":"peerex-network","symbol":"perx","name":"PeerEx Network"},{"id":"peerguess","symbol":"guess","name":"PeerGuess"},{"id":"peet-defi","symbol":"pte","name":"Peet DeFi"},{"id":"pegasus-pow","symbol":"$pgs","name":"Pegasus"},{"id":"pegasys","symbol":"psys","name":"Pegasys"},{"id":"pegaxy-stone","symbol":"pgx","name":"Pegaxy Stone"},{"id":"pegazus-finance","symbol":"peg","name":"Pegazus Finance"},{"id":"pele-network","symbol":"pele","name":"PELE Network"},{"id":"pembrock","symbol":"pem","name":"Pembrock"},{"id":"pencil-dao","symbol":"pencil","name":"Pencil DAO"},{"id":"pendle","symbol":"pendle","name":"Pendle"},{"id":"pendulum-chain","symbol":"pen","name":"Pendulum"},{"id":"penguin-finance","symbol":"pefi","name":"Penguin Finance"},{"id":"penguin-karts","symbol":"pgk","name":"Penguin Karts"},{"id":"penny-token","symbol":"penny","name":"Penny Token"},{"id":"penrose-finance","symbol":"pen","name":"Penrose Finance"},{"id":"peony-coin","symbol":"pny","name":"Peony Coin"},{"id":"peoples-punk","symbol":"dddd","name":"People's Punk"},{"id":"peoplez","symbol":"lez","name":"Peoplez"},{"id":"peos","symbol":"peos","name":"pEOS"},{"id":"pepa-inu","symbol":"pepa","name":"Pepa Inu"},{"id":"pepe-bet","symbol":"pepebet","name":"PEPE.bet"},{"id":"pepe-ceo","symbol":"peo","name":"Pepe CEO"},{"id":"pepedex","symbol":"ppdex","name":"Pepedex"},{"id":"pepesol","symbol":"pepe","name":"PepeSol"},{"id":"pera-finance","symbol":"pera","name":"Pera Finance"},{"id":"peri-finance","symbol":"peri","name":"PERI Finance"},{"id":"perion","symbol":"perc","name":"Perion"},{"id":"perlin","symbol":"perl","name":"PERL.eco"},{"id":"permission-coin","symbol":"ask","name":"Permission Coin"},{"id":"perpetual-protocol","symbol":"perp","name":"Perpetual Protocol"},{"id":"perpetual-wallet","symbol":"pwt","name":"Perpetual Wallet"},{"id":"perpetuum-coin","symbol":"prp","name":"Perpetuum Coin"},{"id":"perp-inu","symbol":"perpi","name":"Perp Inu"},{"id":"perpy-finance","symbol":"pry","name":"Perpy Finance"},{"id":"perseus-fintech","symbol":"prs","name":"Perseus Fintech"},{"id":"persib-fan-token","symbol":"persib","name":"Persib Fan Token"},{"id":"persistence","symbol":"xprt","name":"Persistence"},{"id":"perth-mint-gold-token","symbol":"pmgt","name":"Perth Mint Gold Token"},{"id":"peruvian-national-football-team-fan-token","symbol":"fpft","name":"Peruvian National Football Team Fan Token"},{"id":"pesabase","symbol":"pesa","name":"Pesabase"},{"id":"peseta-digital","symbol":"ptd","name":"Peseta Digital"},{"id":"petals","symbol":"pts","name":"Petals"},{"id":"peth","symbol":"peth","name":"pETH"},{"id":"petoverse","symbol":"peto","name":"Petoverse"},{"id":"petroleum-oil","symbol":"oil","name":"Petroleum OIL"},{"id":"pexcoin","symbol":"pex","name":"Pexcoin"},{"id":"pftm","symbol":"pftm","name":"pFTM"},{"id":"pgala","symbol":"pgala","name":"pGALA"},{"id":"pha","symbol":"pha","name":"Phala"},{"id":"phaeton","symbol":"phae","name":"Phaeton"},{"id":"phantasia","symbol":"fant","name":"Phantasia"},{"id":"phantasma","symbol":"soul","name":"Phantasma"},{"id":"phantasma-energy","symbol":"kcal","name":"Phantasma Energy"},{"id":"phantom-protocol","symbol":"phm","name":"Phantom Protocol"},{"id":"phenix-finance-2","symbol":"phnx","name":"Phenix Finance (Cronos)"},{"id":"phenix-finance-polygon","symbol":"phnx","name":"Phenix Finance (Polygon)"},{"id":"philcoin","symbol":"phl","name":"Philcoin"},{"id":"phobos-token","symbol":"pbos","name":"Phobos Token"},{"id":"phoenix-chain","symbol":"phx","name":"Phoenix Chain"},{"id":"phoenixcoin","symbol":"pxc","name":"Phoenixcoin"},{"id":"phoenixdao","symbol":"phnx","name":"PhoenixDAO"},{"id":"phoenix-global","symbol":"phb","name":"Phoenix Global"},{"id":"phoenix-protocol-b7a9513c-36e9-4a6b-b6ae-6a1a76bb913e","symbol":"pp","name":"Phoenix Protocol"},{"id":"phoenix-token","symbol":"phx","name":"Phoenix Finance"},{"id":"phoneum","symbol":"pht","name":"Phoneum"},{"id":"phonon-dao","symbol":"phonon","name":"Phonon DAO"},{"id":"phore","symbol":"phr","name":"Phore"},{"id":"photochromic","symbol":"phcr","name":"PhotoChromic"},{"id":"photonswap","symbol":"photon","name":"PhotonSwap"},{"id":"phunk-vault-nftx","symbol":"phunk","name":"PHUNK Vault (NFTX)"},{"id":"phuntoken","symbol":"phtk","name":"Phun Token"},{"id":"phuture","symbol":"phtr","name":"Phuture"},{"id":"phuture-defi-index","symbol":"pdi","name":"Phuture DeFi Index"},{"id":"physis","symbol":"phy","name":"Physis"},{"id":"pias","symbol":"pias","name":"PIAS"},{"id":"pibble","symbol":"pib","name":"Pibble"},{"id":"picasso","symbol":"pica","name":"PICASSO"},{"id":"piccolo-inu","symbol":"pinu","name":"Piccolo Inu"},{"id":"pickle-finance","symbol":"pickle","name":"Pickle Finance"},{"id":"piconnect","symbol":"pico","name":"PiConnect"},{"id":"piedao-balanced-crypto-pie","symbol":"bcp","name":"PieDAO Balanced Crypto Pie"},{"id":"piedao-btc","symbol":"btc++","name":"PieDAO BTC++"},{"id":"piedao-defi","symbol":"defi++","name":"PieDAO DEFI++"},{"id":"piedao-defi-large-cap","symbol":"defi+l","name":"PieDAO DEFI Large Cap"},{"id":"piedao-dough-v2","symbol":"dough","name":"PieDAO DOUGH v2"},{"id":"pige-inu","symbol":"pinu","name":"Pige Inu"},{"id":"pigeoncoin","symbol":"pgn","name":"Pigeoncoin"},{"id":"pig-finance","symbol":"pig","name":"Pig Finance"},{"id":"piggy","symbol":"piggy","name":"Piggy"},{"id":"piggy-bank-token","symbol":"piggy","name":"PiggyBank"},{"id":"piggy-share","symbol":"pshare","name":"Piggy Share"},{"id":"pigs-2","symbol":"afp","name":"PIGS"},{"id":"pikachu","symbol":"pika","name":"Pika"},{"id":"pikaster","symbol":"mls","name":"Metaland Shares"},{"id":"pillar","symbol":"plr","name":"Pillar"},{"id":"pilot","symbol":"ptd","name":"Pilot"},{"id":"pine","symbol":"pine","name":"Pine"},{"id":"pi-network-defi","symbol":"pinetworkdefi","name":"Pi Network DeFi"},{"id":"pi-network-iou","symbol":"pi","name":"Pi Network"},{"id":"pine-world","symbol":"pwlc","name":"Pine World"},{"id":"pink-bnb","symbol":"pnb","name":"Pink BNB"},{"id":"pinkcoin","symbol":"pink","name":"Pinkcoin"},{"id":"pinkelon","symbol":"pinke","name":"PinkElon"},{"id":"pinkie-inu","symbol":"pinkie","name":"Pinkie Inu"},{"id":"pinkmoon","symbol":"pinkm","name":"PinkMoon"},{"id":"pinknode","symbol":"pnode","name":"Pinknode"},{"id":"pinkpea-finance","symbol":"pea","name":"PinkPea.Finance"},{"id":"pinksale","symbol":"pinksale","name":"PinkSale"},{"id":"pinkswap-token","symbol":"pinks","name":"PinkSwap"},{"id":"pintu-token","symbol":"ptu","name":"Pintu"},{"id":"piogold","symbol":"pio","name":"PioGold"},{"id":"pioneerpay","symbol":"ppay","name":"PioneerPay"},{"id":"pip","symbol":"pip","name":"PIP"},{"id":"pi-protocol","symbol":"pip","name":"Pi Protocol"},{"id":"piratecash","symbol":"pirate","name":"PirateCash"},{"id":"pirate-chain","symbol":"arrr","name":"Pirate Chain"},{"id":"piratecoin","symbol":"piratecoin☠","name":"PirateCoin"},{"id":"pirate-dice","symbol":"booty","name":"Pirate Dice"},{"id":"piratera","symbol":"pira","name":"Piratera"},{"id":"pirate-x-pirate","symbol":"pxp","name":"Pirate x Pirate"},{"id":"pirichain","symbol":"piri","name":"Pirichain"},{"id":"piston","symbol":"pstn","name":"Piston"},{"id":"pitbull","symbol":"pit","name":"Pitbull"},{"id":"pitbull-inu","symbol":"piti","name":"Pitbull Inu"},{"id":"pitch-fxs","symbol":"pitchfxs","name":"Pitch FXS"},{"id":"pivn","symbol":"pivn","name":"PIVN"},{"id":"pivot-token","symbol":"pvt","name":"Pivot"},{"id":"pivx","symbol":"pivx","name":"PIVX"},{"id":"pixel-battle","symbol":"pwc","name":"Pixel Battle"},{"id":"pixel-doge","symbol":"pxdoge","name":"Pixel Doge"},{"id":"pixelpotus","symbol":"pxl","name":"PixelPotus"},{"id":"pixelverse","symbol":"pixel","name":"PixelVerse"},{"id":"pixiaai","symbol":"pixia","name":"PixiaAI"},{"id":"pixie","symbol":"pix","name":"Pixie"},{"id":"pixiu-finance","symbol":"pixiu","name":"Pixiu Finance"},{"id":"pixl-coin-2","symbol":"pxlc","name":"Pixl Coin"},{"id":"pizza-game","symbol":"pizza","name":"Pizza Game"},{"id":"pizza-pug-coin","symbol":"ppug","name":"Pizza Pug Coin"},{"id":"pizza-usde","symbol":"pizza","name":"PIZZA"},{"id":"pkt","symbol":"pkt","name":"PKT"},{"id":"place-war","symbol":"place","name":"PlaceWar Governance"},{"id":"plan-b-dao","symbol":"planb","name":"Plan B DAO"},{"id":"planet","symbol":"pla","name":"PLANET"},{"id":"planetapeclub","symbol":"plac","name":"PlanetApeClub"},{"id":"planetcats","symbol":"catcoin","name":"PlanetCats"},{"id":"planet-finance","symbol":"aqua","name":"Planet Finance"},{"id":"planet-sandbox","symbol":"psb","name":"Planet Sandbox"},{"id":"planetwatch","symbol":"planets","name":"PlanetWatch"},{"id":"planq","symbol":"plq","name":"Planq"},{"id":"plant-empires","symbol":"pefi","name":"Plant Empires"},{"id":"plant-exodus","symbol":"pexo","name":"Plant Exodus"},{"id":"plant-vs-undead-token","symbol":"pvu","name":"Plant vs Undead"},{"id":"plasma-finance","symbol":"ppay","name":"Plasma Finance"},{"id":"plastiks","symbol":"plastik","name":"Plastiks"},{"id":"plata-network","symbol":"plata","name":"Plata Network"},{"id":"platincoin","symbol":"plc","name":"PlatinCoin"},{"id":"platinx","symbol":"ptx","name":"PlatinX"},{"id":"plato-farm","symbol":"mark","name":"Plato Farm"},{"id":"platon-network","symbol":"lat","name":"PlatON Network"},{"id":"platypus-finance","symbol":"ptp","name":"Platypus Finance"},{"id":"platypus-usd","symbol":"usp","name":"Platypus USD"},{"id":"playa3ull-games","symbol":"3ull","name":"Playa3ull Games"},{"id":"playcent","symbol":"pcnt","name":"Playcent"},{"id":"playchip","symbol":"pla","name":"PlayChip"},{"id":"playdapp","symbol":"pla","name":"PlayDapp"},{"id":"player-2","symbol":"deo","name":"Player 2"},{"id":"playermon","symbol":"pym","name":"Playermon"},{"id":"playgame","symbol":"pxg","name":"PlayGame"},{"id":"playground","symbol":"playa","name":"Playground"},{"id":"playground-waves-floor-index","symbol":"waves","name":"Playground Waves Floor Index"},{"id":"play-it-forward-dao","symbol":"pif","name":"Play It Forward DAO"},{"id":"playkey","symbol":"pkt","name":"PlayKey"},{"id":"playmarket","symbol":"pmt","name":"DAO PlayMarket 2.0"},{"id":"playmusic","symbol":"play","name":"Playmusic"},{"id":"playnity","symbol":"ply","name":"PlayNity"},{"id":"playpad","symbol":"ppad","name":"PlayPad"},{"id":"playposeidon-nft","symbol":"ppp","name":"PlayPoseidon NFT"},{"id":"plc-ultima","symbol":"plcu","name":"PLC Ultima"},{"id":"plearn","symbol":"pln","name":"PLEARN"},{"id":"pleasure-coin","symbol":"nsfw","name":"Pleasure Coin"},{"id":"plebe-gaming","symbol":"pleb","name":"Plebe Gaming"},{"id":"pledge","symbol":"plgr","name":"Pledge"},{"id":"pledgecamp","symbol":"plg","name":"Pledgecamp"},{"id":"plenty-dao","symbol":"plenty","name":"Plenty DeFi"},{"id":"plenty-ply","symbol":"ply","name":"Plenty PLY"},{"id":"plex","symbol":"plex","name":"PLEX"},{"id":"plexus-app","symbol":"plx","name":"PLEXUS"},{"id":"plgnet","symbol":"plug","name":"PL^Gnet"},{"id":"plotx","symbol":"plot","name":"PlotX"},{"id":"plug-chain","symbol":"pc","name":"Plug Chain"},{"id":"plugin","symbol":"pli","name":"Plugin"},{"id":"plunge","symbol":"plg","name":"Plunge"},{"id":"pluracoin","symbol":"plura","name":"PluraCoin"},{"id":"plusonecoin","symbol":"plus1","name":"PlusOneCoin"},{"id":"pluton","symbol":"plu","name":"Pluton"},{"id":"plutonian-dao","symbol":"pld","name":"Plutonian DAO"},{"id":"plutusdao","symbol":"pls","name":"PlutusDAO"},{"id":"plutus-dpx","symbol":"plsdpx","name":"Plutus DPX"},{"id":"plutusfi","symbol":"plut","name":"PlutusFi"},{"id":"pmg-coin","symbol":"pmg","name":"PMG Coin"},{"id":"pnetwork","symbol":"pnt","name":"pNetwork"},{"id":"poa-network","symbol":"poa","name":"POA Network"},{"id":"poc-blockchain","symbol":"poc","name":"POC Blockchain"},{"id":"pochi-inu","symbol":"pochi","name":"Pochi Inu"},{"id":"pocket-arena","symbol":"poc","name":"Pocket Arena"},{"id":"pocketcoin","symbol":"pkoin","name":"Pocketcoin"},{"id":"pocket-doge","symbol":"pckt","name":"Pocket"},{"id":"pocket-network","symbol":"pokt","name":"Pocket Network"},{"id":"pocket-project","symbol":"ppt","name":"Pocket ProjecT"},{"id":"pocoland","symbol":"poco","name":"Pocoland"},{"id":"podfast","symbol":"$fast","name":"PodFast"},{"id":"poet","symbol":"poe","name":"Po.et"},{"id":"pog-coin","symbol":"pog","name":"PolygonumOnline"},{"id":"poglana","symbol":"pog","name":"Poglana"},{"id":"point-coin","symbol":"point","name":"Point Coin"},{"id":"point-network","symbol":"point","name":"Point Network"},{"id":"pointpay","symbol":"pxp","name":"PointPay"},{"id":"poison-finance","symbol":"poi$on","name":"Poison Finance"},{"id":"pokedx","symbol":"pdx","name":"PokeDX"},{"id":"poken","symbol":"pkn","name":"Poken"},{"id":"pokeplay-token","symbol":"ppc","name":"PokePlay Token"},{"id":"pokerfi","symbol":"pokerfi","name":"PokerFi"},{"id":"polar","symbol":"polar","name":"POLAR"},{"id":"polaris","symbol":"polar","name":"Polarisdefi"},{"id":"polaris-finance-orbital","symbol":"orbital","name":"Polaris Finance Orbital"},{"id":"polaris-share","symbol":"pola","name":"Polaris Share"},{"id":"polars","symbol":"pol","name":"Polars"},{"id":"polar-sync","symbol":"polar","name":"Polar Sync"},{"id":"polar-token","symbol":"polar","name":"Polaris Finance Polar"},{"id":"polinate","symbol":"poli","name":"Polinate"},{"id":"polis","symbol":"polis","name":"Polis"},{"id":"polkabridge","symbol":"pbr","name":"PolkaBridge"},{"id":"polka-city","symbol":"polc","name":"Polkacity"},{"id":"polka-classic","symbol":"dotc","name":"Polka Classic"},{"id":"polkadex","symbol":"pdex","name":"Polkadex"},{"id":"polkadomain","symbol":"name","name":"PolkaDomain"},{"id":"polkadot","symbol":"dot","name":"Polkadot"},{"id":"polkaex","symbol":"pkex","name":"PolkaEx"},{"id":"polkafantasy","symbol":"xp","name":"PolkaFantasy"},{"id":"polkafoundry","symbol":"pkf","name":"Red Kite"},{"id":"polkally","symbol":"kally","name":"Kally"},{"id":"polkamarkets","symbol":"polk","name":"Polkamarkets"},{"id":"polkapet-world","symbol":"pets","name":"PolkaPet World"},{"id":"polkaplay","symbol":"polo","name":"NftyPlay"},{"id":"polkarare","symbol":"prare","name":"Polkarare"},{"id":"polkasocial-network","symbol":"psn","name":"Polkasocial Network"},{"id":"polkastarter","symbol":"pols","name":"Polkastarter"},{"id":"polkaswap","symbol":"pswap","name":"Polkaswap"},{"id":"polkawar","symbol":"pwar","name":"PolkaWar"},{"id":"polker","symbol":"pkr","name":"Polker"},{"id":"pollchain","symbol":"poll","name":"Pollchain"},{"id":"pollen","symbol":"pln","name":"Pollen"},{"id":"pollen-coin","symbol":"pcn","name":"Pollen Coin"},{"id":"pollux-coin","symbol":"pox","name":"Pollux Coin"},{"id":"polly","symbol":"polly","name":"Polly Finance"},{"id":"polly-defi-nest","symbol":"ndefi","name":"Polly DeFi Nest"},{"id":"polyalpha-finance","symbol":"alpha","name":"PolyAlpha Finance"},{"id":"polybeta-finance","symbol":"beta","name":"PolyBeta Finance"},{"id":"polybius","symbol":"plbt","name":"Polybius"},{"id":"polycat-finance","symbol":"fish","name":"Polycat Finance"},{"id":"polychain-monsters","symbol":"pmon","name":"Polychain Monsters"},{"id":"polychain-monsters-genesis","symbol":"pmlg","name":"Polychain Monsters Genesis"},{"id":"polycub","symbol":"polycub","name":"PolyCub"},{"id":"polydoge","symbol":"polydoge","name":"PolyDoge"},{"id":"polygamma","symbol":"gamma","name":"PolyGamma Finance"},{"id":"polygen","symbol":"pgen","name":"Polygen"},{"id":"polygod","symbol":"gull","name":"PolyGod"},{"id":"polygold","symbol":"polygold","name":"PolyGold"},{"id":"polygon-babydoge","symbol":"polybabydoge","name":"Polygon BabyDoge"},{"id":"polygonfarm-finance","symbol":"spade","name":"PolygonFarm Finance"},{"id":"polygon-hbd","symbol":"phbd","name":"Polygon HBD"},{"id":"polygon-hive","symbol":"phive","name":"Polygon Hive"},{"id":"polylastic","symbol":"polx","name":"Polylastic"},{"id":"polylauncher","symbol":"angel","name":"Polylauncher"},{"id":"polymath","symbol":"poly","name":"Polymath"},{"id":"poly-maximus","symbol":"poly","name":"POLY Maximus"},{"id":"polymesh","symbol":"polyx","name":"Polymesh"},{"id":"polypad","symbol":"polypad","name":"PolyPad"},{"id":"poly-peg-mdex","symbol":"hmdx","name":"Poly-Peg Mdex"},{"id":"polypup","symbol":"pup","name":"PolyPup"},{"id":"polyquity","symbol":"pyq","name":"PolyQuity"},{"id":"polyroll","symbol":"roll","name":"Polyroll"},{"id":"polyshark-finance","symbol":"shark","name":"PolyShark Finance"},{"id":"polyshield","symbol":"shi3ld","name":"PolyShield"},{"id":"polysports","symbol":"ps1","name":"POLYSPORTS"},{"id":"polyswarm","symbol":"nct","name":"PolySwarm"},{"id":"polytrade","symbol":"trade","name":"Polytrade"},{"id":"polywhale","symbol":"krill","name":"Polywhale"},{"id":"polywolf","symbol":"moon","name":"Polywolf"},{"id":"polyx","symbol":"pxt","name":"POLYX"},{"id":"polyyeld-token","symbol":"yeld","name":"PolyYeld"},{"id":"polyyield-token","symbol":"yield","name":"PolyYield"},{"id":"polyyork","symbol":"york","name":"PolyYork"},{"id":"polyzap","symbol":"pzap","name":"PolyZap"},{"id":"pomeranian-eth","symbol":"pom","name":"Pomeranian ETH"},{"id":"pomerium","symbol":"pmr","name":"Pomerium"},{"id":"pomerium-ecosystem","symbol":"pmg","name":"Pomerium Ecosystem Token"},{"id":"pom-governance","symbol":"pomg","name":"POM Governance"},{"id":"pomi","symbol":"pomi","name":"Pomi"},{"id":"pomo","symbol":"pomo","name":"Pomo"},{"id":"pompom","symbol":"pom","name":"PomPom"},{"id":"pong-heroes","symbol":"pong","name":"Pong Heroes"},{"id":"pontem-network","symbol":"pont","name":"Pontem Network"},{"id":"pontoon","symbol":"toon","name":"Pontoon"},{"id":"ponzicoin","symbol":"ponzi","name":"PonziCoin"},{"id":"pooch","symbol":"pooch","name":"Pooch"},{"id":"poochain","symbol":"poop","name":"Poochain"},{"id":"poocoin","symbol":"poocoin","name":"PooCoin"},{"id":"poodle","symbol":"poodl","name":"Poodl"},{"id":"poodlefi","symbol":"pfi","name":"PoodleFi"},{"id":"poo-doge","symbol":"poo doge","name":"Poo Doge"},{"id":"poofcash","symbol":"poof","name":"PoofCash"},{"id":"poof-token","symbol":"poof","name":"Poof Token"},{"id":"poogrow","symbol":"poogrow","name":"PooGrow"},{"id":"poollotto-finance","symbol":"plt","name":"Poollotto.finance"},{"id":"pool-party","symbol":"pp","name":"Pool Party"},{"id":"pooltogether","symbol":"pool","name":"PoolTogether"},{"id":"poolz-finance","symbol":"poolz","name":"Poolz Finance [OLD]"},{"id":"poolz-finance-2","symbol":"poolx","name":"Poolz Finance"},{"id":"poomoon","symbol":"poo","name":"POOMOON"},{"id":"poopcoin","symbol":"poop","name":"PoopCoin"},{"id":"poopsicle","symbol":"poop","name":"Poopsicle"},{"id":"poorpleb","symbol":"pp","name":"PoorPleb"},{"id":"pop","symbol":"pop!","name":"POP"},{"id":"pop-chest-token","symbol":"pop","name":"POP Network"},{"id":"popcoin","symbol":"pop","name":"Popcoin"},{"id":"popcorn","symbol":"pop","name":"Popcorn"},{"id":"popkon","symbol":"popk","name":"POPKON"},{"id":"populous","symbol":"ppt","name":"Populous"},{"id":"populous-xbrl-token","symbol":"pxt","name":"Populous XBRL"},{"id":"poriverse","symbol":"riken","name":"Poriverse"},{"id":"porkswap","symbol":"pswap","name":"PorkSwap"},{"id":"pornrocket","symbol":"pornrocket","name":"PornRocket"},{"id":"porta","symbol":"kian","name":"Porta"},{"id":"porte-token","symbol":"porte","name":"Porte"},{"id":"port-finance","symbol":"port","name":"Port Finance"},{"id":"portify","symbol":"pfy","name":"Portify"},{"id":"portion","symbol":"prt","name":"Portion"},{"id":"portugal-national-team-fan-token","symbol":"por","name":"Portugal National Team Fan Token"},{"id":"portuma","symbol":"por","name":"Portuma"},{"id":"pos-32","symbol":"pos32","name":"PoS-32"},{"id":"poseidon-2","symbol":"psdn","name":"Poseidon"},{"id":"poseidon-finance","symbol":"psdn","name":"Poseidon Finance"},{"id":"poseidon-ocean","symbol":"psdnocean","name":"Poseidon OCEAN"},{"id":"posh4d","symbol":"p4d","name":"PoSH4D"},{"id":"position-token","symbol":"posi","name":"Position"},{"id":"positron-token","symbol":"pot","name":"Positron"},{"id":"posschain","symbol":"poss","name":"Posschain"},{"id":"posthuman","symbol":"phmn","name":"POSTHUMAN"},{"id":"potato","symbol":"potato","name":"Potato"},{"id":"potcoin","symbol":"pot","name":"Potcoin"},{"id":"potent-coin","symbol":"ptt","name":"Potent Coin"},{"id":"potentiam","symbol":"ptm","name":"Potentiam"},{"id":"potfolio","symbol":"ptf","name":"Potfolio"},{"id":"poundtoken","symbol":"gbpt","name":"poundtoken"},{"id":"powabit","symbol":"powa","name":"Powabit"},{"id":"powerful","symbol":"pwfl","name":"Powerful"},{"id":"power-index-pool-token","symbol":"pipt","name":"Power Index Pool"},{"id":"power-ledger","symbol":"powr","name":"Power Ledger"},{"id":"power-nodes","symbol":"power","name":"Power Nodes"},{"id":"power-of-deep-ocean","symbol":"podo","name":"Power Of Deep Ocean"},{"id":"powerswap","symbol":"power","name":"PowerSwap"},{"id":"powertrade-fuel","symbol":"ptf","name":"PowerTrade Fuel"},{"id":"power-vault","symbol":"powv","name":"Power Vault"},{"id":"powpad","symbol":"pp","name":"Powpad"},{"id":"powswap","symbol":"pow","name":"Powswap"},{"id":"prcy-coin","symbol":"prcy","name":"PRivaCY Coin"},{"id":"predictcoin","symbol":"pred","name":"Predictcoin"},{"id":"prema","symbol":"prmx","name":"PREMA"},{"id":"premia","symbol":"premia","name":"Premia"},{"id":"premio","symbol":"premio","name":"Premio"},{"id":"pre-retogeum","symbol":"prtg","name":"Pre-Retogeum"},{"id":"presale-world","symbol":"presale","name":"Presale.World"},{"id":"presearch","symbol":"pre","name":"Presearch"},{"id":"pricetools","symbol":"ptools","name":"Pricetools"},{"id":"primal-2","symbol":"prm","name":"Primal Network"},{"id":"primal-b3099cd0-995a-4311-80d5-9c133153b38e","symbol":"primal","name":"PRIMAL"},{"id":"primas","symbol":"pst","name":"Primas"},{"id":"primate","symbol":"primate","name":"Primate"},{"id":"prime","symbol":"d2d","name":"Prime"},{"id":"prime-chain","symbol":"pmc","name":"Prime Chain"},{"id":"primecoin","symbol":"xpm","name":"Primecoin"},{"id":"prime-numbers","symbol":"prnt","name":"Prime Numbers Ecosystem"},{"id":"primex-finance","symbol":"pmx","name":"Primex Finance"},{"id":"primo-dao","symbol":"primo","name":"Primo DAO"},{"id":"princess-striker-gem","symbol":"prsg","name":"Princess Striker Gem"},{"id":"prism","symbol":"prism","name":"Prism"},{"id":"prism-protocol","symbol":"prism","name":"Prism Protocol"},{"id":"privacoin","symbol":"prvc","name":"PrivaCoin"},{"id":"privacyswap","symbol":"prv","name":"PrivacySwap"},{"id":"privapp-network","symbol":"bpriva","name":"Privapp Network"},{"id":"privateum","symbol":"pri","name":"Privateum Global"},{"id":"privatix","symbol":"prix","name":"Privatix"},{"id":"privcy","symbol":"priv","name":"PRiVCY"},{"id":"privilege","symbol":"prvg","name":"Privilege"},{"id":"prizm","symbol":"pzm","name":"Prizm"},{"id":"probably-nothing","symbol":"prbly","name":"Probably Nothing"},{"id":"probinex","symbol":"pbx","name":"Probinex"},{"id":"probit-exchange","symbol":"prob","name":"Probit"},{"id":"professional-fighters-league-fan-token","symbol":"pfl","name":"Professional Fighters League Fan Token"},{"id":"project202","symbol":"p202","name":"Project 202"},{"id":"project-babel","symbol":"pbt","name":"Project Babel"},{"id":"project-carecoin","symbol":"caresv2","name":"Project CareCoin"},{"id":"project-galaxy","symbol":"gal","name":"Galxe"},{"id":"project-inverse","symbol":"xiv","name":"Planet Inverse"},{"id":"projectmars","symbol":"mars","name":"ProjectMars"},{"id":"project-oasis","symbol":"oasis","name":"ProjectOasis"},{"id":"project-quantum","symbol":"qbit","name":"Project Quantum"},{"id":"project-with","symbol":"wiken","name":"Project WITH"},{"id":"projectx","symbol":"xil","name":"Xillion"},{"id":"projectx-d78dc2ae-9c8a-45ed-bd6a-22291d9d0812","symbol":"prox","name":"ProjectX"},{"id":"project-xeno","symbol":"gxe","name":"PROJECT XENO"},{"id":"prometeus","symbol":"prom","name":"Prom"},{"id":"prometheus-token","symbol":"pro","name":"Peak Finance Prometheus"},{"id":"promodio","symbol":"pmd","name":"Promodio"},{"id":"proof-of-apes","symbol":"poa","name":"Proof Of Apes"},{"id":"proof-of-degen","symbol":"bnb2.0","name":"Proof of Degen"},{"id":"proof-of-gorila","symbol":"pog","name":"Proof Of Gorila"},{"id":"proof-of-liquidity","symbol":"pol","name":"Proof Of Liquidity"},{"id":"proof-of-memes","symbol":"eth2.0","name":"Proof Of Memes - Ethereum"},{"id":"proof-of-memes-pomchain","symbol":"pom","name":"Proof Of Memes"},{"id":"propel-token","symbol":"pel","name":"Propel"},{"id":"property-blockchain-trade","symbol":"pbt","name":"PROPERTY BLOCKCHAIN TRADE"},{"id":"prophet","symbol":"pro","name":"Prophet"},{"id":"propland","symbol":"prop","name":"Propland"},{"id":"props","symbol":"props","name":"Props"},{"id":"propy","symbol":"pro","name":"Propy"},{"id":"prospectorcrane","symbol":"crane$","name":"ProspectorCrane"},{"id":"prosper","symbol":"pros","name":"Prosper"},{"id":"prospera-tax-credit","symbol":"ptc","name":"Prospera Tax Credit"},{"id":"prostarter-token","symbol":"prot","name":"ProStarter"},{"id":"protectors-of-the-realm","symbol":"wer1","name":"Protectors of the Realm"},{"id":"proteo-defi","symbol":"proteo","name":"Proteo DeFi"},{"id":"protocol-zero","symbol":"zro","name":"Protocol Zero"},{"id":"protocon","symbol":"pen","name":"Protocon"},{"id":"protofi","symbol":"proto","name":"Protofi"},{"id":"proto-gyro-dollar","symbol":"p-gyd","name":"Proto Gyro Dollar"},{"id":"proton","symbol":"xpr","name":"Proton"},{"id":"proton-coin","symbol":"pro","name":"Proton Coin"},{"id":"proton-loan","symbol":"loan","name":"Proton Loan"},{"id":"proton-protocol","symbol":"proton","name":"Proton Protocol"},{"id":"province","symbol":"maple","name":"Province"},{"id":"proxima","symbol":"prox","name":"Proxima"},{"id":"proximax","symbol":"xpx","name":"ProximaX"},{"id":"proxy","symbol":"prxy","name":"Proxy"},{"id":"proxy-swap","symbol":"proxy","name":"Proxy Swap"},{"id":"pruf-protocol","symbol":"pruf","name":"PRüF Protocol"},{"id":"pryz","symbol":"pryz","name":"Pryz"},{"id":"pstake-finance","symbol":"pstake","name":"pSTAKE Finance"},{"id":"pstake-staked-bnb","symbol":"stkbnb","name":"pSTAKE Staked BNB"},{"id":"psyche","symbol":"usd1","name":"Psyche"},{"id":"psycho-doge","symbol":"psychodoge","name":"Psycho Doge"},{"id":"psyoptions","symbol":"psy","name":"PsyFi"},{"id":"pterosaur-finance","symbol":"pter","name":"Pterosaur Finance"},{"id":"ptokens-btc","symbol":"pbtc","name":"pTokens BTC [OLD]"},{"id":"ptokens-btc-2","symbol":"pbtc","name":"pTokens BTC"},{"id":"ptokens-ore","symbol":"ore","name":"ORE Network"},{"id":"ptx","symbol":"ptx","name":"ProtocolX"},{"id":"pube-finance","symbol":"pube","name":"Pube Finance"},{"id":"pub-finance","symbol":"pint","name":"Pub Finance"},{"id":"publc","symbol":"publx","name":"PUBLC"},{"id":"public-index-network","symbol":"pin","name":"Public Index Network"},{"id":"public-mint","symbol":"mint","name":"Public Mint"},{"id":"publish","symbol":"news","name":"PUBLISH"},{"id":"pudgy-cat","symbol":"$pudgy","name":"Pudgy Cat"},{"id":"pudgy-vault-nftx","symbol":"pudgy","name":"PUDGY Vault (NFTX)"},{"id":"puff","symbol":"puff","name":"PUFF"},{"id":"puglife","symbol":"pugl","name":"PugLife"},{"id":"puli-inu","symbol":"puli","name":"Puli"},{"id":"pulsar-coin","symbol":"plsr","name":"Pulsar Coin"},{"id":"pulsebitcoin","symbol":"plsb","name":"PulseBitcoin"},{"id":"pulsedoge","symbol":"pulsedoge","name":"PulseDoge"},{"id":"pulsedogecoin","symbol":"plsd","name":"PulseDogecoin"},{"id":"pulsefolio","symbol":"pulse","name":"PulseFolio"},{"id":"pulsepad","symbol":"plspad","name":"PulsePad"},{"id":"pulse-token","symbol":"pulse","name":"PulseMarkets"},{"id":"pumapay","symbol":"pma","name":"PumaPay"},{"id":"puml-better-health","symbol":"puml","name":"PUML Better Health"},{"id":"pumlx","symbol":"pumlx","name":"PUMLx"},{"id":"pumpopoly","symbol":"pumpopoly","name":"Pumpopoly"},{"id":"punchy-token","symbol":"punch","name":"Punchy Token"},{"id":"pundi-x","symbol":"npxs","name":"Pundi X [OLD]"},{"id":"pundi-x-2","symbol":"pundix","name":"Pundi X"},{"id":"pundi-x-nem","symbol":"npxsxem","name":"Pundi X NEM"},{"id":"pundi-x-purse","symbol":"purse","name":"Pundi X PURSE"},{"id":"punk-panda-messenger","symbol":"ppm","name":"Punk Panda Messenger"},{"id":"punks-comic-pow","symbol":"pow","name":"POW"},{"id":"punk-shiba","symbol":"punks","name":"Punk Shiba"},{"id":"punk-vault-nftx","symbol":"punk","name":"Punk Vault (NFTX)"},{"id":"pupazzi-punk-brise-of-sun","symbol":"pps","name":"Pupazzi Punk Brise Of Sun"},{"id":"pup-doge","symbol":"pupdoge","name":"Pup Doge"},{"id":"puppets-arts","symbol":"puppets","name":"Puppets Arts"},{"id":"purchasa","symbol":"pca","name":"Purchasa"},{"id":"purefi","symbol":"ufi","name":"PureFi"},{"id":"puregold-token","symbol":"pgpay","name":"PGPay"},{"id":"puriever","symbol":"pure","name":"Puriever"},{"id":"purpose","symbol":"prps","name":"Purpose"},{"id":"pusd","symbol":"pusd","name":"PUSD_Polyquity"},{"id":"pusd-2","symbol":"pusd","name":"PUSd"},{"id":"pussy-financial","symbol":"pussy","name":"Pussy Financial"},{"id":"pusuke-inu","symbol":"pusuke","name":"Pusuke Inu"},{"id":"putincoin","symbol":"put","name":"PUTinCoin"},{"id":"puzzle-swap","symbol":"puzzle","name":"Puzzle Swap"},{"id":"pwrcash","symbol":"pwrc","name":"PWRCASH"},{"id":"pylon-eco-token","symbol":"petn","name":"Pylon Eco Token"},{"id":"pyram-token","symbol":"pyram","name":"Pyram"},{"id":"pyrexcoin","symbol":"gpyx","name":"GoldenPyrex"},{"id":"pyrk","symbol":"pyrk","name":"Pyrk"},{"id":"pyromatic","symbol":"pyro","name":"PYROmatic"},{"id":"pyrrho-defi","symbol":"pyo","name":"Pyrrho"},{"id":"q2","symbol":"q2","name":"Pocketful of Quarters"},{"id":"qanplatform","symbol":"qanx","name":"QANplatform"},{"id":"qash","symbol":"qash","name":"QASH"},{"id":"qatar","symbol":"qatar","name":"QAtar"},{"id":"qatargrow","symbol":"qatargrow","name":"QatarGrow"},{"id":"qatar-inu","symbol":"qatar","name":"Qatar Inu"},{"id":"qatar-inu-token","symbol":"qatar","name":"Qatar Inu Token"},{"id":"qawalla","symbol":"qwla","name":"Qawalla"},{"id":"qbao","symbol":"qbt","name":"Qbao"},{"id":"qchain-qdt","symbol":"qdt","name":"QChain QDT"},{"id":"qian-second-generation-dollar","symbol":"qsd","name":"QIAN Second Generation Dollar"},{"id":"qi-dao","symbol":"qi","name":"Qi Dao"},{"id":"qie","symbol":"qie","name":"QI Blockchain"},{"id":"qiswap","symbol":"qi","name":"QiSwap"},{"id":"qitchain-network","symbol":"qtc","name":"Qitcoin"},{"id":"qitmeer-network","symbol":"meer","name":"Qitmeer Network"},{"id":"qiusd","symbol":"qiusd","name":"QiUSD"},{"id":"qlindo","symbol":"qlindo","name":"QLINDO"},{"id":"qlink","symbol":"qlc","name":"Kepple"},{"id":"qmall","symbol":"qmall","name":"Qmall"},{"id":"qmcoin","symbol":"qmc","name":"QMCoin"},{"id":"qoda-finance","symbol":"qodex","name":"Qoda Finance"},{"id":"qoiniq","symbol":"qiq","name":"QoinIQ"},{"id":"qowatt","symbol":"qwt","name":"QoWatt"},{"id":"qqq-token","symbol":"qqq","name":"Poseidon Network"},{"id":"qqq-tokenized-stock-defichain","symbol":"dqqq","name":"Invesco QQQ Trust Defichain"},{"id":"qredit","symbol":"xqr","name":"Qredit"},{"id":"qredo","symbol":"qrdo","name":"Qredo"},{"id":"qrkita-token","symbol":"qrt","name":"Qrkita"},{"id":"qrolli","symbol":"qr","name":"Qrolli"},{"id":"qroni","symbol":"qni","name":"Qroni"},{"id":"qtoken","symbol":"qto","name":"Qtoken"},{"id":"qtum","symbol":"qtum","name":"Qtum"},{"id":"quack","symbol":"quack","name":"QUACK"},{"id":"quadency","symbol":"quad","name":"Quadency"},{"id":"quadrans","symbol":"qdt","name":"Quadrans"},{"id":"quadrant-protocol","symbol":"equad","name":"Quadrant Protocol"},{"id":"quai-network","symbol":"quai","name":"Quai Network"},{"id":"quantfury","symbol":"qtf","name":"Quantfury"},{"id":"quantic","symbol":"quantic","name":"Quantic"},{"id":"quantland","symbol":"qlt","name":"Quantland"},{"id":"quant-network","symbol":"qnt","name":"Quant"},{"id":"quantstamp","symbol":"qsp","name":"Quantstamp"},{"id":"quantum-assets","symbol":"qa","name":"Quantum Assets"},{"id":"quantum-resistant-ledger","symbol":"qrl","name":"Quantum Resistant Ledger"},{"id":"quantum-tech","symbol":"qua","name":"Quantum Tech"},{"id":"quarashi","symbol":"qua","name":"Quarashi"},{"id":"quark","symbol":"qrk","name":"Quark"},{"id":"quark-chain","symbol":"qkc","name":"QuarkChain"},{"id":"quartz","symbol":"qtz","name":"Quartz"},{"id":"quasacoin","symbol":"qua","name":"Quasacoin"},{"id":"quasar","symbol":"qsr","name":"Quasar"},{"id":"qube-2","symbol":"qube","name":"Qube"},{"id":"qubit","symbol":"qbt","name":"Qubit"},{"id":"quebecoin","symbol":"qbc","name":"Quebecoin"},{"id":"quick","symbol":"quick","name":"Quickswap [OLD]"},{"id":"quickchart","symbol":"quickchart","name":"QuickChart"},{"id":"quick-intel","symbol":"quicki","name":"Quick Intel"},{"id":"quicksilver","symbol":"qck","name":"Quicksilver"},{"id":"quickswap","symbol":"quick","name":"Quickswap"},{"id":"quickx-protocol","symbol":"qcx","name":"QuickX Protocol"},{"id":"quidax","symbol":"qdx","name":"Quidax"},{"id":"quidd","symbol":"quidd","name":"Quidd"},{"id":"quid-ika","symbol":"quid","name":"Quid Ika"},{"id":"quincoin","symbol":"qin","name":"QUINCOIN"},{"id":"quint","symbol":"quint","name":"Quint"},{"id":"quipuswap-governance-token","symbol":"quipu","name":"QuipuSwap Governance"},{"id":"quiverx","symbol":"qrx","name":"QuiverX"},{"id":"quizdrop","symbol":"qdrop","name":"QuizDrop"},{"id":"quiztok","symbol":"qtcon","name":"Quiztok"},{"id":"quo","symbol":"quo","name":"Quoll Finance"},{"id":"quontral","symbol":"quon","name":"Quontral"},{"id":"quorum","symbol":"rum","name":"Quorum"},{"id":"r","symbol":"r","name":"R"},{"id":"r34p","symbol":"r34p","name":"R34P"},{"id":"rabbit2023","symbol":"rabbit","name":"Rabbit2023"},{"id":"rabbit-finance","symbol":"rabbit","name":"Rabbit Finance"},{"id":"rabbit-halloween","symbol":"rh31","name":"Rabbit Halloween"},{"id":"rabbitking","symbol":"rb","name":"RabbitKing"},{"id":"rabbitswap","symbol":"rabbit","name":"RabbitSwap"},{"id":"rabbit-wallet","symbol":"rab","name":"Rabbit Wallet"},{"id":"rabity-finance","symbol":"rbf","name":"Rabity Finance"},{"id":"rabona","symbol":"ra","name":"Rabona"},{"id":"rac","symbol":"rac","name":"RAC"},{"id":"racefi","symbol":"racefi","name":"RaceFi"},{"id":"race-kingdom","symbol":"atoz","name":"Race Kingdom"},{"id":"racex","symbol":"racex","name":"RaceX"},{"id":"racing-club-fan-token","symbol":"racing","name":"Racing Club Fan Token"},{"id":"rad","symbol":"rad","name":"RAD"},{"id":"radar","symbol":"radar","name":"Radar"},{"id":"radial-finance","symbol":"rdl","name":"Radial Finance"},{"id":"radiant","symbol":"rxd","name":"Radiant"},{"id":"radiant-capital","symbol":"rdnt","name":"Radiant Capital"},{"id":"radical-chess","symbol":"chess","name":"Radical Chess"},{"id":"radicle","symbol":"rad","name":"Radicle"},{"id":"radio-caca","symbol":"raca","name":"Radio Caca"},{"id":"radioreum","symbol":"theradio","name":"Radioreum"},{"id":"radioshack","symbol":"radio","name":"RadioShack"},{"id":"radium","symbol":"val","name":"Validity"},{"id":"radix","symbol":"xrd","name":"Radix"},{"id":"rae-token","symbol":"rae","name":"Receive Access Ecosystem"},{"id":"rafflet","symbol":"raf","name":"Rafflet"},{"id":"rage-fan","symbol":"rage","name":"Rage.Fan"},{"id":"raggiecoin","symbol":"rag","name":"RaggieCoin"},{"id":"rai","symbol":"rai","name":"Rai Reflex Index"},{"id":"raiden-network","symbol":"rdn","name":"Raiden Network"},{"id":"raider-aurum","symbol":"aurum","name":"Raider Aurum"},{"id":"raider-inu","symbol":"raid","name":"Raider Inu"},{"id":"raid-token","symbol":"raid","name":"Raid"},{"id":"rai-finance","symbol":"sofi","name":"RAI Finance"},{"id":"railgun","symbol":"rail","name":"Railgun"},{"id":"rainbowtoken","symbol":"rainbowtoken","name":"RainbowToken"},{"id":"rainbow-token","symbol":"rnbw","name":"HaloDAO"},{"id":"rainbow-token-2","symbol":"rbw","name":"Rainbow Token"},{"id":"rainicorn","symbol":"$raini","name":"Raini"},{"id":"rainmaker-games","symbol":"rain","name":"Rainmaker Games"},{"id":"rai-yvault","symbol":"yvrai","name":"RAI yVault"},{"id":"rake-finance","symbol":"rak","name":"Rake Finance"},{"id":"rake-in","symbol":"rake","name":"Rake.in"},{"id":"rally-2","symbol":"rly","name":"Rally"},{"id":"rally-solana","symbol":"srly","name":"Rally (Solana)"},{"id":"ramestta","symbol":"rama","name":"Ramestta"},{"id":"ramifi","symbol":"ram","name":"Ramifi Protocol"},{"id":"ramp","symbol":"ramp","name":"RAMP [OLD]"},{"id":"ramses-exchange","symbol":"ram","name":"Ramses Exchange"},{"id":"ranbased","symbol":"ranb","name":"RANBASED"},{"id":"random","symbol":"rndm","name":"Random"},{"id":"rangers-fan-token","symbol":"rft","name":"Rangers Fan Token"},{"id":"rangers-protocol-gas","symbol":"rpg","name":"Rangers Protocol"},{"id":"rankerdao","symbol":"ranker","name":"RankerDao"},{"id":"ran-x-crypto","symbol":"rxc","name":"Ran x Crypto"},{"id":"rapids","symbol":"rpd","name":"Rapids"},{"id":"rapidz","symbol":"rpzx","name":"Rapidz"},{"id":"raptoreum","symbol":"rtm","name":"Raptoreum"},{"id":"raptor-finance-2","symbol":"rptr","name":"Raptor Finance"},{"id":"rare-ball-shares","symbol":"rbp","name":"Rare Ball Potion"},{"id":"rare-fnd","symbol":"fnd","name":"Rare FND"},{"id":"raresama","symbol":"poop","name":"Raresama"},{"id":"rarible","symbol":"rari","name":"Rarible"},{"id":"rari-governance-token","symbol":"rgt","name":"Rari Governance"},{"id":"rasko","symbol":"rasko","name":"rASKO"},{"id":"rasta-finance","symbol":"rasta","name":"Rasta Finance"},{"id":"ratecoin","symbol":"xra","name":"Ratecoin"},{"id":"ratio-finance","symbol":"ratio","name":"Ratio Protocol"},{"id":"ratscoin","symbol":"rats","name":"Ratscoin"},{"id":"ratscoin-team-dao","symbol":"ratsdao","name":"Ratscoin Team Dao"},{"id":"ravelin-finance","symbol":"rav","name":"Ravelin Finance"},{"id":"rave-names","symbol":"rave","name":"Rave Names"},{"id":"ravencoin","symbol":"rvn","name":"Ravencoin"},{"id":"ravencoin-classic","symbol":"rvc","name":"Ravencoin Classic"},{"id":"raven-dark","symbol":"xrd","name":"Raven Dark"},{"id":"ravendex","symbol":"rave","name":"Ravendex"},{"id":"raven-protocol","symbol":"raven","name":"Raven Protocol"},{"id":"raydium","symbol":"ray","name":"Raydium"},{"id":"ray-network","symbol":"xray","name":"Ray Network"},{"id":"rays","symbol":"rays","name":"RAYS"},{"id":"raze-network","symbol":"raze","name":"Raze Network"},{"id":"razor-network","symbol":"razor","name":"Razor Network"},{"id":"rb-finance","symbol":"rb","name":"RB Finance"},{"id":"rb-share","symbol":"rbx","name":"RB Share"},{"id":"rbxsamurai","symbol":"rbxs","name":"RBXSamurai"},{"id":"rbx-token","symbol":"rbx","name":"RBX"},{"id":"rc-celta-de-vigo-fan-token","symbol":"cft","name":"RC Celta de Vigo Fan Token"},{"id":"rcd-espanyol-fan-token","symbol":"enft","name":"RCD Espanyol Fan Token"},{"id":"rchain","symbol":"rev","name":"RChain"},{"id":"reach-dao","symbol":"$read","name":"Reach DAO"},{"id":"readfi","symbol":"rdf","name":"ReadFi"},{"id":"real-estate-token","symbol":"r3t","name":"Real Estate Token"},{"id":"realfevr","symbol":"fevr","name":"RealFevr"},{"id":"realfinance-network","symbol":"refi","name":"Realfinance Network"},{"id":"realio-network","symbol":"rio","name":"Realio"},{"id":"realis-network","symbol":"lis","name":"Realis Network"},{"id":"realital-metaverse","symbol":"reta","name":"Realital Metaverse"},{"id":"reallink","symbol":"real","name":"RealLink"},{"id":"realm","symbol":"realm","name":"Realm"},{"id":"realmoneyworld","symbol":"rmw","name":"RealMoneyWorld"},{"id":"realms-of-ethernity","symbol":"reth","name":"Realms of Ethernity"},{"id":"real-realm","symbol":"real","name":"Real Realm"},{"id":"real-sociedad-fan-token","symbol":"rso","name":"Real Sociedad Fan Token"},{"id":"realtract","symbol":"ret","name":"RealTract"},{"id":"real-usd","symbol":"usdr","name":"Real USD"},{"id":"realy-metaverse","symbol":"real","name":"Realy Metaverse"},{"id":"reapchain","symbol":"reap","name":"ReapChain"},{"id":"reaper-token","symbol":"reaper","name":"Reaper"},{"id":"rebel-bots","symbol":"rbls","name":"Rebel Bots"},{"id":"rebellion-dao","symbol":"reb","name":"Rebellion DAO"},{"id":"rebellion-protocol","symbol":"rebl","name":"Rebellion Protocol"},{"id":"rebeltradertoken","symbol":"rtt","name":"RebelTrader"},{"id":"reborn","symbol":"rb","name":"Hey Reborn [OLD]"},{"id":"rebus","symbol":"rebus","name":"Rebus"},{"id":"recast1","symbol":"r1","name":"Recast1"},{"id":"recession-coin","symbol":"econ","name":"Recession Coin"},{"id":"recharge","symbol":"rcg","name":"Recharge"},{"id":"recoverydao","symbol":"rec","name":"RecoveryDAO"},{"id":"recovery-right-token","symbol":"rrt","name":"Recovery Right"},{"id":"recycle-x","symbol":"rcx","name":"Recycle-X"},{"id":"recycling-cyc","symbol":"cyc","name":"Recycling CYC"},{"id":"red","symbol":"red","name":"Red"},{"id":"redacted","symbol":"btrfly","name":"Redacted"},{"id":"redancoin","symbol":"redan","name":"REDANCOIN"},{"id":"reddcoin","symbol":"rdd","name":"Reddcoin"},{"id":"red-falcon","symbol":"rfn","name":"Red Falcon"},{"id":"redfeg","symbol":"redfeg","name":"RedFeg"},{"id":"redfireants","symbol":"rants","name":"redFireAnts"},{"id":"redfox-labs-2","symbol":"rfox","name":"RFOX"},{"id":"redi","symbol":"redi","name":"REDi"},{"id":"redlight-chain","symbol":"redlc","name":"Redlight Chain"},{"id":"redmars","symbol":"rmars","name":"REDMARS"},{"id":"redpanda-earth-v2","symbol":"redpanda","name":"RedPanda Earth V2"},{"id":"redpill-2","symbol":"rpill","name":"RedPill"},{"id":"red-pulse","symbol":"phb","name":"Phoenix Global [OLD]"},{"id":"red-rabbit","symbol":"rr","name":"Red Rabbit"},{"id":"red-token","symbol":"red","name":"RED TOKEN"},{"id":"redux","symbol":"redux","name":"ReduX"},{"id":"reef","symbol":"reef","name":"Reef"},{"id":"reelfi","symbol":"reelfi","name":"ReelFi"},{"id":"reel-token","symbol":"reelt","name":"Reel Token"},{"id":"refereum","symbol":"rfr","name":"Refereum"},{"id":"ref-finance","symbol":"ref","name":"Ref Finance"},{"id":"refinable","symbol":"fine","name":"Refinable"},{"id":"reflect-finance","symbol":"rfi","name":"reflect.finance"},{"id":"reflecto","symbol":"rto","name":"Reflecto"},{"id":"reflecto-usd","symbol":"rusd","name":"Reflecto USD"},{"id":"reflex","symbol":"rfx","name":"Reflex"},{"id":"reflexer-ungovernance-token","symbol":"flx","name":"Reflexer Ungovernance"},{"id":"reforestation-mahogany","symbol":"rmog","name":"Reforestation Mahogany"},{"id":"reftoken","symbol":"ref","name":"Ref"},{"id":"regen","symbol":"regen","name":"Regen"},{"id":"regularpresale","symbol":"regu","name":"RegularPresale"},{"id":"rei-network","symbol":"rei","name":"REI Network"},{"id":"rejuve-ai","symbol":"rjv","name":"Rejuve.AI"},{"id":"rektskulls","symbol":"rekt","name":"RektSkulls"},{"id":"relaxable","symbol":"relax","name":"Relaxable"},{"id":"relay-token","symbol":"relay","name":"Relay Chain"},{"id":"release-ico-project","symbol":"rel","name":"RELEASE"},{"id":"relevant","symbol":"rel","name":"Relevant"},{"id":"relic","symbol":"relic","name":"Relic"},{"id":"relictumpro-genesis-token","symbol":"gtn","name":"RelictumPro Genesis Token"},{"id":"relite-finance","symbol":"reli","name":"Relite Finance"},{"id":"rematicegc","symbol":"rmtx","name":"RematicEGC"},{"id":"remi","symbol":"remi","name":"REMI"},{"id":"remme","symbol":"rem","name":"Remme"},{"id":"rena-finance","symbol":"rena","name":"RENA Finance"},{"id":"renbtc","symbol":"renbtc","name":"renBTC"},{"id":"render-token","symbol":"rndr","name":"Render"},{"id":"rendoge","symbol":"rendoge","name":"renDOGE"},{"id":"renec","symbol":"renec","name":"RENEC"},{"id":"renewable-energy","symbol":"ret","name":"Renewable Energy"},{"id":"renq-finance","symbol":"renq","name":"Renq Finance"},{"id":"rens","symbol":"rens","name":"Rens"},{"id":"rentberry","symbol":"berry","name":"Rentberry"},{"id":"rentible","symbol":"rnb","name":"Rentible"},{"id":"reptilian","symbol":"rptc","name":"Reptilian"},{"id":"republic-credits","symbol":"rpc","name":"Republic Credits"},{"id":"republic-protocol","symbol":"ren","name":"REN"},{"id":"request-network","symbol":"req","name":"Request"},{"id":"researchcoin","symbol":"rsc","name":"ResearchCoin"},{"id":"reserve","symbol":"rsv","name":"Reserve"},{"id":"reserveblock","symbol":"rbx","name":"ReserveBlock"},{"id":"reserve-rights-token","symbol":"rsr","name":"Reserve Rights"},{"id":"resource-protocol","symbol":"source","name":"ReSource Protocol"},{"id":"restore-truth-token","symbol":"rtt","name":"Restore Truth"},{"id":"retawars-goldrose-token","symbol":"grt","name":"Retawars GoldRose Token"},{"id":"reth","symbol":"reth","name":"StaFi Staked ETH"},{"id":"reth2","symbol":"reth2","name":"rETH2"},{"id":"retrocade","symbol":"rc","name":"RetroCade"},{"id":"retsuko","symbol":"suko","name":"Retsuko"},{"id":"reunit-wallet","symbol":"reuni","name":"Reunit Wallet"},{"id":"rev3al","symbol":"rev3l","name":"REV3AL"},{"id":"revain","symbol":"rev","name":"Revain"},{"id":"revault-network","symbol":"reva","name":"Revault Network"},{"id":"revenant","symbol":"gamefi","name":"Revenant"},{"id":"revenue-coin","symbol":"rvc","name":"Revenue Coin"},{"id":"revest-finance","symbol":"rvst","name":"Revest Finance"},{"id":"revivalx","symbol":"rvlx","name":"RevivalX"},{"id":"revoai","symbol":"revoai","name":"revoAI"},{"id":"revoland","symbol":"revo","name":"Revoland"},{"id":"revolotto","symbol":"rvl","name":"Revolotto"},{"id":"revolt-2-earn","symbol":"rvlt","name":"Revolt 2 Earn"},{"id":"revolutiongames","symbol":"rvlng","name":"RevolutionGames"},{"id":"revolution-populi","symbol":"rvp","name":"Revolution Populi"},{"id":"revoluzion","symbol":"rvz","name":"Revoluzion"},{"id":"revolve-games","symbol":"rpg","name":"Revolve Games"},{"id":"revomon","symbol":"revo","name":"Revomon"},{"id":"revuto","symbol":"revu","name":"Revuto"},{"id":"revv","symbol":"revv","name":"REVV"},{"id":"rewardeum","symbol":"reum","name":"Rewardeum"},{"id":"rex-token","symbol":"xrx","name":"Rex"},{"id":"rhegic2","symbol":"rhegic2","name":"rHEGIC2"},{"id":"rhinofi","symbol":"dvf","name":"Rhino.fi"},{"id":"rhinos-finance","symbol":"rho","name":"Rhinos Finance"},{"id":"rho-token","symbol":"rho","name":"Rho"},{"id":"rhythm","symbol":"rhythm","name":"Rhythm"},{"id":"ribbon-finance","symbol":"rbn","name":"Ribbon Finance"},{"id":"rice","symbol":"rice","name":"Rice"},{"id":"riceswap","symbol":"rice","name":"RiceSwap"},{"id":"rice-wallet","symbol":"rice","name":"Rice Wallet"},{"id":"rich","symbol":"rch","name":"Rich"},{"id":"richcity","symbol":"rich","name":"RichCity"},{"id":"richierich-coin","symbol":"rich","name":"RichieRich Coin"},{"id":"richochet","symbol":"ric","name":"Ricochet"},{"id":"richquack","symbol":"quack","name":"Rich Quack"},{"id":"rich-santa","symbol":"santa","name":"Rich Santa"},{"id":"ricnatum","symbol":"rcnt","name":"Ricnatum"},{"id":"ride_finance","symbol":"rides","name":"Rides Finance"},{"id":"ridge","symbol":"ridge","name":"Ridge"},{"id":"ridotto","symbol":"rdt","name":"Ridotto"},{"id":"riecoin","symbol":"ric","name":"Riecoin"},{"id":"rifi-united","symbol":"ru","name":"RIFI United"},{"id":"rif-token","symbol":"rif","name":"RSK Infrastructure Framework"},{"id":"rigel-protocol","symbol":"rgp","name":"Rigel Protocol"},{"id":"rigoblock","symbol":"grg","name":"RigoBlock"},{"id":"rikkei-finance","symbol":"rifi","name":"Rikkei Finance"},{"id":"rillafi","symbol":"rilla","name":"RillaFi"},{"id":"rimaunangis","symbol":"rxt","name":"RIMAUNANGIS"},{"id":"rin-finance-coin","symbol":"rifico","name":"Rin Finance Coin"},{"id":"ring-financial","symbol":"ring","name":"RING Financial"},{"id":"rinia-inu","symbol":"rinia","name":"Rinia Inu"},{"id":"rio-defi","symbol":"rfuel","name":"RioDeFi"},{"id":"riot-racers","symbol":"riot","name":"Riot Racers"},{"id":"ripae","symbol":"pae","name":"Ripae"},{"id":"ripae-avax","symbol":"pavax","name":"Ripae AVAX"},{"id":"ripae-pbnb","symbol":"pbnb","name":"Ripae pBNB"},{"id":"ripae-peth","symbol":"peth","name":"Ripae pETH"},{"id":"ripae-pmatic","symbol":"pmatic","name":"Ripae pMATIC"},{"id":"ripae-seth","symbol":"seth","name":"Ripae sETH"},{"id":"ripio-credit-network","symbol":"rcn","name":"Ripio Credit Network"},{"id":"ripple","symbol":"xrp","name":"XRP"},{"id":"rise","symbol":"rise","name":"Rise"},{"id":"risecoin","symbol":"rsc","name":"Risecoin"},{"id":"rise-of-elves","symbol":"roe","name":"Rise of Elves"},{"id":"rise-of-nebula","symbol":"ron","name":"Rise Of Nebula"},{"id":"risu","symbol":"risu","name":"Risu"},{"id":"risuchainswap","symbol":"rcs","name":"RisuChainSwap"},{"id":"ritestream","symbol":"rite","name":"ritestream"},{"id":"rito","symbol":"rito","name":"Rito"},{"id":"ri-token","symbol":"ri","name":"Xiotri RI"},{"id":"riverboat","symbol":"rib","name":"RiverBoat"},{"id":"rizespor-token","symbol":"rize","name":"Rizespor Token"},{"id":"rizon","symbol":"atolo","name":"RIZON"},{"id":"rloop","symbol":"rloop","name":"rLoop"},{"id":"rmrk","symbol":"rmrk","name":"RMRK"},{"id":"road","symbol":"road","name":"ROAD"},{"id":"roaland-core","symbol":"roa","name":"ROA CORE"},{"id":"roar-token","symbol":"roar","name":"SOL Tigers Roar"},{"id":"robodoge-coin","symbol":"robodoge","name":"RoboDoge Coin"},{"id":"robofi-token","symbol":"vics","name":"RoboFi"},{"id":"robo-inu-finance","symbol":"rbif","name":"Robo Inu Finance"},{"id":"robonomics-network","symbol":"xrt","name":"Robonomics Network"},{"id":"robonomics-web-services","symbol":"rws","name":"Robonomics Web Services"},{"id":"roboots","symbol":"rbo","name":"Roboots"},{"id":"robot","symbol":"robot","name":"Robot"},{"id":"robo-token","symbol":"robo","name":"Robo"},{"id":"robust-token","symbol":"rbt","name":"Robust"},{"id":"rocketbusd","symbol":"rocketbusd","name":"RocketBUSD"},{"id":"rocketcoin-2","symbol":"rocket","name":"RocketCoin"},{"id":"rocketfi","symbol":"rocketfi","name":"RocketFi"},{"id":"rocket-global-coin","symbol":"rckc","name":"Rocket Global Coin"},{"id":"rocket-pool","symbol":"rpl","name":"Rocket Pool"},{"id":"rocket-pool-eth","symbol":"reth","name":"Rocket Pool ETH"},{"id":"rocket-raccoon-v2","symbol":"roc","name":"Rocket Raccoon V2"},{"id":"rocketverse","symbol":"rkv","name":"RocketVerse"},{"id":"rocketx","symbol":"rvf","name":"RocketX exchange"},{"id":"rocki","symbol":"rocki","name":"Rocki"},{"id":"rock-n-rain-coin","symbol":"rnrc","name":"Rock N Rain Coin"},{"id":"rocky-inu","symbol":"rocky","name":"Rocky Inu"},{"id":"roco-finance","symbol":"roco","name":"Roco Finance"},{"id":"rodeo-coin","symbol":"rodeo","name":"Rodeo Coin"},{"id":"roge","symbol":"roge","name":"Rogue Doge"},{"id":"rogin-ai","symbol":"rog","name":"ROGin AI"},{"id":"rogue-coin","symbol":"rogue","name":"Rogue Coin"},{"id":"roima-inc","symbol":"rmai","name":"ROIMA INC"},{"id":"roko-network","symbol":"roko","name":"Roko Network"},{"id":"rollbit-coin","symbol":"rlb","name":"Rollbit Coin"},{"id":"rollium","symbol":"rlm","name":"MarbleVerse"},{"id":"rome","symbol":"rome","name":"Rome"},{"id":"rond","symbol":"rond","name":"ROND"},{"id":"ronin","symbol":"ron","name":"Ronin"},{"id":"ronpaulcoin","symbol":"rpc","name":"RonPaulCoin"},{"id":"roobee","symbol":"roobee","name":"Roobee"},{"id":"rook","symbol":"rook","name":"Rook"},{"id":"rooster-battle","symbol":"rice","name":"Rooster Battle"},{"id":"root","symbol":"root","name":"Root"},{"id":"rootstock","symbol":"rbtc","name":"Rootstock RSK"},{"id":"rope","symbol":"$rope","name":"Rope"},{"id":"rope-token","symbol":"rope","name":"Rope Token"},{"id":"ror-universe","symbol":"ror","name":"ROR Universe"},{"id":"rose","symbol":"rose","name":"Rose"},{"id":"rose-finance","symbol":"rose","name":"Rose Finance"},{"id":"roseon","symbol":"rosx","name":"Roseon"},{"id":"roseon-finance","symbol":"rosn","name":"Roseon Finance [OLD]"},{"id":"rotharium","symbol":"rth","name":"Rotharium"},{"id":"rottoken","symbol":"rotto","name":"Rottoken"},{"id":"round-x","symbol":"rndx","name":"Round X"},{"id":"roush-fenway-racing-fan-token","symbol":"roush","name":"Roush Fenway Racing Fan Token"},{"id":"route","symbol":"route","name":"Router Protocol"},{"id":"rover-inu","symbol":"rover","name":"Rover Inu"},{"id":"rovi-protocol","symbol":"rovi","name":"ROVI Protocol"},{"id":"rowan-coin","symbol":"rwn","name":"Rowan Coin"},{"id":"roxe","symbol":"roc","name":"Roxe"},{"id":"royale","symbol":"roya","name":"Royale"},{"id":"royal-gold","symbol":"rgold","name":"Royal Gold"},{"id":"royal-smart-future-token","symbol":"rsft","name":"ROYAL SMART FUTURE TOKEN"},{"id":"rps-league","symbol":"rps","name":"Rps League"},{"id":"rss3","symbol":"rss3","name":"RSS3"},{"id":"rubic","symbol":"rbc","name":"Rubic"},{"id":"rubidium","symbol":"rbd","name":"Rubidium"},{"id":"rubix","symbol":"rbt","name":"Rubix"},{"id":"rublix","symbol":"rblx","name":"Rublix"},{"id":"ruby","symbol":"ruby","name":"RUBY"},{"id":"ruby-currency","symbol":"rbc","name":"Ruby Currency"},{"id":"ruby-play-network","symbol":"ruby","name":"Ruby Play Network"},{"id":"ruff","symbol":"ruff","name":"Ruff"},{"id":"r-u-generous","symbol":"rug","name":"R U Generous"},{"id":"rugpull-prevention","symbol":"rugpull","name":"Rugpull Prevention"},{"id":"rugzombie","symbol":"zmbe","name":"RugZombie"},{"id":"run","symbol":"run","name":"Run"},{"id":"runblox","symbol":"rux","name":"RunBlox"},{"id":"run-burn","symbol":"rbt","name":"Run\u0026Burn"},{"id":"rune-shards","symbol":"rxs","name":"Rune Shards"},{"id":"runner-land","symbol":"rltv2","name":"RLTv2"},{"id":"run-together","symbol":"run","name":"Run Together"},{"id":"runy","symbol":"runy","name":"Runy"},{"id":"rupee","symbol":"rup","name":"Rupee"},{"id":"rupiah-token","symbol":"idrt","name":"Rupiah Token"},{"id":"rusd","symbol":"rusd","name":"rUSD"},{"id":"rushcoin","symbol":"rush","name":"RushCoin"},{"id":"rutheneum","symbol":"rth","name":"Rutheneum"},{"id":"ruufcoin","symbol":"ruuf","name":"RuufCoin"},{"id":"rxcdnatoken","symbol":"dna","name":"RxcDna"},{"id":"rxcgames","symbol":"rxcg","name":"RXCGames"},{"id":"ryi-unity","symbol":"ryiu","name":"RYI Unity"},{"id":"ryo","symbol":"ryo","name":"Ryo Currency"},{"id":"ryoma","symbol":"ryoma","name":"Ryoma"},{"id":"ryoshis-vision","symbol":"ryoshi","name":"Ryoshis Vision"},{"id":"ryoshi-token","symbol":"ryoshi","name":"Ryoshi"},{"id":"s4fe","symbol":"s4f","name":"S4FE"},{"id":"sabai-ecovers","symbol":"sabai","name":"Sabai Ecoverse"},{"id":"sabaka-inu","symbol":"sabaka inu","name":"Sabaka Inu"},{"id":"saber","symbol":"sbr","name":"Saber"},{"id":"sacred-tails","symbol":"st","name":"Sacred Tails"},{"id":"saddle-finance","symbol":"sdl","name":"Saddle Finance"},{"id":"safcoin","symbol":"saf","name":"SafCoin"},{"id":"safe-anwang","symbol":"safe","name":"SAFE(AnWang)"},{"id":"safe-baby-shiba","symbol":"sbsh","name":"Safe Baby Shiba"},{"id":"safeblast","symbol":"blast","name":"SafeBlast"},{"id":"safecapital","symbol":"scap","name":"SafeCapital"},{"id":"safechaintoken","symbol":"sct","name":"Safechain"},{"id":"safeclassic","symbol":"safeclassic","name":"SafeClassic"},{"id":"safe-coin-2","symbol":"safe","name":"SafeCoin"},{"id":"safecookie","symbol":"safecookie","name":"SafeCookie"},{"id":"safe-deal","symbol":"sfd","name":"SafeDeal"},{"id":"safedogecoin-v2","symbol":"safedoge","name":"SafeDogeCoin V2"},{"id":"safedollar","symbol":"sdo","name":"SafeDollar"},{"id":"safeearth","symbol":"safeearth","name":"SafeEarth"},{"id":"safegem","symbol":"gems","name":"Safegem"},{"id":"safegrow","symbol":"sfg","name":"SafeGrow"},{"id":"safe-haven","symbol":"sha","name":"Safe Haven"},{"id":"safeinsure","symbol":"sins","name":"SafeInsure"},{"id":"safelaunch","symbol":"sfex","name":"SafeLaunch"},{"id":"safemars","symbol":"safemars","name":"Safemars"},{"id":"safemars-protocol","symbol":"smars","name":"Safemars Protocol"},{"id":"safememe","symbol":"sme","name":"SafeMeme"},{"id":"safemoon","symbol":"safemoon","name":"SafeMoon [OLD]"},{"id":"safemoon-1996","symbol":"sm96","name":"Safemoon 1996"},{"id":"safemoon-2","symbol":"sfm","name":"SafeMoon"},{"id":"safemoon-inu","symbol":"smi","name":"SafeMoon Inu"},{"id":"safemoon-swap","symbol":"sfms","name":"SafeMoon Swap"},{"id":"safemoon-zilla","symbol":"sfz","name":"Safemoon Zilla"},{"id":"safe-nebula","symbol":"snb","name":"Safe Nebula"},{"id":"safepal","symbol":"sfp","name":"SafePal"},{"id":"safermoon","symbol":"safermoon","name":"SAFERMOON"},{"id":"safe-seafood-coin","symbol":"ssf","name":"Safe SeaFood Coin"},{"id":"safeswap-online","symbol":"swap","name":"SafeSwap Online"},{"id":"safeswap-token","symbol":"ssgtx","name":"Safeswap SSGTX"},{"id":"safe-token","symbol":"safe","name":"Safe"},{"id":"safetrees","symbol":"trees","name":"Safetrees"},{"id":"safe-universe","symbol":"sfu","name":"Safe Universe"},{"id":"safewolf","symbol":"sw","name":"SafeWolf"},{"id":"safezone","symbol":"safezone","name":"SafeZone [OLD]"},{"id":"safezone-2","symbol":"safezone","name":"SafeZone"},{"id":"saffron-finance","symbol":"sfi","name":"saffron.finance"},{"id":"safle","symbol":"safle","name":"Safle"},{"id":"safufide","symbol":"safest","name":"SafuFide"},{"id":"safu-protocol","symbol":"safu","name":"SAFU Protocol"},{"id":"safuu","symbol":"safuu","name":"SAFUU"},{"id":"saharadao","symbol":"mng","name":"SaharaDAO"},{"id":"sai","symbol":"sai","name":"Sai"},{"id":"saiko-the-revival","symbol":"saiko","name":"Saiko - The Revival"},{"id":"sail","symbol":"sail","name":"SAIL"},{"id":"saint-inu","symbol":"saint","name":"Saint Inu"},{"id":"saint-ligne","symbol":"stle","name":"Saint Ligne"},{"id":"saitama-inu","symbol":"saitama","name":"Saitama"},{"id":"saitamax","symbol":"saitax","name":"SaitamaX"},{"id":"saitanobi","symbol":"saitanobi","name":"Saitanobi"},{"id":"saitarealty","symbol":"srlty","name":"SaitaRealty"},{"id":"saito","symbol":"saito","name":"Saito"},{"id":"saitoki-inu","symbol":"saitoki","name":"Saitoki Inu"},{"id":"sak3","symbol":"sak3","name":"SAKE"},{"id":"sakai-vault","symbol":"sakai","name":"Sakai Vault"},{"id":"sakaryaspor","symbol":"skry","name":"Sakaryaspor"},{"id":"sake-token","symbol":"sake","name":"SakeSwap"},{"id":"sakura","symbol":"sku","name":"Sakura"},{"id":"sakura-neko","symbol":"neko","name":"Sakura Neko"},{"id":"sakura-planet","symbol":"sak","name":"Sakura Planet"},{"id":"salmon","symbol":"slm","name":"Salmon"},{"id":"salmonation","symbol":"sui","name":"Salmonation"},{"id":"salo-players","symbol":"salo","name":"Salo Players"},{"id":"salt","symbol":"salt","name":"SALT"},{"id":"saltmarble","symbol":"sml","name":"Saltmarble"},{"id":"salus","symbol":"sls","name":"SaluS"},{"id":"samo-inu","symbol":"sinu","name":"Samo INU"},{"id":"samoyedcoin","symbol":"samo","name":"Samoyedcoin"},{"id":"samsunspor-fan-token","symbol":"sam","name":"Samsunspor Fan Token"},{"id":"samurai-legends","symbol":"smg","name":"Samurai Legends"},{"id":"samusky-token","symbol":"samu","name":"Samusky"},{"id":"sanctum","symbol":"sanctum","name":"Sanctum"},{"id":"sandclock","symbol":"quartz","name":"Sandclock"},{"id":"san-diego-coin","symbol":"sand","name":"San Diego Coin"},{"id":"sandwich-network","symbol":"$sandwich","name":"Sandwich Network"},{"id":"sangkara","symbol":"misa","name":"Sangkara"},{"id":"sanin-inu","symbol":"sani","name":"Sanin Inu"},{"id":"sanji-inu","symbol":"sanji","name":"Sanji Inu"},{"id":"sanliurfaspor-token","symbol":"urfa","name":"Sanliurfaspor Token"},{"id":"sanshu-inu","symbol":"sanshu","name":"Sanshu Inu"},{"id":"santaclaus","symbol":"santa","name":"Santaclaus"},{"id":"santa-coin-2","symbol":"santa","name":"Santa Coin"},{"id":"santa-floki-v2","symbol":"hohoho","name":"Santa Floki v2.0"},{"id":"santa-inu","symbol":"saninu","name":"Santa Inu"},{"id":"santiment-network-token","symbol":"san","name":"Santiment Network"},{"id":"santos-fc-fan-token","symbol":"santos","name":"Santos FC Fan Token"},{"id":"sao-paulo-fc-fan-token","symbol":"spfc","name":"Sao Paulo FC Fan Token"},{"id":"sappchat","symbol":"app","name":"SappChat"},{"id":"sapphire","symbol":"sapp","name":"Sapphire"},{"id":"saracens-fan-token","symbol":"sarries","name":"Saracens Fan Token"},{"id":"sarcophagus","symbol":"sarco","name":"Sarcophagus"},{"id":"sashimi","symbol":"sashimi","name":"Sashimi"},{"id":"satelstar","symbol":"stsr","name":"SatelStar"},{"id":"satin-exchange","symbol":"satin","name":"Satin Exchange"},{"id":"satisfinance","symbol":"sat","name":"SatisFinance"},{"id":"sator","symbol":"sao","name":"Sator"},{"id":"satoshicity","symbol":"city","name":"SatoshiCity"},{"id":"satoshi-island","symbol":"stc","name":"Satoshi Island"},{"id":"satoshis-vision","symbol":"sats","name":"Satoshis Vision"},{"id":"satoshiswap-2","symbol":"swap","name":"SatoshiSwap"},{"id":"satozhi","symbol":"satoz","name":"Satozhi"},{"id":"satt","symbol":"satt","name":"SaTT"},{"id":"saturna","symbol":"sat","name":"Saturna"},{"id":"saucerswap","symbol":"sauce","name":"SaucerSwap"},{"id":"saudi-shiba-inu","symbol":"saudishib","name":"SAUDI SHIBA INU"},{"id":"saunafinance-token","symbol":"sauna","name":"SaunaFinance"},{"id":"savage","symbol":"savg","name":"SAVAGE"},{"id":"savanna","symbol":"svn","name":"Savanna"},{"id":"savant-ai","symbol":"savantai","name":"Savant AI"},{"id":"save-baby-doge","symbol":"babydoge","name":"Save Baby Doge"},{"id":"savedroid","symbol":"svd","name":"Savedroid"},{"id":"saveplanetearth","symbol":"spe","name":"SavePlanetEarth"},{"id":"savetheworld","symbol":"save","name":"SaveTheWorld"},{"id":"savix","symbol":"svx","name":"Savix"},{"id":"sawa-crypto","symbol":"sawa","name":"SAWA Crypto"},{"id":"sax-token","symbol":"sax","name":"IdleStoneage SAX"},{"id":"saylor-moon","symbol":"smoon","name":"SaylorMoon"},{"id":"sbet","symbol":"sbet","name":"SBET"},{"id":"sb-group","symbol":"sbg","name":"SB Group"},{"id":"sbtc","symbol":"sbtc","name":"sBTC"},{"id":"sbu-honey","symbol":"bhny","name":"SBU Honey"},{"id":"scalara-nft-index","symbol":"nfti","name":"Scalara NFT Index"},{"id":"scaleswap-token","symbol":"sca","name":"Scaleswap"},{"id":"scallop","symbol":"sclp","name":"Scallop"},{"id":"scalpingcoin","symbol":"scalp","name":"SCALPingcoin"},{"id":"scarab-finance","symbol":"scarab","name":"Scarab Finance"},{"id":"scarcity","symbol":"scx","name":"Scarcity"},{"id":"scarecrow","symbol":"scare","name":"ScareCrow"},{"id":"scarface-finance","symbol":"scar","name":"Scarface Finance"},{"id":"scarface-lion","symbol":"sfl","name":"ScarFace Lion"},{"id":"scary-bunny","symbol":"sb","name":"Scary Bunny"},{"id":"s-c-corinthians-fan-token","symbol":"sccp","name":"S.C. Corinthians Fan Token"},{"id":"scholarship-coin","symbol":"scho","name":"Scholarship Coin"},{"id":"schrodinger","symbol":"kitty dinger","name":"Schrodinger"},{"id":"schwiftai","symbol":"swai","name":"SchwiftAI"},{"id":"sci-coin","symbol":"sci","name":"SCI Coin [OLD]"},{"id":"sci-coin-2","symbol":"sci+","name":"SCI Coin"},{"id":"scientia","symbol":"scie","name":"Scientia"},{"id":"scientix","symbol":"scix","name":"Scientix"},{"id":"scifi-index","symbol":"scifi","name":"SCIFI Index"},{"id":"sc-internacional-fan-token","symbol":"saci","name":"SC Internacional Fan Token"},{"id":"sconex","symbol":"sconex","name":"SCOneX"},{"id":"scootercoin","symbol":"scoot","name":"ScooterCoin"},{"id":"scopecoin","symbol":"xscp","name":"ScopeCoin"},{"id":"scopuly-token","symbol":"scop","name":"Scopuly"},{"id":"scorai","symbol":"scorai","name":"Staking Compound ORAI"},{"id":"score-token","symbol":"sco","name":"Score"},{"id":"scotty-beam","symbol":"scotty","name":"Scotty Beam"},{"id":"scouthub","symbol":"hub","name":"Scouthub"},{"id":"scrap","symbol":"scrap","name":"Scrap"},{"id":"scratch","symbol":"scratch","name":"Scratch"},{"id":"scream","symbol":"scream","name":"Scream"},{"id":"scriv","symbol":"scriv","name":"SCRIV"},{"id":"scrooge","symbol":"scrooge","name":"Scrooge"},{"id":"scrooge-junior","symbol":"scrooge jr","name":"SCROOGE JUNIOR"},{"id":"scry-info","symbol":"ddd","name":"Scry.info"},{"id":"scry-protocol","symbol":"scry","name":"Scry Protocol"},{"id":"sculptor","symbol":"sculpt","name":"Sculptor"},{"id":"sdao","symbol":"sdao","name":"SDAO"},{"id":"sea","symbol":"sea","name":"Sea"},{"id":"seachain","symbol":"seachain","name":"SeaChain"},{"id":"seadog-metaverse","symbol":"seadog","name":"Seadog Metaverse"},{"id":"seahorsechain","symbol":"seah","name":"SeahorseChain"},{"id":"seamlessswap-token","symbol":"seamless","name":"SeamlessSwap"},{"id":"seancecircle","symbol":"seance","name":"SeanceCircle"},{"id":"seatlabnft","symbol":"seat","name":"SeatlabNFT"},{"id":"seba","symbol":"seba","name":"Seba"},{"id":"sechain","symbol":"snn","name":"SeChain"},{"id":"secret","symbol":"scrt","name":"Secret"},{"id":"secret-erc20","symbol":"wscrt","name":"Secret (ERC20)"},{"id":"secret-finance","symbol":"sefi","name":"Secret Finance"},{"id":"secret-skellies-society","symbol":"$crypt","name":"Secret Skellies Society"},{"id":"secretum","symbol":"ser","name":"Secretum"},{"id":"secretworld","symbol":"ssd","name":"SecretWorld"},{"id":"sector","symbol":"sect","name":"Sector"},{"id":"sector-finance","symbol":"sect","name":"Sector Finance"},{"id":"secure-cash","symbol":"scsx","name":"Secure Cash"},{"id":"secured-moonrat-token","symbol":"smrat","name":"Secured MoonRat"},{"id":"sedo-pow-token","symbol":"sedo","name":"SEDO POW"},{"id":"seedbox","symbol":"sbx","name":"SeedBox"},{"id":"seeded-network","symbol":"seeded","name":"Seeded Network"},{"id":"seedify-fund","symbol":"sfund","name":"Seedify.fund"},{"id":"seedlaunch","symbol":"slt","name":"SeedLaunch"},{"id":"seedling","symbol":"sdln","name":"Seedling"},{"id":"seedon","symbol":"seon","name":"Seedon"},{"id":"seeds","symbol":"seeds","name":"Seeds"},{"id":"seedswap","symbol":"snft","name":"SeedSwap"},{"id":"seedswap-token","symbol":"seed","name":"SeedSwap SEED"},{"id":"seedx","symbol":"seedx","name":"SEEDx"},{"id":"seek-tiger","symbol":"sti","name":"Seek Tiger"},{"id":"seele","symbol":"seele","name":"Seele"},{"id":"seesaw","symbol":"ssw","name":"Seesaw"},{"id":"seigniorage-shares","symbol":"share","name":"Seigniorage Shares"},{"id":"seiren-games-network","symbol":"serg","name":"Seiren Games Network"},{"id":"sekuritance","symbol":"skrt","name":"Sekuritance"},{"id":"sekuya","symbol":"skuy","name":"Sekuya"},{"id":"selfbar","symbol":"sbar","name":"Selfbar"},{"id":"selfkey","symbol":"key","name":"SelfKey"},{"id":"senate","symbol":"senate","name":"SENATE"},{"id":"sendcrypto","symbol":"sendc","name":"SendCrypto"},{"id":"sense","symbol":"sense","name":"Sense"},{"id":"sense4fit","symbol":"sfit","name":"Sense4FIT"},{"id":"sensei","symbol":"sensei","name":"Sensei"},{"id":"sensi","symbol":"sensi","name":"Sensi"},{"id":"sensitrust","symbol":"sets","name":"Sensitrust"},{"id":"senso","symbol":"senso","name":"SENSO"},{"id":"senspark","symbol":"sen","name":"Senspark"},{"id":"sentiment-token","symbol":"sent","name":"Sentiment"},{"id":"sentinel","symbol":"dvpn","name":"Sentinel"},{"id":"sentinel-chain","symbol":"senc","name":"Sentinel Chain"},{"id":"sentinel-group","symbol":"dvpn","name":"Sentinel [OLD]"},{"id":"sentinel-protocol","symbol":"upp","name":"Sentinel Protocol"},{"id":"sentivate","symbol":"sntvt","name":"Sentivate"},{"id":"sentre","symbol":"sntr","name":"Sentre"},{"id":"seor-network","symbol":"seor","name":"SEOR Network"},{"id":"serenity","symbol":"seren","name":"Serenity"},{"id":"serey-coin","symbol":"sry","name":"Serey Coin"},{"id":"serum","symbol":"srm","name":"Serum"},{"id":"serum-ser","symbol":"ser","name":"Serum SER"},{"id":"seth","symbol":"seth","name":"sETH"},{"id":"seth2","symbol":"seth2","name":"sETH2"},{"id":"setter-protocol","symbol":"set","name":"Setter Protocol"},{"id":"seur","symbol":"seur","name":"sEUR"},{"id":"seven-q","symbol":"svq","name":"Seven-Q"},{"id":"severe-rise-games","symbol":"srgt","name":"Severe Rise Games"},{"id":"sf-capital","symbol":"sfcp","name":"SF Capital"},{"id":"s-finance","symbol":"sfg","name":"S.Finance"},{"id":"sgd-tracker","symbol":"blusgd","name":"SGD Tracker"},{"id":"shack","symbol":"shack","name":"Shack"},{"id":"shade-cash","symbol":"shade","name":"Shade Cash"},{"id":"shade-protocol","symbol":"shd","name":"Shade Protocol"},{"id":"shadowcats","symbol":"shadowcats","name":"Shadowcats"},{"id":"shadows","symbol":"dows","name":"Shadows"},{"id":"shadowswap-token","symbol":"shdw","name":"ShadowSwap Token"},{"id":"shadow-token","symbol":"shdw","name":"Shadow"},{"id":"shaggy-token","symbol":"shag","name":"SHAGGY TOKEN"},{"id":"shakita-inu","symbol":"shak","name":"Shakita Inu"},{"id":"shaman","symbol":"shaman","name":"Shaman"},{"id":"shambala","symbol":"bala","name":"Shambala"},{"id":"shanum","symbol":"shan","name":"Shanum"},{"id":"shapeshift-fox-token","symbol":"fox","name":"ShapeShift FOX"},{"id":"sharbi","symbol":"$sharbi","name":"Sharbi"},{"id":"shard","symbol":"shard","name":"Shard Coin"},{"id":"shard-2","symbol":"shard","name":"Shard"},{"id":"shardeum","symbol":"shm","name":"Shardeum"},{"id":"shardus","symbol":"ult","name":"Shardus"},{"id":"sharedstake-governance-token","symbol":"sgtv2","name":"SharedStake Governance v2"},{"id":"sharering","symbol":"shr","name":"Share"},{"id":"shark","symbol":"shark","name":"Shark"},{"id":"sharky-swap","symbol":"sharky","name":"Sharky Swap"},{"id":"sheepasheep","symbol":"ylgy","name":"SheepAsheep"},{"id":"sheesh","symbol":"sheesh","name":"Sheesh"},{"id":"sheesha-finance","symbol":"sheesha","name":"Sheesha Finance (BEP20)"},{"id":"sheesha-finance-erc20","symbol":"sheesha","name":"Sheesha Finance (ERC20)"},{"id":"sheesha-finance-polygon","symbol":"msheesha","name":"Sheesha Finance Polygon"},{"id":"sheikh-inu","symbol":"shinu","name":"Sheikh Inu"},{"id":"shelling","symbol":"shl","name":"Shelling"},{"id":"shell-token","symbol":"shell","name":"Shell"},{"id":"shelterz","symbol":"terz","name":"SHELTERZ"},{"id":"shen","symbol":"shen","name":"Shen"},{"id":"shengweihu","symbol":"shengweihu","name":"Shengweihu"},{"id":"shera","symbol":"shr","name":"shera [OLD]"},{"id":"shera-2","symbol":"shr","name":"Shera"},{"id":"sherlock","symbol":"sher","name":"Sherlock"},{"id":"shiba-bsc","symbol":"shibsc","name":"SHIBA BSC"},{"id":"shiba-cartel","symbol":"pesos","name":"Shiba Cartel"},{"id":"shibacash","symbol":"shibacash","name":"ShibaCash"},{"id":"shiba-ceo","symbol":"shibceo","name":"Shiba CEO"},{"id":"shiba-classic","symbol":"shibc","name":"Shiba Classic"},{"id":"shibacorgi","symbol":"shico","name":"ShibaCorgi"},{"id":"shibadoge","symbol":"shibdoge","name":"ShibaDoge"},{"id":"shiba-dollars","symbol":"shibadollars","name":"Shiba Dollars"},{"id":"shiba-elon","symbol":"eshib","name":"Shiba Elon"},{"id":"shibaelonverse","symbol":"shibev","name":"ShibaElonVerse"},{"id":"shiba-fantom","symbol":"shiba","name":"Shiba Fantom"},{"id":"shiba-floki","symbol":"floki","name":"Shiba Floki Inu"},{"id":"shibaforest","symbol":"shf","name":"ShibaForest"},{"id":"shibagalaxy","symbol":"shibgx","name":"ShibaGalaxy"},{"id":"shibagun","symbol":"shibgun","name":"Shibagun"},{"id":"shibai-labs","symbol":"slab","name":"ShibAI Labs"},{"id":"shiba-inu","symbol":"shib","name":"Shiba Inu"},{"id":"shiba-inu-classic","symbol":"shibic","name":"SHIBIC"},{"id":"shiba-inu-empire","symbol":"shibemp","name":"Shiba Inu Empire"},{"id":"shiba-inu-mother","symbol":"shibm","name":"Shiba Inu Mother"},{"id":"shiba-inu-wormhole","symbol":"shib","name":"Shiba Inu (Wormhole)"},{"id":"shibaken-finance","symbol":"shibaken","name":"Shibaken Finance"},{"id":"shibalana","symbol":"shiba","name":"Shibalana"},{"id":"shibalite","symbol":"shiblite","name":"ShibaLite"},{"id":"shibamon","symbol":"shibamon","name":"Shibamon"},{"id":"shibana","symbol":"bana","name":"Shibana"},{"id":"shibanft","symbol":"shibanft","name":"ShibaNFT"},{"id":"shiba-nodes","symbol":"shino","name":"Shiba Nodes"},{"id":"shibapoconk","symbol":"conk","name":"ShibaPoconk"},{"id":"shiba-predator","symbol":"qom","name":"Shiba Predator"},{"id":"shibarium-dao","symbol":"shibdao","name":"Shibarium DAO"},{"id":"shibarium-name-service","symbol":"sns","name":"Shibarium Name Service"},{"id":"shibarium-pad","symbol":"$shibp","name":"Shibarium Pad"},{"id":"shibarium-perpetuals","symbol":"serp","name":"Shibarium Perpetuals"},{"id":"shib-army","symbol":"shibarmy","name":"Shib Army"},{"id":"shiba-universe","symbol":"shibu","name":"Shiba Universe"},{"id":"shibavax","symbol":"shibx","name":"Shibavax"},{"id":"shibaverse","symbol":"verse","name":"Shibaverse"},{"id":"shibaverse-token","symbol":"shiver","name":"Shibaverse SHIVER"},{"id":"shibaw-inu","symbol":"shibaw","name":"ShibaW Inu"},{"id":"shiba-world-cup","symbol":"swc","name":"Shiba World Cup"},{"id":"shibazilla","symbol":"shibazilla","name":"ShibaZilla"},{"id":"shibcat","symbol":"shibcat","name":"SHIBCAT"},{"id":"shibceo","symbol":"shibceo","name":"ShibCEO"},{"id":"shibchain","symbol":"sc","name":"ShibChain"},{"id":"shibcraft","symbol":"shft","name":"Shibcraft"},{"id":"shibelon","symbol":"shibelon","name":"ShibElon"},{"id":"shibfalcon","symbol":"shflcn","name":"ShibFalcon"},{"id":"shib-generating","symbol":"shg","name":"Shib Generating"},{"id":"shibgf","symbol":"shibgf","name":"SHIBGF"},{"id":"shibird","symbol":"shird","name":"Shibird"},{"id":"shibkiller","symbol":"shibkiller","name":"ShibKiller"},{"id":"shibmerican","symbol":"shibmerican","name":"Shibmerican"},{"id":"shibnaut","symbol":"shibn","name":"Shibnaut"},{"id":"shibnobi","symbol":"shinja","name":"Shibnobi"},{"id":"shibonk","symbol":"shibo","name":"ShibonkBSC"},{"id":"shibonk-311f81df-a4ea-4f31-9e61-df0af8211bd7","symbol":"sbonk","name":"SHIBONK"},{"id":"shib-original-vision","symbol":"sov","name":"Shib Original Vision"},{"id":"shibosu-a4432072-cdc3-4f03-b781-46937463ea98","symbol":"shibo","name":"Shibosu"},{"id":"shibot","symbol":"shibot","name":"Shibot"},{"id":"shibtama","symbol":"shibtama","name":"Shibtama"},{"id":"shibuya-white-rabbit","symbol":"wrab","name":"Shibuya White Rabbit"},{"id":"shibwallet","symbol":"swt","name":"ShibWallet"},{"id":"shiden","symbol":"sdn","name":"Shiden Network"},{"id":"shido","symbol":"shido","name":"Shido"},{"id":"shield","symbol":"xsh","name":"SHIELD"},{"id":"shield-bsc-token","symbol":"shdb","name":"Shield BSC Token"},{"id":"shield-dao","symbol":"sld","name":"Shield (SLD)"},{"id":"shield-finance","symbol":"coli","name":"Coliquidity"},{"id":"shield-network","symbol":"shieldnet","name":"Shield Network"},{"id":"shield-protocol-2","symbol":"shield","name":"Shield Protocol"},{"id":"shih-tzu","symbol":"shih","name":"Shih Tzu"},{"id":"shihtzu-exchange","symbol":"stzu","name":"Shihtzu Exchange"},{"id":"shih-tzu-inu","symbol":"shih-tzu","name":"Shih Tzu Inu"},{"id":"shika-finance","symbol":"shika","name":"Shika Finance"},{"id":"shikoku","symbol":"shik","name":"Shikoku"},{"id":"shikoku-inu","symbol":"shiko","name":"Shikoku Inu"},{"id":"shila-inu","symbol":"shil","name":"Shila Inu"},{"id":"shilling","symbol":"sh","name":"Shilling"},{"id":"shill-token","symbol":"shill","name":"SHILL Token"},{"id":"shilly-bar","symbol":"shbar","name":"Shilly Bar"},{"id":"shimmer","symbol":"smr","name":"Shimmer"},{"id":"shina-inu","symbol":"shi","name":"Shina Inu"},{"id":"shinchan-token","symbol":"shinnosuke","name":"ShinChan"},{"id":"shinji-inu","symbol":"shinji","name":"Shinji Inu"},{"id":"shinjiru-inu","symbol":"shinji","name":"Shinjiru Inu"},{"id":"shinsekai","symbol":"shin","name":"Shinsekai"},{"id":"shinshu-inu","symbol":"shinshu","name":"Shinshu Inu"},{"id":"shintama","symbol":"shintama","name":"Shintama"},{"id":"shira-cat","symbol":"catshira","name":"Shira Cat"},{"id":"shirtum","symbol":"shi","name":"Shirtum"},{"id":"shiryo-inu","symbol":"shiryo-inu","name":"Shiryo"},{"id":"shita-kiri-suzume","symbol":"suzume","name":"Shita-kiri Suzume"},{"id":"shitcoin","symbol":"shit","name":"ShitCoin"},{"id":"shitzu","symbol":"shitzu","name":"Shitzu"},{"id":"shiwa","symbol":"shiwa","name":"Shiwa"},{"id":"shkooby-inu","symbol":"shkooby","name":"SHKOOBY INU"},{"id":"shoebill-coin","symbol":"shbl","name":"Shoebill Coin"},{"id":"shoefy","symbol":"shoe","name":"ShoeFy"},{"id":"shontoken","symbol":"shon","name":"Shon"},{"id":"shoot","symbol":"shoo","name":"SHOOT"},{"id":"shopdi","symbol":"shod","name":"Shopdi"},{"id":"shopnext-loyalty-token","symbol":"next","name":"ShopNext Loyalty Token"},{"id":"shopnext-reward-token","symbol":"ste","name":"ShopNEXT Reward Token"},{"id":"shopping-io-token","symbol":"shop","name":"Shopping.io"},{"id":"shori","symbol":"yshori","name":"Shori"},{"id":"short-term-t-bill-token","symbol":"stbt","name":"Short-term T-Bill Token"},{"id":"shping","symbol":"shping","name":"Shping"},{"id":"shrapnel","symbol":"shrap","name":"Shrapnel"},{"id":"shroom-finance","symbol":"shroom","name":"Niftyx Protocol"},{"id":"shuey-rhon-inu","symbol":"shuey","name":"Shuey Rhon Inu"},{"id":"shumi","symbol":"shumi","name":"SHUMI"},{"id":"shuna-inuverse","symbol":"shunav2","name":"Shuna Inuverse"},{"id":"shuts-wave","symbol":"swave","name":"shuts Wave"},{"id":"shyft-network-2","symbol":"shft","name":"Shyft Network"},{"id":"siacoin","symbol":"sc","name":"Siacoin"},{"id":"siambitcoin","symbol":"sbtc","name":"SiamBitcoin"},{"id":"siaprime-coin","symbol":"scp","name":"ScPrime"},{"id":"sibcoin","symbol":"sib","name":"SIBCoin"},{"id":"sicash","symbol":"sic","name":"SICash"},{"id":"sidekick-token","symbol":"sk","name":"SideKick"},{"id":"sideshift-token","symbol":"xai","name":"SideShift"},{"id":"sidus","symbol":"sidus","name":"Sidus"},{"id":"sienna","symbol":"sienna","name":"Sienna"},{"id":"sienna-erc20","symbol":"wsienna","name":"Sienna [ERC-20]"},{"id":"sif","symbol":"sif","name":"Sif"},{"id":"sifchain","symbol":"erowan","name":"Sifchain"},{"id":"sifu-vision","symbol":"sifu","name":"SIFU"},{"id":"sigil-finance","symbol":"sigil","name":"Sigil Finance"},{"id":"sign","symbol":"sign","name":"Sign"},{"id":"signata","symbol":"sata","name":"Signata"},{"id":"signed","symbol":"sign","name":"Signed"},{"id":"signum","symbol":"signa","name":"Signum"},{"id":"silent-notary","symbol":"ubsn","name":"Silent Notary"},{"id":"silk","symbol":"silk","name":"Spider Tanks"},{"id":"silo-finance","symbol":"silo","name":"Silo Finance"},{"id":"silva-token","symbol":"silva","name":"Silva"},{"id":"silvercashs","symbol":"svc","name":"Silvercashs"},{"id":"silverstonks","symbol":"sstx","name":"Silver Stonks"},{"id":"silver-tokenized-stock-defichain","symbol":"dslv","name":"iShares Silver Trust Defichain"},{"id":"simba-inu","symbol":"simbainu","name":"Simba Inu"},{"id":"simba-storage-token","symbol":"sst","name":"SIMBA Storage"},{"id":"simbcoin-swap","symbol":"smbswap","name":"SimbCoin Swap"},{"id":"simple-masternode-coin","symbol":"smnc","name":"Simple Masternode Coin"},{"id":"simple-token","symbol":"ost","name":"OST"},{"id":"simpli-finance","symbol":"simpli","name":"Simpli Finance"},{"id":"simp-token","symbol":"simp","name":"Simp"},{"id":"simracer-coin","symbol":"src","name":"Simracer Coin"},{"id":"sincere-cate","symbol":"$scate","name":"Sincere Cate"},{"id":"sincere-doge","symbol":"sdoge","name":"Sincere Doge"},{"id":"sinceredogedao","symbol":"sdao","name":"SincereDogeDAO"},{"id":"sin-city","symbol":"sin","name":"Sinverse"},{"id":"sincronix","symbol":"snx","name":"SincroniX"},{"id":"sinfinite","symbol":"sfn","name":"Sinfinite"},{"id":"singh","symbol":"singh","name":"Singh"},{"id":"single-finance","symbol":"single","name":"Single Finance"},{"id":"sing-token","symbol":"sing","name":"Sing"},{"id":"sing-token-avalanche","symbol":"sing","name":"Sing (Avalanche)"},{"id":"sing-token-bsc","symbol":"sing","name":"Sing (BSC)"},{"id":"sing-token-ftm","symbol":"sing","name":"Sing FTM"},{"id":"singulardtv","symbol":"sngls","name":"SingularDTV"},{"id":"singularity","symbol":"sgly","name":"Singularity"},{"id":"singularitydao","symbol":"sdao","name":"SingularityDAO"},{"id":"singularitynet","symbol":"agix","name":"SingularityNET"},{"id":"sino","symbol":"sino","name":"Cantosino.com"},{"id":"sint-truidense-voetbalvereniging-fan-token","symbol":"stv","name":"Sint-Truidense Voetbalvereniging Fan Token"},{"id":"sipher","symbol":"sipher","name":"Sipher"},{"id":"siren","symbol":"si","name":"Siren"},{"id":"sirin-labs-token","symbol":"srn","name":"Sirin Labs"},{"id":"sirius-finance","symbol":"srs","name":"Sirius Finance"},{"id":"sivasspor","symbol":"siv","name":"Sivasspor"},{"id":"six-network","symbol":"six","name":"SIX Network"},{"id":"sjwcoin","symbol":"sjw","name":"SJWCoin"},{"id":"skale","symbol":"skl","name":"SKALE"},{"id":"skeb","symbol":"skeb","name":"Skeb"},{"id":"skey-network","symbol":"skey","name":"Skey Network"},{"id":"skillchain","symbol":"ski","name":"Skillchain"},{"id":"skincoin","symbol":"skin","name":"SkinCoin"},{"id":"sklay","symbol":"sklay","name":"sKLAY"},{"id":"skrumble-network","symbol":"skm","name":"Skrumble Network"},{"id":"skull","symbol":"skull","name":"Skull"},{"id":"skullswap-exchange","symbol":"skull","name":"SkullSwap Exchange"},{"id":"sky-bandit-coin","symbol":"sbc","name":"Sky Bandit Coin"},{"id":"skycoin","symbol":"sky","name":"Skycoin"},{"id":"skyplay","symbol":"skp","name":"SKYPlay"},{"id":"skyrim-finance","symbol":"skyrim","name":"Skyrim Finance"},{"id":"skyup","symbol":"su","name":"skyup"},{"id":"sky-v2","symbol":"sky","name":"SkyToken"},{"id":"slam-token","symbol":"slam","name":"Slam"},{"id":"sleepearn-finance","symbol":"sen","name":"SleepEarn Finance"},{"id":"sleepfuture","symbol":"sleepee","name":"SleepFuture"},{"id":"slimcoin","symbol":"slm","name":"Slimcoin"},{"id":"slime-royale-cupid-essence","symbol":"sce","name":"Slime Royale Cupid Essence"},{"id":"slime-royale-gold","symbol":"srg","name":"Slime Royale Gold"},{"id":"slnv2","symbol":"slnv2","name":"SLNV2"},{"id":"small-fish-cookie","symbol":"sfc","name":"Small Fish Cookie"},{"id":"smardex","symbol":"sdex","name":"SmarDex"},{"id":"smart-block-chain-city","symbol":"sbcc","name":"Smart Block Chain City"},{"id":"smartcash","symbol":"smart","name":"SmartCash"},{"id":"smartcoin-2","symbol":"smrt","name":"SmartCoin"},{"id":"smart-coin-smrtr","symbol":"smrtr","name":"SmarterCoin"},{"id":"smartcredit-token","symbol":"smartcredit","name":"SmartCredit"},{"id":"smart-donation-coin","symbol":"sdc","name":"Smart Donation Coin"},{"id":"smart-electrum","symbol":"select","name":"Smart Electrum"},{"id":"smartfi","symbol":"smtf","name":"SmartFi"},{"id":"smart-game-finance","symbol":"smart","name":"Smart Game Finance"},{"id":"smartlands","symbol":"dnt","name":"Definder Network"},{"id":"smartlink","symbol":"smak","name":"Smartlink"},{"id":"smartlox","symbol":"smartlox","name":"SmartLOX"},{"id":"smart-marketing-token","symbol":"smt","name":"Smart Marketing"},{"id":"smart-medical-coin","symbol":"smc","name":"Smart Medical Coin"},{"id":"smartmesh","symbol":"smt","name":"SmartMesh"},{"id":"smart-mfg","symbol":"mfg","name":"Smart MFG"},{"id":"smartnft","symbol":"smartnft","name":"SmartNFT"},{"id":"smartofgiving","symbol":"aog","name":"smARTOFGIVING"},{"id":"smartpad-2","symbol":"pad","name":"SmartPad"},{"id":"smart-reward-token","symbol":"srt","name":"Smart Reward Token"},{"id":"smartshare","symbol":"ssp","name":"Smartshare"},{"id":"smart-shiba","symbol":"smartshib","name":"Smart Shiba"},{"id":"smart-valor","symbol":"valor","name":"Smart Valor"},{"id":"smart-wallet-token","symbol":"swt","name":"Smart Wallet"},{"id":"smart-world-union","symbol":"swu","name":"Smart World Union"},{"id":"smarty-pay","symbol":"spy","name":"Smarty Pay"},{"id":"smash-cash","symbol":"smash","name":"Smash Cash"},{"id":"smaugs-nft","symbol":"smg","name":"Smaugs NFT"},{"id":"smd-coin","symbol":"smd","name":"SMD Coin"},{"id":"smelt","symbol":"smelt","name":"Smelt"},{"id":"smg","symbol":"smg","name":"SMG"},{"id":"smile-coin","symbol":"smile","name":"Smile Coin"},{"id":"smileycoin","symbol":"smly","name":"Smileycoin"},{"id":"smolting-inu","symbol":"smol","name":"Smolting Inu"},{"id":"smooth-love-potion","symbol":"slp","name":"Smooth Love Potion"},{"id":"smoothy","symbol":"smty","name":"Smoothy"},{"id":"smpcoin","symbol":"smpc","name":"SMPCOIN"},{"id":"smscodes","symbol":"smsct","name":"SMSCodes"},{"id":"smurfsinu","symbol":"smurf","name":"SmurfsINU"},{"id":"snail-trail","symbol":"slime","name":"Snail Trail"},{"id":"snake-city","symbol":"snct","name":"Snake City"},{"id":"snake-token","symbol":"snk","name":"CryptoSnake"},{"id":"snapex","symbol":"snap","name":"SnapEx"},{"id":"snapy","symbol":"spy","name":"Snapy"},{"id":"snetwork","symbol":"snet","name":"Snetwork"},{"id":"snfts-seedify-nft-space","symbol":"snfts","name":"Seedify NFT Space"},{"id":"snkrz","symbol":"skz","name":"SNKRZ"},{"id":"snook","symbol":"snk","name":"Snook"},{"id":"snovio","symbol":"snov","name":"Snovian.Space"},{"id":"snow","symbol":"icz","name":"Snow"},{"id":"snowball-token","symbol":"snob","name":"Snowball"},{"id":"snowbank","symbol":"sb","name":"Snowbank"},{"id":"snowblossom","symbol":"snow","name":"SnowBlossom"},{"id":"snowcrash-token","symbol":"nora","name":"SnowCrash"},{"id":"snowflake","symbol":"$snow","name":"Snowflake"},{"id":"snowswap","symbol":"snow","name":"Snowswap"},{"id":"snowtomb","symbol":"stomb","name":"Snowtomb"},{"id":"snowtomb-lot","symbol":"slot","name":"Snowtomb LOT"},{"id":"snx-yvault","symbol":"yvsnx","name":"SNX yVault"},{"id":"soakmont","symbol":"skmt","name":"Soakmont"},{"id":"soba-token","symbol":"soba","name":"SOBA"},{"id":"so-cal","symbol":"sct","name":"So Cal"},{"id":"socaverse","symbol":"soca","name":"Socaverse"},{"id":"soccer-crypto","symbol":"sot","name":"Soccer Crypto"},{"id":"soccer-galaxy","symbol":"sog","name":"Soccer Galaxy"},{"id":"soccerhub","symbol":"sch","name":"SoccerHub"},{"id":"soccerinu","symbol":"soccer","name":"SoccerInu"},{"id":"soccers-dog","symbol":"sd","name":"Soccers Dog"},{"id":"socean-staked-sol","symbol":"scnsol","name":"Socean Staked Sol"},{"id":"social-ai","symbol":"socialai","name":"Social AI"},{"id":"socialblox","symbol":"sblx","name":"SocialBlox"},{"id":"social-capitalism-2","symbol":"socap","name":"Social Capitalism"},{"id":"social-good-project","symbol":"sg","name":"SocialGood"},{"id":"sociall","symbol":"scl","name":"Sociall"},{"id":"social-send","symbol":"send","name":"Social Send"},{"id":"socialswap-token","symbol":"sst","name":"Social Swap"},{"id":"socialxclub","symbol":"sxc","name":"SocialxClub"},{"id":"socol","symbol":"simp","name":"SO-COL"},{"id":"soda-coin","symbol":"soc","name":"SODA Coin"},{"id":"sodatsu","symbol":"sodatsu","name":"Sodatsu"},{"id":"soft-dao","symbol":"soft","name":"Soft DAO"},{"id":"soga-project","symbol":"soga","name":"SOGA Project"},{"id":"sohei","symbol":"hei","name":"Sohei"},{"id":"sokuswap","symbol":"soku","name":"SokuSwap"},{"id":"solabrador","symbol":"solab","name":"Solabrador"},{"id":"solace","symbol":"solace","name":"SOLACE"},{"id":"solana","symbol":"sol","name":"Solana"},{"id":"solana-ecosystem-index","symbol":"soli","name":"Solana Ecosystem Index"},{"id":"solana-inu","symbol":"inu","name":"Solana Inu"},{"id":"solana-nut","symbol":"solnut","name":"Solana Nut"},{"id":"solanaprime","symbol":"prime","name":"SolanaPrime"},{"id":"solanasail-governance-token","symbol":"gsail","name":"SolanaSail Governance"},{"id":"solanax","symbol":"sold","name":"Solanax"},{"id":"sola-ninja","symbol":"snj","name":"Sola Ninja"},{"id":"solanium","symbol":"slim","name":"Solanium"},{"id":"solape-token","symbol":"solape","name":"SOLAPE"},{"id":"solar","symbol":"solar","name":"Solar"},{"id":"solarbeam","symbol":"solar","name":"Solarbeam"},{"id":"solar-bear","symbol":"solbear","name":"Solar Bear"},{"id":"solar-energy","symbol":"seg","name":"Solar Energy"},{"id":"solareum-wallet","symbol":"xsb","name":"Solareum Wallet"},{"id":"solarflare","symbol":"flare","name":"Solarflare"},{"id":"solar-full-cycle","symbol":"sfc","name":"Solar Full Cycle"},{"id":"solaris-betting-token","symbol":"sbt","name":"Solaris Betting Token"},{"id":"solaris-finance","symbol":"slr","name":"Solaris Finance"},{"id":"solarix","symbol":"solarix","name":"SOLARIX"},{"id":"solarminex","symbol":"smx","name":"SolarMineX"},{"id":"solarr","symbol":"slrr","name":"Solarr"},{"id":"sola-token","symbol":"sola","name":"SOLA"},{"id":"sola-x","symbol":"sax","name":"SOLA-X"},{"id":"sol-baby-doge","symbol":"sbabydoge","name":"SOL Baby Doge"},{"id":"solbank-token","symbol":"sbnk","name":"Solbank"},{"id":"solberg","symbol":"slb","name":"Solberg"},{"id":"solcash","symbol":"solcash","name":"SOLCash"},{"id":"solcats","symbol":"meow","name":"Solcats"},{"id":"solchicks-shards","symbol":"shards","name":"SolChicks Shards"},{"id":"solchicks-token","symbol":"chicks","name":"SolChicks"},{"id":"solcial","symbol":"slcl","name":"Solcial"},{"id":"solclout","symbol":"sct","name":"SolClout"},{"id":"solcondoms","symbol":"condoms","name":"SolCondoms"},{"id":"solcubator","symbol":"solc","name":"Solcubator"},{"id":"soldate-token","symbol":"date","name":"SolDate"},{"id":"solderland","symbol":"sldr","name":"Solderland"},{"id":"soldex","symbol":"solx","name":"Soldex"},{"id":"soldoge","symbol":"sdoge","name":"SolDoge"},{"id":"solend","symbol":"slnd","name":"Solend"},{"id":"solex-finance","symbol":"slx","name":"Solex Finance"},{"id":"solfarm","symbol":"tulip","name":"Tulip Protocol"},{"id":"solfina","symbol":"solfi","name":"Solfina"},{"id":"sol-flowers","symbol":"flwr","name":"SOL Flowers"},{"id":"solge","symbol":"solge","name":"Solge"},{"id":"solice","symbol":"slc","name":"Solice"},{"id":"solidex","symbol":"sex","name":"Solidex"},{"id":"solidlizard","symbol":"sliz","name":"SolidLizard"},{"id":"solidly","symbol":"solid","name":"Solidly v1"},{"id":"solidlydex","symbol":"solid","name":"Solidly"},{"id":"solid-protocol","symbol":"solid","name":"Solid Protocol"},{"id":"solidsex-tokenized-vesolid","symbol":"solidsex","name":"SOLIDsex: Tokenized veSOLID"},{"id":"solily-protocol","symbol":"lily","name":"Solily Protocol"},{"id":"solimax","symbol":"slm","name":"SoliMax"},{"id":"solit","symbol":"slt","name":"Solit"},{"id":"sollama-utilities","symbol":"sollama","name":"Sollama Utilities"},{"id":"solminter","symbol":"smrt","name":"Solminter"},{"id":"solo-coin","symbol":"solo","name":"Sologenic"},{"id":"solomon-defi","symbol":"slm","name":"Solomon Defi"},{"id":"soloxcoin","symbol":"sl","name":"SoloxCoin"},{"id":"solpad-finance","symbol":"solpad","name":"Solpad Finance"},{"id":"solpatrol-bail","symbol":"bail","name":"SolPatrol Bail"},{"id":"solpay-finance","symbol":"solpay","name":"SolPay Finance"},{"id":"solrazr","symbol":"solr","name":"RazrFi"},{"id":"solrise-finance","symbol":"slrs","name":"Solrise Finance"},{"id":"solster","symbol":"str","name":"Solster"},{"id":"soltato-fries","symbol":"fries","name":"Soltato FRIES"},{"id":"solum","symbol":"solum","name":"Solum"},{"id":"soluna","symbol":"slna","name":"Soluna"},{"id":"solve-care","symbol":"solve","name":"SOLVE"},{"id":"solvent","symbol":"svt","name":"Solvent"},{"id":"solvia","symbol":"sva","name":"Solvia"},{"id":"solv-protocol","symbol":"solv","name":"Solv Protocol"},{"id":"sol-wormhole","symbol":"sol","name":"SOL (Wormhole)"},{"id":"solx-gaming-guild","symbol":"sgg","name":"SolX Gaming Guild"},{"id":"solyard-finance","symbol":"yard","name":"Solyard Finance"},{"id":"sombra-network","symbol":"smbr","name":"Sombra"},{"id":"somee-advertising-token","symbol":"sat","name":"SoMee Advertising"},{"id":"somee-social","symbol":"somee","name":"SoMee.Social"},{"id":"somee-social-old","symbol":"ong","name":"SoMee.Social [OLD]"},{"id":"somesing","symbol":"ssx","name":"SOMESING Exchange"},{"id":"sommelier","symbol":"somm","name":"Sommelier"},{"id":"somnium","symbol":"som","name":"Somnium"},{"id":"somnium-space-cubes","symbol":"cube","name":"Somnium Space CUBEs"},{"id":"sonar","symbol":"ping","name":"Sonar"},{"id":"sonarwatch","symbol":"sonar","name":"SonarWatch"},{"id":"songbird","symbol":"sgb","name":"Songbird"},{"id":"songcoin","symbol":"song","name":"SongCoin"},{"id":"sonic-suite","symbol":"sonic","name":"Sonic Suite"},{"id":"sonm","symbol":"snm","name":"SONM"},{"id":"sonne-finance","symbol":"sonne","name":"Sonne Finance"},{"id":"sonocoin","symbol":"sono","name":"SonoCoin"},{"id":"son-of-doge-v2","symbol":"sod","name":"Son of Doge"},{"id":"soonaverse","symbol":"soon","name":"Soonaverse"},{"id":"soonswap","symbol":"soon","name":"SoonSwap"},{"id":"sopay","symbol":"sop","name":"SoPay"},{"id":"sora","symbol":"xor","name":"Sora"},{"id":"sorachancoin","symbol":"sora","name":"SorachanCoin"},{"id":"sora-synthetics","symbol":"xst","name":"SORA Synthetics"},{"id":"sora-synthetic-usd","symbol":"xstusd","name":"SORA Synthetic USD"},{"id":"sora-validator-token","symbol":"val","name":"Sora Validator"},{"id":"sos-foundation","symbol":"sos","name":"SOS Foundation"},{"id":"sota-finance","symbol":"sota","name":"SOTA Finance"},{"id":"soul-dog-city-bones","symbol":"bones","name":"Soul Dogs City Bones"},{"id":"soulocoin","symbol":"soulo","name":"SouloCoin"},{"id":"soulsaver","symbol":"soul","name":"Soulsaver"},{"id":"souls-of-meta","symbol":"som","name":"Souls of Meta"},{"id":"soul-swap","symbol":"soul","name":"Soul Swap"},{"id":"sound-coin","symbol":"sound","name":"Sound Coin"},{"id":"souni-token","symbol":"son","name":"Souni"},{"id":"soup-finance","symbol":"soup","name":"Soup Finance"},{"id":"sourceless","symbol":"str","name":"Sourceless"},{"id":"source-protocol","symbol":"srcx","name":"Source Protocol"},{"id":"southxchange-coin","symbol":"sxcc","name":"SouthXchange Coin"},{"id":"sov","symbol":"sov","name":"SOV"},{"id":"sovi-token","symbol":"sovi","name":"Sovi"},{"id":"sovryn","symbol":"sov","name":"Sovryn"},{"id":"sowl","symbol":"sowl","name":"SOWL"},{"id":"soy-finance","symbol":"soy","name":"Soy Finance"},{"id":"spacechain-erc-20","symbol":"spc","name":"SpaceChain (ERC-20)"},{"id":"spacecorgi","symbol":"scorgi","name":"SpaceCorgi"},{"id":"space-corsair-key","symbol":"sck","name":"Space Corsair Key"},{"id":"spacecowboy","symbol":"scb","name":"SpaceCowBoy"},{"id":"space-crypto","symbol":"spg","name":"Space Crypto"},{"id":"spacedawgs","symbol":"dawgs","name":"SpaceDawgs"},{"id":"spacefalcon","symbol":"fcon","name":"SpaceFalcon"},{"id":"spacefi","symbol":"space","name":"SpaceFi"},{"id":"spacegoat-token","symbol":"sgt","name":"SpaceGoat"},{"id":"spacegrime","symbol":"grimex","name":"SpaceGrime"},{"id":"space-id","symbol":"id","name":"SPACE ID"},{"id":"space-iz","symbol":"spiz","name":"SPACE-iZ"},{"id":"spacelens","symbol":"space","name":"Spacelens"},{"id":"spacemine","symbol":"mine","name":"SpaceMine"},{"id":"space-misfits","symbol":"smcw","name":"Space Misfits"},{"id":"spacen","symbol":"sn","name":"SpaceN"},{"id":"space-ore","symbol":"spo","name":"Space Ore"},{"id":"spacepi","symbol":"spacepi","name":"SpacePi"},{"id":"space-rebase","symbol":"space","name":"Space Rebase"},{"id":"space-rebase-xusd","symbol":"xusd","name":"Space Rebase XUSD"},{"id":"spacerobotdao","symbol":"srd","name":"SpaceRobotDao"},{"id":"spaceshipx","symbol":"xship","name":"XSHIP"},{"id":"spaceshipx-ssx","symbol":"ssx","name":"SpaceShipX SSX"},{"id":"space-sip","symbol":"sip","name":"Space SIP"},{"id":"space-soldier","symbol":"soldier","name":"Space Soldier"},{"id":"spaceswap-milk2","symbol":"milk2","name":"Spaceswap MILK2"},{"id":"spaceswap-shake","symbol":"shake","name":"Spaceswap SHAKE"},{"id":"space-token","symbol":"space","name":"ApeRocket Space"},{"id":"space-token-bsc","symbol":"space","name":"Space Token BSC"},{"id":"spacevikings","symbol":"svt","name":"SpaceVikings"},{"id":"space-xmitter","symbol":"sx","name":"Space Xmitter"},{"id":"spacey-2025","symbol":"spay","name":"SpaceY 2025"},{"id":"spain-national-fan-token","symbol":"snft","name":"Spain National Football Team Fan Token"},{"id":"spankchain","symbol":"spank","name":"SpankChain"},{"id":"sparklab","symbol":"spark","name":"SparkLab"},{"id":"sparkle-coin","symbol":"sctk","name":"Sparkle Coin"},{"id":"sparkpoint","symbol":"srk","name":"SparkPoint"},{"id":"sparkpoint-fuel","symbol":"sfuel","name":"SparkPoint Fuel"},{"id":"sparks","symbol":"spk","name":"SparksPay"},{"id":"sparq","symbol":"sparq","name":"Sparq"},{"id":"spartacats","symbol":"purr","name":"Spartacats"},{"id":"spartacus","symbol":"spa","name":"Spartacus"},{"id":"spartacus-money","symbol":"lambda","name":"Spartacus Money"},{"id":"spartan-protocol-token","symbol":"sparta","name":"Spartan Protocol"},{"id":"spartan-token","symbol":"spa","name":"Spartans"},{"id":"spdr-s-p-500-etf-trust-defichain","symbol":"dspy","name":"SPDR S\u0026P 500 ETF Trust Defichain"},{"id":"speciex","symbol":"spex","name":"Speciex"},{"id":"spectrecoin","symbol":"alias","name":"Alias"},{"id":"spectresecuritycoin","symbol":"xspc","name":"SpectreSecurityCoin"},{"id":"spectrum-finance","symbol":"spf","name":"Spectrum Finance"},{"id":"speed-mining-service","symbol":"sms","name":"Speed Mining Service"},{"id":"speed-star-joc","symbol":"joc","name":"Speed Star JOC"},{"id":"speed-star-speed","symbol":"speed","name":"Speed Star SPEED"},{"id":"speed-star-star","symbol":"star","name":"Speed Star STAR"},{"id":"spellfire","symbol":"spellfire","name":"Spellfire"},{"id":"spell-token","symbol":"spell","name":"Spell"},{"id":"sperax","symbol":"spa","name":"Sperax"},{"id":"sperax-usd","symbol":"usds","name":"Sperax USD"},{"id":"sphere","symbol":"sphr","name":"Sphere"},{"id":"sphere-finance","symbol":"sphere","name":"Sphere Finance"},{"id":"spheresxs","symbol":"sxs","name":"SphereSXS"},{"id":"spherium","symbol":"sphri","name":"Spherium"},{"id":"spheroid-universe","symbol":"sph","name":"Spheroid Universe"},{"id":"sphynx","symbol":"sphynx","name":"Sphynx"},{"id":"sphynxfi","symbol":"sf","name":"SphynxFi"},{"id":"spice","symbol":"spice","name":"Spice Token"},{"id":"spice-dao","symbol":"spice","name":"Spice DAO"},{"id":"spice-trade","symbol":"spice","name":"Spice Trade"},{"id":"spiceusd","symbol":"usds","name":"SpiceUSD"},{"id":"spiderdao","symbol":"spdr","name":"SpiderDAO"},{"id":"spillways","symbol":"spillways","name":"Spillways"},{"id":"spinada-cash","symbol":"spin","name":"Spinada Cash"},{"id":"spindle","symbol":"spd","name":"SPINDLE"},{"id":"spin-fi","symbol":"$spin","name":"Spin Fi"},{"id":"spintop","symbol":"spin","name":"Spintop"},{"id":"spiritswap","symbol":"spirit","name":"SpiritSwap"},{"id":"spitz-chain","symbol":"spc","name":"Spitz Chain"},{"id":"splinterlands","symbol":"sps","name":"Splintershards"},{"id":"splyt","symbol":"shopx","name":"SHOPX"},{"id":"spookeletons-token","symbol":"spkl","name":"Spookeletons"},{"id":"spookyhalloweenfloki","symbol":"shf","name":"SpookyHalloweenFloki"},{"id":"spookyshiba-2","symbol":"spky","name":"SpookyShiba"},{"id":"spookyswap","symbol":"boo","name":"Spookyswap"},{"id":"spooky-uni","symbol":"spku","name":"Spooky Uni"},{"id":"spool-dao-token","symbol":"spool","name":"Spool DAO"},{"id":"spore","symbol":"spore","name":"Spore"},{"id":"spores-network","symbol":"spo","name":"Spores Network"},{"id":"sporkdao","symbol":"spork","name":"SporkDAO"},{"id":"sport","symbol":"sport","name":"SPORT"},{"id":"sportium","symbol":"sprt","name":"Sportium"},{"id":"sports-2k75","symbol":"s2k","name":"Sports 2K75"},{"id":"sports-artificial","symbol":"sports-ai","name":"Sports Artificial"},{"id":"sports-bet","symbol":"sbet","name":"Sports Bet"},{"id":"sportsicon","symbol":"$icons","name":"SportsIcon"},{"id":"sportsverse","symbol":"sv","name":"Sportsverse"},{"id":"sporty","symbol":"sporty","name":"Sporty"},{"id":"sportzchain","symbol":"spn","name":"Sportzchain"},{"id":"spot","symbol":"spot","name":"Spot"},{"id":"spotrax","symbol":"spox","name":"Spotrax"},{"id":"spots","symbol":"spt","name":"Spots"},{"id":"spring","symbol":"spring","name":"Spring"},{"id":"sprink","symbol":"sprink","name":"Sprink"},{"id":"sprint-coin","symbol":"sprx","name":"Sprint Coin"},{"id":"spritzmoon-crypto","symbol":"spritzmoon","name":"SpritzMoon Crypto Token"},{"id":"sprouts","symbol":"sprts","name":"Sprouts"},{"id":"spume","symbol":"spume","name":"Spume"},{"id":"spurdex","symbol":"spdx","name":"SpurDex"},{"id":"sqgl-vault-nftx","symbol":"sqgl","name":"SQGL Vault (NFTX)"},{"id":"squad","symbol":"squad","name":"Superpower Squad"},{"id":"square-token","symbol":"squa","name":"Square"},{"id":"squeeze-token","symbol":"squeeze","name":"Squeeze"},{"id":"squid-game","symbol":"squid","name":"Squid Game"},{"id":"squidgrow","symbol":"squidgrow","name":"SquidGrow"},{"id":"squirrel-finance","symbol":"nuts","name":"Squirrel Finance"},{"id":"squirt-game","symbol":"squirt","name":"Squirt Game"},{"id":"srnartgallery","symbol":"sact","name":"srnArtGallery"},{"id":"srune","symbol":"srune","name":"sRUNE"},{"id":"ssv-network","symbol":"ssv","name":"SSV Network"},{"id":"stabilize","symbol":"stbz","name":"Stabilize"},{"id":"stabilize-token","symbol":"set","name":"Stabilize SET"},{"id":"stable-asset","symbol":"sta","name":"STABLE ASSET"},{"id":"stabledoc-token","symbol":"sdt","name":"Stabledoc"},{"id":"stablefund-usd","symbol":"sfusd","name":"StableFund USD"},{"id":"stable-one-rocket","symbol":"srocket","name":"Stable One Rocket"},{"id":"stableusd","symbol":"usds","name":"Stably USDS"},{"id":"stablexswap","symbol":"stax","name":"StableXSwap"},{"id":"stablz","symbol":"stablz","name":"Stablz"},{"id":"stacker-ventures","symbol":"stack","name":"Stacker Ventures"},{"id":"stackos","symbol":"stack","name":"StackOS"},{"id":"stacktical","symbol":"dsla","name":"DSLA Protocol"},{"id":"stacy","symbol":"stacy","name":"Stacy"},{"id":"stade-francais-paris-fan-token","symbol":"sfp","name":"Stade Français Paris Fan Token"},{"id":"stader","symbol":"sd","name":"Stader"},{"id":"stader-bnbx","symbol":"bnbx","name":"Stader BNBx"},{"id":"stader-maticx","symbol":"maticx","name":"Stader MaticX"},{"id":"stader-nearx","symbol":"nearx","name":"Stader NearX"},{"id":"stader-sftmx","symbol":"sftmx","name":"Stader sFTMX"},{"id":"stadium","symbol":"std","name":"Stadium"},{"id":"stafi","symbol":"fis","name":"Stafi"},{"id":"stafi-staked-atom","symbol":"ratom","name":"StaFi Staked ATOM"},{"id":"stafi-staked-bnb","symbol":"rbnb","name":"StaFi Staked BNB"},{"id":"stafi-staked-matic","symbol":"rmatic","name":"StaFi Staked MATIC"},{"id":"stafi-staked-sol","symbol":"rsol","name":"StaFi Staked SOL"},{"id":"staika","symbol":"stik","name":"Staika"},{"id":"stakeborg-dao","symbol":"standard","name":"Construct"},{"id":"stakecube","symbol":"scc","name":"Stakecube"},{"id":"staked-aave-balancer-pool-token","symbol":"stkabpt","name":"Staked Aave Balancer Pool Token"},{"id":"stake-dao","symbol":"sdt","name":"Stake DAO"},{"id":"stake-dao-crv","symbol":"sdcrv","name":"Stake DAO CRV"},{"id":"staked-core","symbol":"score","name":"Staked CORE"},{"id":"staked-ether","symbol":"steth","name":"Lido Staked Ether"},{"id":"staked-frax-ether","symbol":"sfrxeth","name":"Staked Frax Ether"},{"id":"staked-kcs","symbol":"skcs","name":"Staked KCS"},{"id":"staked-near","symbol":"stnear","name":"Staked NEAR"},{"id":"staked-tarot","symbol":"xtarot","name":"Staked TAROT"},{"id":"staked-wemix","symbol":"stwemix","name":"Staked WEMIX"},{"id":"staked-yearn-crv-vault","symbol":"st-ycrv","name":"Staked Yearn CRV Vault"},{"id":"stake-goblin","symbol":"goblin","name":"Stake Goblin"},{"id":"stake-link","symbol":"sdl","name":"stake.link"},{"id":"stake-link-staked-link","symbol":"stlink","name":"Staked LINK"},{"id":"stakemoon","symbol":"smoon","name":"Stakemoon"},{"id":"staker-dao","symbol":"stkr","name":"Staker DAO"},{"id":"stakewise","symbol":"swise","name":"StakeWise"},{"id":"stamen-tellus-token","symbol":"stt","name":"Stamen Tellus Token"},{"id":"standard-euro","symbol":"seuro","name":"Standard Euro"},{"id":"standard-protocol","symbol":"stnd","name":"Standard Protocol"},{"id":"standard-token","symbol":"tst","name":"The Standard Token"},{"id":"star-atlas","symbol":"atlas","name":"Star Atlas"},{"id":"star-atlas-dao","symbol":"polis","name":"Star Atlas DAO"},{"id":"starbots","symbol":"bot","name":"Starbots"},{"id":"starbots-gear","symbol":"gear","name":"Starbots GEAR"},{"id":"starchain","symbol":"stc","name":"StarChain"},{"id":"star-chain","symbol":"star","name":"Star Chain"},{"id":"starchi","symbol":"elixir","name":"Starchi"},{"id":"starcoin","symbol":"stc","name":"Starcoin"},{"id":"starfish-finance","symbol":"sean","name":"Starfish Finance"},{"id":"starfish-os","symbol":"sfo","name":"StarFish OS"},{"id":"stargate-finance","symbol":"stg","name":"Stargate Finance"},{"id":"stargaze","symbol":"stars","name":"Stargaze"},{"id":"starkmeta","symbol":"smeta","name":"StarkMeta"},{"id":"starlaunch","symbol":"stars","name":"StarLaunch"},{"id":"starlay-finance","symbol":"lay","name":"Starlay Finance"},{"id":"starlink","symbol":"starl","name":"StarLink"},{"id":"starly","symbol":"starly","name":"Starly"},{"id":"starmon-token","symbol":"smon","name":"StarMon"},{"id":"starname","symbol":"iov","name":"Starname"},{"id":"starpad","symbol":"srp","name":"Starpad"},{"id":"starsharks","symbol":"sss","name":"StarSharks"},{"id":"starsharks-sea","symbol":"sea","name":"StarSharks SEA"},{"id":"starship","symbol":"starship","name":"StarShip"},{"id":"starslax","symbol":"sslx","name":"StarSlax"},{"id":"star-wars-cat","symbol":"swcat","name":"Star Wars Cat"},{"id":"starworks-global-ecosystem","symbol":"starx","name":"STARX"},{"id":"starz","symbol":"stz","name":"Starz"},{"id":"stasis-eurs","symbol":"eurs","name":"STASIS EURO"},{"id":"stat","symbol":"stat","name":"STAT"},{"id":"statera","symbol":"sta","name":"Statera"},{"id":"statik","symbol":"statik","name":"Statik"},{"id":"sta-token","symbol":"sta","name":"STA"},{"id":"stats","symbol":"stats","name":"STATS"},{"id":"status","symbol":"snt","name":"Status"},{"id":"stay","symbol":"stay","name":"STAY"},{"id":"staysafu","symbol":"safu","name":"StaySAFU"},{"id":"steakbank-finance","symbol":"sbf","name":"SteakBank Finance"},{"id":"steakhut-finance","symbol":"steak","name":"SteakHut Finance"},{"id":"stealthcoin","symbol":"xst","name":"Stealth"},{"id":"steam-exchange","symbol":"steamx","name":"Steam Exchange"},{"id":"steem","symbol":"steem","name":"Steem"},{"id":"steem-dollars","symbol":"sbd","name":"Steem Dollars"},{"id":"stelia","symbol":"stelia","name":"Stelia"},{"id":"stella-fantasy-token","symbol":"sfty","name":"Stella Fantasy Token"},{"id":"stellar","symbol":"xlm","name":"Stellar"},{"id":"stellar-diamond","symbol":"xld","name":"Stellar Diamond"},{"id":"stellaswap","symbol":"stella","name":"StellaSwap"},{"id":"stellite","symbol":"xla","name":"Scala"},{"id":"stemx","symbol":"stemx","name":"STEMX"},{"id":"step","symbol":"step","name":"Step"},{"id":"step-app-fitfi","symbol":"fitfi","name":"Step App"},{"id":"stepex","symbol":"spex","name":"StepEx"},{"id":"step-finance","symbol":"step","name":"Step Finance"},{"id":"stepg","symbol":"stepg","name":"STEPG"},{"id":"step-hero","symbol":"hero","name":"Step Hero"},{"id":"stepn","symbol":"gmt","name":"STEPN"},{"id":"stepwatch","symbol":"swp","name":"Stepwatch"},{"id":"stepwatch-land-token","symbol":"swld","name":"Stepwatch Land Token"},{"id":"stereoai","symbol":"stai","name":"StereoAI"},{"id":"sterling-finance","symbol":"str","name":"Sterling Finance"},{"id":"stfx","symbol":"stfx","name":"STFX"},{"id":"stickman-battleground","symbol":"stman","name":"Stickman Battleground"},{"id":"stilton","symbol":"stilt","name":"Stilton"},{"id":"stima","symbol":"stima","name":"STIMA"},{"id":"stkatom","symbol":"stkatom","name":"stkATOM"},{"id":"stkd-scrt","symbol":"stkd","name":"Stkd SCRT"},{"id":"stobox-token","symbol":"stbu","name":"Stobox"},{"id":"ston","symbol":"ston","name":"Ston"},{"id":"stonedao","symbol":"sdt","name":"StoneDAO"},{"id":"stoned-ape-crew-index","symbol":"sac","name":"Stoned Ape Crew Index"},{"id":"stone-token","symbol":"stn","name":"Stone"},{"id":"stonkleague","symbol":"aegis","name":"StonkLeague"},{"id":"stopelon","symbol":"stopelon","name":"StopElon"},{"id":"storepay","symbol":"spc","name":"Storepay"},{"id":"storiqa","symbol":"stq","name":"Storiqa"},{"id":"storj","symbol":"storj","name":"Storj"},{"id":"storm","symbol":"stmx","name":"StormX"},{"id":"storm-token","symbol":"storm","name":"Storm"},{"id":"storx","symbol":"srx","name":"StorX"},{"id":"story","symbol":"story","name":"Story"},{"id":"stox","symbol":"stx","name":"Stox"},{"id":"stp-network","symbol":"stpt","name":"STP"},{"id":"straitsx-indonesia-rupiah","symbol":"xidr","name":"XIDR"},{"id":"stratis","symbol":"strax","name":"Stratis"},{"id":"stratos","symbol":"stos","name":"Stratos"},{"id":"strayacoin","symbol":"nah","name":"Strayacoin"},{"id":"streakk","symbol":"stkk","name":"Streakk"},{"id":"streamcoin","symbol":"strm","name":"StreamCoin"},{"id":"streamer-inu","symbol":"streamerinu","name":"Streamer Inu"},{"id":"streamr","symbol":"data","name":"Streamr"},{"id":"streamr-xdata","symbol":"xdata","name":"Streamr XDATA"},{"id":"streeth","symbol":"streeth","name":"STREETH"},{"id":"street-runner","symbol":"srg","name":"Street Runner"},{"id":"strelka-ai","symbol":"strelka ai","name":"Strelka AI"},{"id":"stride","symbol":"strd","name":"Stride"},{"id":"stride-staked-atom","symbol":"statom","name":"Stride Staked Atom"},{"id":"stride-staked-juno","symbol":"stjuno","name":"Stride Staked Juno"},{"id":"stride-staked-luna","symbol":"$stluna","name":"Stride Staked Luna"},{"id":"stride-staked-osmo","symbol":"stosmo","name":"Stride Staked Osmo"},{"id":"strike","symbol":"strk","name":"Strike"},{"id":"strikecoin","symbol":"strx","name":"StrikeX"},{"id":"strip-finance","symbol":"strip","name":"Strip Finance"},{"id":"strips-finance","symbol":"strp","name":"Strips Finance"},{"id":"stripto","symbol":"strip","name":"Stripto"},{"id":"strite","symbol":"stri","name":"Strite"},{"id":"stroke-prevention-genomicdao","symbol":"pcsp","name":"Stroke-Prevention GenomicDAO"},{"id":"strong","symbol":"strong","name":"Strong"},{"id":"stronger","symbol":"strngr","name":"Stronger"},{"id":"stronghands-finance","symbol":"ishnd","name":"StrongHands Finance"},{"id":"stronghands-masternode","symbol":"shmn","name":"StrongHands Masternode"},{"id":"stronghold-token","symbol":"shx","name":"Stronghold"},{"id":"strongnode","symbol":"sne","name":"StrongNode"},{"id":"structure-finance","symbol":"stf","name":"Structure Finance"},{"id":"strudel-finance","symbol":"trdl","name":"Strudel Finance"},{"id":"strx-finance","symbol":"sfi","name":"STRX Finance"},{"id":"student-coin","symbol":"stc","name":"Student Coin"},{"id":"studyum","symbol":"stud","name":"Studyum"},{"id":"style","symbol":"style","name":"Style"},{"id":"stylike-governance","symbol":"styl","name":"Stylike Governance"},{"id":"subdao","symbol":"gov","name":"SubDAO"},{"id":"subme","symbol":"sub","name":"Subme"},{"id":"substratum","symbol":"sub","name":"Substratum"},{"id":"succession","symbol":"sccn","name":"Succession"},{"id":"sucrecoin","symbol":"xsr","name":"Sucrecoin"},{"id":"sudoswap","symbol":"sudo","name":"sudoswap"},{"id":"sugarbounce","symbol":"tip","name":"SugarBounce"},{"id":"sugar-kingdom","symbol":"candy","name":"Sugar Kingdom"},{"id":"sugaryield","symbol":"sugar","name":"SugarYield"},{"id":"sukhavati-network","symbol":"skt","name":"Sukhavati Network"},{"id":"sukiyaki","symbol":"suki","name":"Sukiyaki"},{"id":"suku","symbol":"suku","name":"SUKU"},{"id":"summer","symbol":"summer","name":"Summer"},{"id":"sumokoin","symbol":"sumo","name":"Sumokoin"},{"id":"suncontract","symbol":"snc","name":"SunContract"},{"id":"sundaeswap","symbol":"sundae","name":"SundaeSwap"},{"id":"sunder-goverance-token","symbol":"sunder","name":"Sunder Goverance"},{"id":"suneku","symbol":"suneku","name":"Suneku"},{"id":"sunflower-finance","symbol":"sfo","name":"Sunflower Finance"},{"id":"sunflower-land","symbol":"sfl","name":"Sunflower Land"},{"id":"sunny-aggregator","symbol":"sunny","name":"Sunny Aggregator"},{"id":"sunnysideup","symbol":"ssu","name":"SunnySideUp"},{"id":"sunrise","symbol":"sunc","name":"Sunrise"},{"id":"sun-token","symbol":"sun","name":"Sun Token"},{"id":"supa-foundation","symbol":"supa","name":"SUPA Foundation"},{"id":"supe-infinity","symbol":"supe","name":"Supe Infinity"},{"id":"super-athletes-token","symbol":"sat","name":"Super Athletes Token"},{"id":"superbid","symbol":"superbid","name":"SuperBid"},{"id":"superciety","symbol":"super","name":"PeerMe SUPER"},{"id":"super-coinview-token","symbol":"scv","name":"Super CoinView"},{"id":"superfarm","symbol":"super","name":"SuperVerse"},{"id":"super-hero","symbol":"sh","name":"Super Hero"},{"id":"superlauncher-dao","symbol":"launch","name":"Superlauncher"},{"id":"superpower-squad","symbol":"ecg","name":"Superpower Squad ECG"},{"id":"superrare","symbol":"rare","name":"SuperRare"},{"id":"super-rare-ball-shares","symbol":"srbp","name":"Super Rare Ball Potion"},{"id":"superrarebears-rare","symbol":"rare","name":"SuperRareBears RARE"},{"id":"super-shiba","symbol":"$sshiba","name":"Super Shiba"},{"id":"superstake","symbol":"superstake","name":"Superstake"},{"id":"super-three-kingdoms","symbol":"stk","name":"Super Three Kingdoms"},{"id":"supertx-governance-token","symbol":"sup","name":"SuperTx Governance"},{"id":"superwalk","symbol":"grnd","name":"SuperWalk"},{"id":"super-zero","symbol":"sero","name":"SERO"},{"id":"supplycon","symbol":"splc","name":"SupplyCon"},{"id":"supremacy","symbol":"sups","name":"Supremacy"},{"id":"supreme-finance","symbol":"hype","name":"Supreme Finance"},{"id":"supreme-finance-hypes","symbol":"hypes","name":"Supreme Finance HYPES"},{"id":"suprenft","symbol":"snft","name":"SupreNFT"},{"id":"suqa","symbol":"sin","name":"SINOVATE"},{"id":"sureremit","symbol":"rmt","name":"SureRemit"},{"id":"surfexutilitytoken","symbol":"surf","name":"SurfExUtilityToken"},{"id":"surf-finance","symbol":"surf","name":"Surf.Finance"},{"id":"surfswap","symbol":"tide","name":"Surfswap"},{"id":"surge-inu","symbol":"surge","name":"Surge Inu"},{"id":"surviving-soldiers","symbol":"ssg","name":"Surviving Soldiers"},{"id":"survivor","symbol":"$srv","name":"Survivor"},{"id":"susd-yvault","symbol":"yvsusd","name":"sUSD yVault"},{"id":"sushi","symbol":"sushi","name":"Sushi"},{"id":"sushi-yvault","symbol":"yvsushi","name":"SUSHI yVault"},{"id":"sustainable-energy-token","symbol":"set","name":"Sustainable Energy"},{"id":"suterusu","symbol":"suter","name":"Suterusu"},{"id":"suvereno","symbol":"suv","name":"Suvereno"},{"id":"swagbucks","symbol":"bucks","name":"SwagBucks"},{"id":"swag-finance","symbol":"swag","name":"SWAG Finance"},{"id":"s-wallet-protocol","symbol":"swp","name":"S-Wallet Protocol"},{"id":"swamp-coin","symbol":"swamp","name":"Swamp Coin"},{"id":"swampy","symbol":"swamp","name":"Swampy"},{"id":"swanlana","symbol":"swan","name":"Swanlana"},{"id":"swap","symbol":"xwp","name":"Swap"},{"id":"swapdex","symbol":"sdxb","name":"SwapDEX"},{"id":"swaperry","symbol":"perry","name":"Swaperry"},{"id":"swapfish","symbol":"fish","name":"SwapFish"},{"id":"swapfolio","symbol":"swfl","name":"Swapfolio"},{"id":"swapify","symbol":"swify","name":"Swapify"},{"id":"swapp","symbol":"swapp","name":"SWAPP Protocol"},{"id":"swapped-finance","symbol":"swpd","name":"Swapped Finance"},{"id":"swappery-token","symbol":"swpr","name":"The Swappery"},{"id":"swappi","symbol":"ppi","name":"Swappi"},{"id":"swapr","symbol":"swpr","name":"Swapr"},{"id":"swapsicle-pops","symbol":"pops","name":"Swapsicle"},{"id":"swaptracker","symbol":"swpt","name":"SwapTracker"},{"id":"swapz-app","symbol":"swapz","name":"SWAPZ.app"},{"id":"swarm","symbol":"swm","name":"Swarm Network"},{"id":"swarm-bzz","symbol":"bzz","name":"Swarm"},{"id":"swarm-city","symbol":"swt","name":"Swarm City"},{"id":"swarm-markets","symbol":"smt","name":"Swarm Markets"},{"id":"swash","symbol":"swash","name":"Swash"},{"id":"sway-social","symbol":"sway","name":"Sway Social"},{"id":"sweatcoin","symbol":"sweat","name":"Sweatcoin (Sweat Economy)"},{"id":"sweep-token","symbol":"sweep","name":"Sweep Token"},{"id":"sweettoken","symbol":"swt","name":"SweetToken"},{"id":"swell-network","symbol":"swell","name":"Swell Network"},{"id":"sweply","symbol":"swply","name":"Sweply"},{"id":"swerve-dao","symbol":"swrv","name":"Swerve"},{"id":"swerve-protocol","symbol":"swerve","name":"SWERVE Protocol"},{"id":"swftcoin","symbol":"swftc","name":"SWFTCOIN"},{"id":"swgtoken","symbol":"swg","name":"SWG"},{"id":"swiftcash","symbol":"swift","name":"SwiftCash"},{"id":"swiftswap","symbol":"sws","name":"SwiftSwap"},{"id":"swinca-2","symbol":"swi","name":"Swinca"},{"id":"swing","symbol":"swing","name":"Swing"},{"id":"swingby","symbol":"swingby","name":"Swingby"},{"id":"swing-dao","symbol":"swing","name":"Swing DAO"},{"id":"swing-xyz","symbol":"$swing","name":"Swing.xyz"},{"id":"swipe","symbol":"sxp","name":"SXP"},{"id":"swirltoken","symbol":"swirl","name":"SwirlToken"},{"id":"swissborg","symbol":"chsb","name":"SwissBorg"},{"id":"swiss-nft-fund","symbol":"swissnftfund","name":"Swiss NFT Fund"},{"id":"switcheo","symbol":"swth","name":"Carbon Protocol"},{"id":"swivel-governance","symbol":"swiv","name":"Swivel Governance"},{"id":"swole-doge","symbol":"swole","name":"Swole Doge"},{"id":"swop","symbol":"swop","name":"Swop"},{"id":"sword-bsc-token","symbol":"swdb","name":"Sword BSC Token"},{"id":"swtcoin","symbol":"swat","name":"SWTCoin"},{"id":"swusd","symbol":"swusd","name":"Swerve.fi USD"},{"id":"swych","symbol":"swych","name":"Swych"},{"id":"sx-network","symbol":"sx","name":"SX Network"},{"id":"sya-x-flooz","symbol":"sya","name":"SaveYourAssets"},{"id":"syfin","symbol":"syf","name":"Syfin"},{"id":"sylo","symbol":"sylo","name":"Sylo"},{"id":"syltare","symbol":"syl","name":"SYLTARE"},{"id":"symbiosis-finance","symbol":"sis","name":"Symbiosis Finance"},{"id":"symbol","symbol":"xym","name":"Symbol"},{"id":"symmetric","symbol":"symm","name":"Symmetric"},{"id":"symverse","symbol":"sym","name":"SymVerse"},{"id":"synapse-2","symbol":"syn","name":"Synapse"},{"id":"synapse-network","symbol":"snp","name":"Synapse Network"},{"id":"synaptic-ai","symbol":"synapticai","name":"Synaptic AI"},{"id":"syncdao-governance","symbol":"sdg","name":"SyncDAO Governance"},{"id":"synchrony","symbol":"scy","name":"Synchrony"},{"id":"sync-network","symbol":"sync","name":"Sync Network"},{"id":"syndicate-2","symbol":"synr","name":"MOBLAND"},{"id":"synergy-crystal","symbol":"crs","name":"Synergy Crystal"},{"id":"synergy-diamonds","symbol":"dia","name":"Synergy Diamonds"},{"id":"synesis-one","symbol":"sns","name":"Synesis One"},{"id":"synex-coin","symbol":"minecraft","name":"Synex Coin"},{"id":"synopti","symbol":"synopti","name":"Synopti"},{"id":"synthetic-ftt","symbol":"xftt","name":"Synthetic FTT"},{"id":"synthetic-usd","symbol":"xusd","name":"Synthetic USD"},{"id":"synthetify-token","symbol":"sny","name":"Synthetify"},{"id":"synth-ousd","symbol":"ousd","name":"Synth oUSD"},{"id":"sypool","symbol":"syp","name":"Sypool"},{"id":"syrup-finance","symbol":"srx","name":"Syrup Finance"},{"id":"syscoin","symbol":"sys","name":"Syscoin"},{"id":"t","symbol":"t","name":"T"},{"id":"t23","symbol":"t23","name":"T23"},{"id":"tabank","symbol":"tab","name":"Tabank"},{"id":"taboo-token","symbol":"taboo","name":"Taboo"},{"id":"tabtrader","symbol":"ttt","name":"TabTrader"},{"id":"tacos","symbol":"taco","name":"Tacos"},{"id":"tagcoin","symbol":"tag","name":"Tagcoin"},{"id":"tag-protocol","symbol":"tag","name":"Tag Protocol"},{"id":"tahu","symbol":"tahu","name":"TAHU"},{"id":"tai","symbol":"tai","name":"tBridge"},{"id":"tail","symbol":"tail","name":"Tail"},{"id":"tail-finance","symbol":"tail","name":"Tail Finance"},{"id":"tajcoin","symbol":"taj","name":"TajCoin"},{"id":"takamaka-green-coin","symbol":"tkg","name":"Takamaka"},{"id":"takeda-shin","symbol":"takeda","name":"Takeda Shin"},{"id":"take-flight-alpha-dao","symbol":"tfa","name":"Take Flight Alpha DAO"},{"id":"takepile","symbol":"take","name":"Takepile"},{"id":"taki","symbol":"taki","name":"Taki"},{"id":"tako-token","symbol":"tako","name":"Tako"},{"id":"talaxeum","symbol":"talax","name":"Talaxeum"},{"id":"talecraft","symbol":"craft","name":"TaleCraft"},{"id":"talent","symbol":"tnt","name":"Talent"},{"id":"talent-token","symbol":"ttx","name":"Talent TTX"},{"id":"taler","symbol":"tlr","name":"Taler"},{"id":"talkado","symbol":"talk","name":"Talkado"},{"id":"talken","symbol":"talk","name":"Talken"},{"id":"tamadoge","symbol":"tama","name":"Tamadoge"},{"id":"tama-finance","symbol":"tama","name":"Tama Finance"},{"id":"tangent","symbol":"tang","name":"Tangent"},{"id":"tangible","symbol":"tngbl","name":"Tangible"},{"id":"tangle","symbol":"tngl","name":"Tangle"},{"id":"tangoswap","symbol":"tango","name":"TangoSwap"},{"id":"tank-battle","symbol":"tbl","name":"Tank Battle"},{"id":"tank-gold","symbol":"tgold","name":"Tank Gold"},{"id":"tanks","symbol":"tanks","name":"Tanks"},{"id":"tao-te-ching","symbol":"ttc","name":"Tao Te Ching"},{"id":"tap","symbol":"xtp","name":"Tap"},{"id":"tap-fantasy","symbol":"tap","name":"Tap Fantasy"},{"id":"tap-fantasy-mc","symbol":"tfmc","name":"Tap Fantasy MC"},{"id":"tapio-protocol","symbol":"tap","name":"Tapio Protocol"},{"id":"tapme-token","symbol":"tap","name":"TAPME"},{"id":"tapmydata","symbol":"tap","name":"TapX"},{"id":"taraxa","symbol":"tara","name":"Taraxa"},{"id":"tardigrades-finance","symbol":"trdg","name":"TRDGtoken"},{"id":"tari-world","symbol":"tari","name":"Tari World"},{"id":"tarmex","symbol":"tarm","name":"Tarmex"},{"id":"tarot","symbol":"tarot","name":"Tarot"},{"id":"taroverse","symbol":"taro","name":"Taroverse"},{"id":"taroverse-gold","symbol":"tgold","name":"Taroverse Gold"},{"id":"tastenft","symbol":"taste","name":"TasteNFT"},{"id":"tate-token","symbol":"topg","name":"Tate Token"},{"id":"taxa-token","symbol":"txt","name":"Taxa Network"},{"id":"tax-haven-inu","symbol":"taxhaveninu","name":"Tax Haven Inu"},{"id":"tbcc","symbol":"tbcc","name":"TBCC"},{"id":"tbtc","symbol":"tbtc","name":"tBTC"},{"id":"tcgcoin","symbol":"tcgcoin","name":"TCGCoin"},{"id":"tcgcoin-2-0","symbol":"tcg2","name":"TCGCoin 2.0"},{"id":"tcg-verse","symbol":"tcgc","name":"TCG Verse"},{"id":"tdoge","symbol":"tdoge","name":"τDoge"},{"id":"team-heretics-fan-token","symbol":"th","name":"Team Heretics Fan Token"},{"id":"team-vitality-fan-token","symbol":"vit","name":"Team Vitality Fan Token"},{"id":"teaswap-art","symbol":"tsa","name":"Teaswap Art"},{"id":"techpay","symbol":"tpc","name":"Techpay"},{"id":"techtrees","symbol":"ttc","name":"TechTrees"},{"id":"tecracoin","symbol":"tcr","name":"TecraCoin ERC20"},{"id":"tectonic","symbol":"tonic","name":"Tectonic"},{"id":"tectum","symbol":"tet","name":"Tectum"},{"id":"teddy-dog","symbol":"tdg","name":"Teddy Dog"},{"id":"teddy-doge","symbol":"teddy","name":"Teddy Doge"},{"id":"teddy-doge-v2","symbol":"teddy v2","name":"Teddy Doge V2"},{"id":"teddy-dollar","symbol":"tsd","name":"Teddy Dollar"},{"id":"te-food","symbol":"tone","name":"TE-FOOD"},{"id":"tegisto","symbol":"tgs","name":"Tegisto"},{"id":"tegro","symbol":"tgr","name":"Tegro"},{"id":"tehbag","symbol":"bag","name":"tehBag"},{"id":"teh-fund","symbol":"fund","name":"Teh Fund"},{"id":"teh-golden-one","symbol":"gold 1","name":"Teh Golden One"},{"id":"telcoin","symbol":"tel","name":"Telcoin"},{"id":"telefy","symbol":"tele","name":"Telefy"},{"id":"telegram-inu","symbol":"tinu","name":"Telegram Inu"},{"id":"tellor","symbol":"trb","name":"Tellor Tributes"},{"id":"telos","symbol":"tlos","name":"Telos"},{"id":"telos-coin","symbol":"telos","name":"Teloscoin"},{"id":"temco","symbol":"temco","name":"TEMCO"},{"id":"temdao","symbol":"tem","name":"TemDAO"},{"id":"templardao","symbol":"tem","name":"Templar DAO"},{"id":"temple","symbol":"temple","name":"TempleDAO"},{"id":"tempus","symbol":"temp","name":"Tempus"},{"id":"temtem","symbol":"tem","name":"Temtum"},{"id":"ten","symbol":"tenfi","name":"TEN"},{"id":"ten-best-coins","symbol":"tbc","name":"Ten Best Coins"},{"id":"tender-fi","symbol":"tnd","name":"Tender.fi"},{"id":"teneo","symbol":"ten","name":"Teneo"},{"id":"tenset","symbol":"10set","name":"Tenset"},{"id":"tenshi","symbol":"tenshi","name":"Tenshi"},{"id":"tenup","symbol":"tup","name":"Tenup"},{"id":"tenx","symbol":"pay","name":"TenX"},{"id":"terablock","symbol":"tbc","name":"TeraBlock"},{"id":"terareum","symbol":"tera","name":"Terareum [OLD]"},{"id":"terareum-2","symbol":"tera2","name":"Terareum"},{"id":"tera-smart-money","symbol":"tera","name":"TERA"},{"id":"teritori","symbol":"tori","name":"Teritori"},{"id":"ternio","symbol":"tern","name":"Ternio"},{"id":"terracoin","symbol":"trc","name":"Terracoin"},{"id":"terraform-dao","symbol":"terraform","name":"Terraform DAO"},{"id":"terra-luna","symbol":"lunc","name":"Terra Luna Classic"},{"id":"terra-luna-2","symbol":"luna","name":"Terra"},{"id":"terra-name-service","symbol":"tns","name":"Terra Name Service"},{"id":"terran-coin","symbol":"trr","name":"Terran Coin"},{"id":"terra-poker-token","symbol":"tpt","name":"Terra Poker Token"},{"id":"terrausd","symbol":"ustc","name":"TerraClassicUSD"},{"id":"terrausd-wormhole","symbol":"ust","name":"TerraUSD (Wormhole)"},{"id":"teseract","symbol":"tess","name":"Tesseract"},{"id":"tether","symbol":"usdt","name":"Tether"},{"id":"tether-6069e553-7ebb-487e-965e-2896cd21d6ac","symbol":"zusdt","name":"Zilliqa-bridged USDT"},{"id":"tether-avalanche-bridged-usdt-e","symbol":"usdte","name":"Tether Avalanche Bridged (USDT.e)"},{"id":"tether-eurt","symbol":"eurt","name":"Euro Tether"},{"id":"tether-gold","symbol":"xaut","name":"Tether Gold"},{"id":"tether-plenty-bridge","symbol":"usdt.e","name":"Tether (Plenty Bridge)"},{"id":"tether-pow","symbol":"usdw","name":"Tether Pow"},{"id":"tether-rainbow-bridge","symbol":"usdt.e","name":"Tether (Rainbow Bridge)"},{"id":"tether-usd-celer","symbol":"ceusdt","name":"Tether USD - Celer"},{"id":"tether-usd-pos-wormhole","symbol":"usdtpo","name":"Tether USD (PoS) (Wormhole)"},{"id":"tether-usd-wormhole","symbol":"usdtso","name":"Tether USD (Wormhole)"},{"id":"tether-usd-wormhole-from-ethereum","symbol":"usdtet","name":"Tether USD (Wormhole from Ethereum)"},{"id":"tethys-finance","symbol":"tethys","name":"Tethys Finance"},{"id":"tetu","symbol":"tetu","name":"TETU"},{"id":"tetubal","symbol":"tetubal","name":"tetuBAL"},{"id":"tetuqi","symbol":"tetuqi","name":"tetuQi"},{"id":"texan","symbol":"texan","name":"Texan"},{"id":"tezos","symbol":"xtz","name":"Tezos"},{"id":"tfs-token","symbol":"tfs","name":"TFS"},{"id":"tg-dao","symbol":"tgdao","name":"TG DAO"},{"id":"tgold","symbol":"txau","name":"tGOLD"},{"id":"tgrade","symbol":"tgd","name":"Tgrade"},{"id":"thala","symbol":"thl","name":"Thala"},{"id":"thales","symbol":"thales","name":"Thales"},{"id":"the-4th-pillar","symbol":"four","name":"4thpillar technologies"},{"id":"the9","symbol":"the9","name":"THE9"},{"id":"the-abyss","symbol":"abyss","name":"Abyss"},{"id":"the-amaze-world","symbol":"amze","name":"The Amaze World"},{"id":"the-ape","symbol":"ta","name":"The Ape"},{"id":"the-ape-society","symbol":"society","name":"The Ape Society"},{"id":"the-apis","symbol":"api","name":"The APIS"},{"id":"the-bend","symbol":"bend","name":"The Bend"},{"id":"the-bet","symbol":"bet","name":"The Bet"},{"id":"the-big-five","symbol":"bft","name":"The Big Five"},{"id":"the-cat-inu","symbol":"thecat","name":"The Cat Inu"},{"id":"the-champcoin","symbol":"tcc","name":"The ChampCoin"},{"id":"the-citadel","symbol":"citadel","name":"The Citadel"},{"id":"the-commission","symbol":"cmsn","name":"The Commission"},{"id":"the-coop-network","symbol":"gmd","name":"The Coop Network"},{"id":"the-corgi-of-polkabridge","symbol":"corgib","name":"The Corgi of PolkaBridge"},{"id":"the-crypto-prophecies","symbol":"tcp","name":"The Crypto Prophecies"},{"id":"the-crypto-you","symbol":"milk","name":"The Crypto You"},{"id":"the-daox-index","symbol":"daox","name":"The DAOX Index"},{"id":"the-debt-box","symbol":"debt","name":"The Debt Box"},{"id":"the-doge-nft","symbol":"dog","name":"The Doge NFT"},{"id":"the-dragon-gate","symbol":"koi","name":"The Dragon Gate"},{"id":"the-dynasty","symbol":"dyt","name":"The Dynasty"},{"id":"the-employment-commons-work-token","symbol":"work","name":"The Employment Commons Work"},{"id":"the-essential-coin","symbol":"esc","name":"The Essential Coin"},{"id":"the-everlasting-parachain","symbol":"elp","name":"The Everlasting Parachain"},{"id":"the-fire-token","symbol":"xfr","name":"The Fire"},{"id":"theflashcurrency","symbol":"tfc","name":"TheFlashCurrency"},{"id":"the-forbidden-forest","symbol":"forestplus","name":"The Forbidden Forest"},{"id":"theforce-trade","symbol":"foc","name":"TheForce Trade"},{"id":"thefutbolcoin","symbol":"tfc","name":"TheFutbolCoin"},{"id":"the-genesis-block","symbol":"genblok","name":"The Genesis Block"},{"id":"the-golden-dog","symbol":"dog","name":"The Golden Dog"},{"id":"the-graph","symbol":"grt","name":"The Graph"},{"id":"the-guest-list","symbol":"tgl","name":"The Guest List"},{"id":"the-husl","symbol":"husl","name":"The HUSL"},{"id":"the-killbox-game","symbol":"kbox","name":"The Killbox Game"},{"id":"the-landlord","symbol":"lndlrd","name":"The Landlord"},{"id":"the-legend-of-deification","symbol":"tlod","name":"The Legend of Deification"},{"id":"the-mars","symbol":"mrst","name":"Mars Token"},{"id":"the-meebits","symbol":"nmeebits","name":"The Meebits"},{"id":"the-meme-finance","symbol":"mefi","name":"The meme finance"},{"id":"the-midas-touch-gold","symbol":"tmtg","name":"The Midas Touch Gold"},{"id":"the-monopolist","symbol":"mono","name":"The Monopolist"},{"id":"thena","symbol":"the","name":"Thena"},{"id":"the-neko","symbol":"neko","name":"The Neko"},{"id":"the-next-world-coin","symbol":"tnc","name":"The Next World Coin"},{"id":"the-node","symbol":"the","name":"THENODE"},{"id":"the-open-network","symbol":"ton","name":"Toncoin"},{"id":"theopetra","symbol":"theo","name":"Theopetra"},{"id":"theos","symbol":"theos","name":"Theos"},{"id":"the-pablo-token","symbol":"pablo","name":"The Pablo"},{"id":"the-parallel","symbol":"prl","name":"The Parallel"},{"id":"the-path-of-light","symbol":"yori","name":"The Path Of Light"},{"id":"the-people-coin","symbol":"peep$","name":"The People’s Coin"},{"id":"the-phoenix","symbol":"fire","name":"The Phoenix"},{"id":"the-plant-dao","symbol":"sprout","name":"The Plant Dao"},{"id":"the-protocol","symbol":"the","name":"The Protocol"},{"id":"the-randomdao","symbol":"rnd","name":"The RandomDAO"},{"id":"the-realm-defenders","symbol":"trd","name":"The Realm Defenders"},{"id":"the-reaper","symbol":"rpr","name":"The Reaper"},{"id":"the-rug-game","symbol":"trg","name":"The Rug Game"},{"id":"the-sandbox","symbol":"sand","name":"The Sandbox"},{"id":"the-sandbox-wormhole","symbol":"sand","name":"The Sandbox (Wormhole)"},{"id":"the-sharks-fan-token","symbol":"sharks","name":"The Sharks Fan Token"},{"id":"thesolandao","symbol":"sdo","name":"TheSolanDAO"},{"id":"the-space","symbol":"space","name":"The Space"},{"id":"the-sprint-token","symbol":"tst","name":"The Sprint Token"},{"id":"thetadrop","symbol":"tdrop","name":"ThetaDrop"},{"id":"theta-fuel","symbol":"tfuel","name":"Theta Fuel"},{"id":"thetan-arena","symbol":"thg","name":"Thetan Arena"},{"id":"thetan-coin","symbol":"thc","name":"Thetan Coin"},{"id":"theta-token","symbol":"theta","name":"Theta Network"},{"id":"the-three-kingdoms","symbol":"ttk","name":"The Three Kingdoms"},{"id":"the-tokenized-bitcoin","symbol":"imbtc","name":"The Tokenized Bitcoin"},{"id":"the-troller-coin","symbol":"troller","name":"The Troller Coin"},{"id":"the-virtua-kolect","symbol":"tvk","name":"The Virtua Kolect"},{"id":"the-wasted-lands","symbol":"wal","name":"The Wasted Lands"},{"id":"the-web3-project","symbol":"twep","name":"The Web3 Project"},{"id":"the-winkyverse","symbol":"wnk","name":"The Winkyverse"},{"id":"the-world-state","symbol":"w$c","name":"The World State"},{"id":"the-xenobots-project","symbol":"xeno","name":"The Xenobots Project"},{"id":"the-youth-pay","symbol":"typ","name":"The Youth Pay"},{"id":"thingschain","symbol":"tic","name":"Thingschain"},{"id":"thol-token","symbol":"thol","name":"AngelBlock"},{"id":"thor","symbol":"thor","name":"ThorFi"},{"id":"thorchain","symbol":"rune","name":"THORChain"},{"id":"thorchain-erc20","symbol":"rune","name":"THORChain (ERC20)"},{"id":"thoreum-v2","symbol":"thoreum","name":"Thoreum V3"},{"id":"thors-mead","symbol":"mead","name":"Thors Mead"},{"id":"thorstarter","symbol":"xrune","name":"Thorstarter"},{"id":"thorswap","symbol":"thor","name":"THORSwap"},{"id":"thorus","symbol":"tho","name":"Thorus"},{"id":"thorwallet","symbol":"tgt","name":"THORWallet DEX"},{"id":"thought","symbol":"tht","name":"Thought"},{"id":"threefold-token","symbol":"tft","name":"ThreeFold"},{"id":"threshold-network-token","symbol":"t","name":"Threshold Network"},{"id":"throne","symbol":"thn","name":"Throne"},{"id":"thrupenny","symbol":"tpy","name":"Thrupenny"},{"id":"thunderbnb","symbol":"thunderbnb","name":"ThunderBNB"},{"id":"thunder-lands","symbol":"tndr","name":"Thunder Lands"},{"id":"thunder-token","symbol":"tt","name":"ThunderCore"},{"id":"thx-network","symbol":"thx","name":"THX Network"},{"id":"thxone","symbol":"thx","name":"thxone"},{"id":"tia","symbol":"tia","name":"TIA"},{"id":"ticket-finance","symbol":"ticket","name":"Ticket Finance"},{"id":"tickr","symbol":"tickr","name":"Tickr"},{"id":"tidal-finance","symbol":"tidal","name":"Tidal Finance"},{"id":"tidefi","symbol":"tdfy","name":"Tidefi"},{"id":"tidex-token","symbol":"tdx","name":"Tidex"},{"id":"tierion","symbol":"tnt","name":"Tierion"},{"id":"tifi-token","symbol":"tifi","name":"TiFi"},{"id":"tigercash","symbol":"tch","name":"TigerCash"},{"id":"tiger-king","symbol":"tking","name":"Tiger King Coin"},{"id":"tigerqueen","symbol":"tqueen","name":"TigerQueen"},{"id":"tiger-scrub-money-2","symbol":"tiger","name":"Tiger Scrub Money"},{"id":"tiger-trade","symbol":"ti̇gr","name":"Tiger.Trade"},{"id":"tigres-fan-token","symbol":"tigres","name":"Tigres Fan Token"},{"id":"tiki-token","symbol":"tiki","name":"Tiki"},{"id":"tikky-inu","symbol":"tikky","name":"Tikky Inu"},{"id":"tillage","symbol":"till","name":"Tillage"},{"id":"tilwiki","symbol":"tlw","name":"TilWiki"},{"id":"timechain-swap-token","symbol":"tcs","name":"Timechain Swap"},{"id":"timeleap-finance","symbol":"time","name":"Timeleap Finance"},{"id":"timeless","symbol":"lit","name":"Timeless"},{"id":"time-new-bank","symbol":"tnb","name":"Time New Bank"},{"id":"timeseries-ai","symbol":"timeseries","name":"Timeseries AI"},{"id":"tiny-bonez","symbol":"t1ny","name":"Tiny Bonez"},{"id":"tiny-coin","symbol":"tinc","name":"Tiny Coin"},{"id":"tiny-colony","symbol":"tiny","name":"Tiny Colony"},{"id":"tipo-token","symbol":"tipo","name":"TIPO Token"},{"id":"tipsycoin","symbol":"$tipsy","name":"TipsyCoin"},{"id":"tiraverse","symbol":"tvrs","name":"TiraVerse"},{"id":"titan-coin","symbol":"ttn","name":"Titan Coin"},{"id":"titan-hunters","symbol":"tita","name":"Titan Hunters"},{"id":"titania-token","symbol":"titania","name":"Titania"},{"id":"titano","symbol":"titano","name":"Titano"},{"id":"titanswap","symbol":"titan","name":"TitanSwap"},{"id":"titi-financial","symbol":"titi","name":"Titi Financial"},{"id":"titi-protocol","symbol":"titi","name":"TiTi Protocol"},{"id":"title-network","symbol":"tnet","name":"Bitcoin Clashic"},{"id":"tlabs","symbol":"tbs","name":"TLabs"},{"id":"tlchain","symbol":"tlc","name":"TLChain"},{"id":"tlpt","symbol":"tlpt","name":"tLPT"},{"id":"t-mac-dao","symbol":"tmg","name":"T-mac DAO"},{"id":"tnc-coin","symbol":"tnc","name":"TNC Coin"},{"id":"tnns","symbol":"tnns","name":"TNNS"},{"id":"tokamak-network","symbol":"ton","name":"Tokamak Network"},{"id":"tokemak","symbol":"toke","name":"Tokemak"},{"id":"tokenasset","symbol":"ntb","name":"TokenAsset"},{"id":"tokenbank","symbol":"tbank","name":"Tokenbank"},{"id":"tokenbook","symbol":"tbk","name":"TokenBook"},{"id":"tokenbot","symbol":"tkb","name":"TokenBot"},{"id":"tokencard","symbol":"tkn","name":"Monolith"},{"id":"tokenclub","symbol":"tct","name":"TokenClub"},{"id":"tokendesk","symbol":"tds","name":"TokenDesk"},{"id":"token-dforce-usd","symbol":"usx","name":"dForce USD"},{"id":"token-engineering-commons","symbol":"tec","name":"Token Engineering Commons"},{"id":"tokengo","symbol":"gpt","name":"GoPower"},{"id":"tokenize-xchange","symbol":"tkx","name":"Tokenize Xchange"},{"id":"token-kennel","symbol":"kennel","name":"KennelSphere"},{"id":"tokenlon","symbol":"lon","name":"Tokenlon"},{"id":"token-of-fire","symbol":"rhllor","name":"Token of Fire"},{"id":"tokenoid","symbol":"noid","name":"Tokenoid"},{"id":"tokenomy","symbol":"ten","name":"Tokenomy"},{"id":"tokenplace","symbol":"tok","name":"Tokenplace"},{"id":"tokenplay","symbol":"top","name":"Tokenplay"},{"id":"token-pocket","symbol":"tpt","name":"TokenPocket Token"},{"id":"token-runner","symbol":"tkrn","name":"Token Runner"},{"id":"token-shelby","symbol":"tsy","name":"Token Shelby"},{"id":"tokerr","symbol":"tokr","name":"Tokerr"},{"id":"tokhit","symbol":"hitt","name":"TOKHIT"},{"id":"toko","symbol":"toko","name":"Tokoin"},{"id":"tokocrypto","symbol":"tko","name":"Tokocrypto"},{"id":"tokpie","symbol":"tkp","name":"TOKPIE"},{"id":"toku","symbol":"toku","name":"Toku"},{"id":"tokyo","symbol":"tokc","name":"Tokyo Coin"},{"id":"tokyo-au","symbol":"tokau","name":"Tokyo AU"},{"id":"tokyo-inu","symbol":"toki","name":"Tokyo Inu"},{"id":"tolar","symbol":"tol","name":"Tolar"},{"id":"tomato-coin","symbol":"bptc","name":"Tomato Coin"},{"id":"tomb","symbol":"tomb","name":"Tomb"},{"id":"tomb-shares","symbol":"tshare","name":"Tomb Shares"},{"id":"tomcat","symbol":"tcat","name":"TomCat"},{"id":"tom-coin","symbol":"tmc","name":"Tom Coin"},{"id":"tom-finance","symbol":"tom","name":"TOM Finance"},{"id":"tominet","symbol":"tomi","name":"tomiNet"},{"id":"tomochain","symbol":"tomo","name":"TomoChain"},{"id":"tomoe","symbol":"tomoe","name":"TomoChain ERC-20"},{"id":"tomtomcoin","symbol":"toms","name":"TomTomCoin"},{"id":"tonestra","symbol":"tnr","name":"Tonestra"},{"id":"tongtong-coin","symbol":"ttc","name":"Tongtong Coin"},{"id":"tonstarter","symbol":"tos","name":"TONStarter"},{"id":"tontoken","symbol":"ton","name":"TON Community"},{"id":"toobcoin","symbol":"toob","name":"Toobcoin"},{"id":"tools","symbol":"tools","name":"TOOLS"},{"id":"topbidder","symbol":"bid","name":"TopBidder"},{"id":"topchain","symbol":"topc","name":"TopChain"},{"id":"topg-coin","symbol":"topg","name":"TopG Coin"},{"id":"topgoal","symbol":"goal","name":"TopGoal"},{"id":"topmanager","symbol":"tmt","name":"TopManager"},{"id":"top-network","symbol":"top","name":"TOP Network"},{"id":"topshelf-finance","symbol":"liqr","name":"Topshelf Finance"},{"id":"tor","symbol":"tor","name":"TOR"},{"id":"tora","symbol":"tora","name":"TORA"},{"id":"tora-inu","symbol":"tora","name":"Tora Inu"},{"id":"torekko","symbol":"trk","name":"Torekko"},{"id":"torg","symbol":"torg","name":"TORG"},{"id":"torkpad","symbol":"tpad","name":"TorkPad"},{"id":"tornado-cash","symbol":"torn","name":"Tornado Cash"},{"id":"torpedo","symbol":"torpedo","name":"Torpedo"},{"id":"tortuga-staked-aptos","symbol":"tapt","name":"Tortuga Staked Aptos"},{"id":"torum","symbol":"xtm","name":"Torum"},{"id":"tor-wallet","symbol":"tor","name":"Tor Wallet"},{"id":"tosa-inu","symbol":"tos","name":"Tosa Inu"},{"id":"tosdis","symbol":"dis","name":"TosDis"},{"id":"toshi-token","symbol":"toshi","name":"Toshimon"},{"id":"toshi-tools","symbol":"toshi","name":"Toshi Tools"},{"id":"tosidrop","symbol":"ctosi","name":"TosiDrop"},{"id":"total-crypto-market-cap-token","symbol":"tcap","name":"Total Crypto Market Cap"},{"id":"totemfi","symbol":"totm","name":"TotemFi"},{"id":"to-the-moon-token","symbol":"ton","name":"To The Moon Token"},{"id":"totocat","symbol":"totocat","name":"Totocat"},{"id":"totoro-inu","symbol":"totoro","name":"Totoro Inu"},{"id":"toucan-protocol-base-carbon-tonne","symbol":"bct","name":"Toucan Protocol: Base Carbon Tonne"},{"id":"toucan-protocol-nature-carbon-tonne","symbol":"nct","name":"Toucan Protocol: Nature Carbon Tonne"},{"id":"touchcon","symbol":"toc","name":"TouchCon"},{"id":"tourismx","symbol":"trmx","name":"TourismX"},{"id":"tourist-shiba-inu","symbol":"tourists","name":"Tourist Shiba Inu"},{"id":"tower","symbol":"tower","name":"Tower"},{"id":"town-star","symbol":"town","name":"Town Star"},{"id":"toxicdeer-finance","symbol":"deer","name":"ToxicDeer Finance"},{"id":"toxicdeer-share","symbol":"xdshare","name":"ToxicDeer Share"},{"id":"tpro","symbol":"tpro","name":"TPRO"},{"id":"tp-swap","symbol":"tp","name":"Token Swap"},{"id":"tr3zor","symbol":"tr3","name":"Tr3zor"},{"id":"trabzonspor-fan-token","symbol":"tra","name":"Trabzonspor Fan Token"},{"id":"trace-network-labs","symbol":"trace","name":"Trace Network Labs"},{"id":"tracer","symbol":"trc","name":"Tracer"},{"id":"tracer-dao","symbol":"tcr","name":"Tracer DAO"},{"id":"tractor-joe","symbol":"tractor","name":"Tractor Joe"},{"id":"tradao","symbol":"tod","name":"Trava Capital"},{"id":"trade-fighter","symbol":"tdf","name":"Trade Fighter"},{"id":"tradeflow","symbol":"tflow","name":"TradeFlow"},{"id":"traders-coin","symbol":"trdc","name":"Traders Coin"},{"id":"tradestars","symbol":"tsx","name":"TradeStars"},{"id":"trade-tech-ai","symbol":"ttai","name":"Trade Tech AI"},{"id":"tradewix","symbol":"wix","name":"TradeWix"},{"id":"tradix","symbol":"tx","name":"Tradix"},{"id":"tranche-finance","symbol":"slice","name":"Tranche Finance"},{"id":"tranchess","symbol":"chess","name":"Tranchess"},{"id":"tranquil-finance","symbol":"tranq","name":"Tranquil Finance"},{"id":"tranquility-city","symbol":"lumen","name":"Tranquility City"},{"id":"tranquil-staked-one","symbol":"stone","name":"Tranquil Staked ONE"},{"id":"transcodium","symbol":"tns","name":"Transcodium"},{"id":"transhuman-coin","symbol":"thc","name":"Transhuman Coin"},{"id":"transient","symbol":"tsct","name":"Transient"},{"id":"tratok","symbol":"trat","name":"Tratok"},{"id":"trava-finance","symbol":"trava","name":"Trava Finance"},{"id":"travel-care-2","symbol":"travel","name":"Travel Care"},{"id":"travgopv","symbol":"tpv","name":"TravGoPV"},{"id":"traxx","symbol":"traxx","name":"Traxx"},{"id":"trazable","symbol":"trz","name":"Trazable"},{"id":"treasure-token-finance","symbol":"trove","name":"TroveDAO"},{"id":"treasure-under-sea","symbol":"tus","name":"Treasure Under Sea"},{"id":"treasury-bond-eth-tokenized-stock-defichain","symbol":"dtlt","name":"iShares 20+ Year Treasury Bond ETF Defichain"},{"id":"treat","symbol":"treat","name":"Treat"},{"id":"treatdao-v2","symbol":"treat","name":"TreatDAO"},{"id":"treeb","symbol":"treeb","name":"Retreeb"},{"id":"treecle","symbol":"trcl","name":"Treecle"},{"id":"trellis","symbol":"treis","name":"Trellis"},{"id":"trendai","symbol":"trendai","name":"TrendAI"},{"id":"trendering","symbol":"trnd","name":"Trendering"},{"id":"trendsy","symbol":"trndz","name":"Trendsy"},{"id":"trend-x","symbol":"trendx","name":"Trend X"},{"id":"trezarcoin","symbol":"tzc","name":"TrezarCoin"},{"id":"triall","symbol":"trl","name":"Triall"},{"id":"trias-token","symbol":"trias","name":"TriasLab"},{"id":"tribalpunk-cryptoverse","symbol":"anta","name":"Tribalpunk Cryptoverse"},{"id":"tribal-token","symbol":"tribl","name":"Tribal Token"},{"id":"tribar","symbol":"xtri","name":"Tribar"},{"id":"tribe-2","symbol":"tribe","name":"Tribe"},{"id":"tribeone","symbol":"haka","name":"TribeOne"},{"id":"tribe-token","symbol":"tribex","name":"Tribe Token"},{"id":"trice","symbol":"tri","name":"Trice"},{"id":"trickle","symbol":"h2o","name":"Trickle"},{"id":"tridentdao","symbol":"psi","name":"TridentDAO"},{"id":"triflex-token","symbol":"trfx","name":"Triflex"},{"id":"triipmiles","symbol":"tiim","name":"TriipMiles"},{"id":"trillium","symbol":"tt","name":"Trillium"},{"id":"trinity-network-credit","symbol":"tnc","name":"Trinity Network Credit"},{"id":"trinity-swap","symbol":"trinity","name":"Trinity Swap"},{"id":"trips-community","symbol":"trips","name":"Trips Community"},{"id":"trism","symbol":"trism","name":"Trism"},{"id":"trisolaris","symbol":"tri","name":"Trisolaris"},{"id":"triton","symbol":"xeq","name":"Equilibria"},{"id":"triumphx","symbol":"trix","name":"TriumphX"},{"id":"triveum","symbol":"trv","name":"Triveum"},{"id":"trivian","symbol":"trivia","name":"Trivians"},{"id":"trolite","symbol":"trl","name":"Trolite"},{"id":"trollbox","symbol":"tox","name":"trollbox"},{"id":"tron","symbol":"trx","name":"TRON"},{"id":"tronai","symbol":"tai","name":"TronAI"},{"id":"tronbetlive","symbol":"live","name":"TRONbetLive"},{"id":"tron-bsc","symbol":"trx","name":"TRON (BSC)"},{"id":"tronclassic","symbol":"trxc","name":"TronClassic"},{"id":"troneuroperewardcoin","symbol":"terc","name":"TronEuropeRewardCoin"},{"id":"tronpad","symbol":"tronpad","name":"TRONPAD"},{"id":"tropical-finance","symbol":"daiquiri","name":"Tropical Finance"},{"id":"troy","symbol":"troy","name":"TROY"},{"id":"trubadger","symbol":"trubgr","name":"TruBadger"},{"id":"truebit-protocol","symbol":"tru","name":"Truebit Protocol"},{"id":"true-chain","symbol":"true","name":"TrueChain"},{"id":"truecnh","symbol":"tcnh","name":"TrueCNH"},{"id":"truedeck","symbol":"tdp","name":"TrueDeck"},{"id":"truefeedbackchain","symbol":"tfbx","name":"Truefeedback"},{"id":"truefi","symbol":"tru","name":"TrueFi"},{"id":"truefreeze","symbol":"frz","name":"TrueFreeze"},{"id":"true-pnl","symbol":"pnl","name":"True PNL"},{"id":"true-usd","symbol":"tusd","name":"TrueUSD"},{"id":"trustbase","symbol":"tbe","name":"TrustBase"},{"id":"trusted-node","symbol":"tnode","name":"Trusted Node"},{"id":"trustfi-network-token","symbol":"tfi","name":"TrustFi Network"},{"id":"trustnft","symbol":"trustnft","name":"TrustNFT"},{"id":"trustpad","symbol":"tpad","name":"TrustPad"},{"id":"trustpay","symbol":"tph","name":"Trustpay"},{"id":"trustrise","symbol":"trise","name":"TrustRise"},{"id":"trustswap","symbol":"swap","name":"Trustswap"},{"id":"trustverse","symbol":"trv","name":"TrustVerse"},{"id":"trust-wallet-token","symbol":"twt","name":"Trust Wallet"},{"id":"trustworks","symbol":"trust","name":"Trustworks"},{"id":"truth-seekers","symbol":"truth","name":"Truth Seekers"},{"id":"trx3l","symbol":"trx3l","name":"TRX3L"},{"id":"tryc","symbol":"tryc","name":"TRYC"},{"id":"tryhards","symbol":"try","name":"TryHards"},{"id":"tryvium-2","symbol":"tryv","name":"Tryvium"},{"id":"tsilver","symbol":"txag","name":"tSILVER"},{"id":"tsuki-inu","symbol":"tkinu","name":"Tsuki Inu"},{"id":"tsuki-no-usagi","symbol":"gyokuto","name":"Tsuki no usagi"},{"id":"tsukuyomi-no-mikoto","symbol":"mikoto","name":"Tsukuyomi-no-Mikoto"},{"id":"tsuzuki-inu","symbol":"tzki","name":"Tsuzuki Inu"},{"id":"ttcoin","symbol":"tc","name":"TTcoin"},{"id":"ttc-protocol","symbol":"maro","name":"Maro"},{"id":"ttx-metaverse","symbol":"xmeta","name":"TTX Metaverse"},{"id":"tudabirds","symbol":"burd","name":"tudaBirds"},{"id":"tuf-token","symbol":"tuf","name":"TUF Token"},{"id":"tundra-token","symbol":"tundra","name":"Tundra"},{"id":"tune-fm","symbol":"jam","name":"Tune.Fm"},{"id":"tune-token","symbol":"tune","name":"TUNE TOKEN"},{"id":"tupan","symbol":"tupan","name":"Tupan"},{"id":"turbo-wallet","symbol":"turbo","name":"Turbo Wallet"},{"id":"turex","symbol":"tur","name":"Turex"},{"id":"turismo-ai","symbol":"turai","name":"Turismo AI"},{"id":"turkiye-basketbol-federasyonu-token","symbol":"tbft","name":"Türkiye Basketbol Federasyonu Fan Token"},{"id":"turkiye-motosiklet-federasyonu-fan-token","symbol":"tmft","name":"Türkiye Motosiklet Federasyonu Fan Token"},{"id":"turk-shiba","symbol":"tushi","name":"Turk Shiba"},{"id":"turtlecoin","symbol":"trtl","name":"TurtleCoin"},{"id":"turtles-token","symbol":"trtls","name":"Turtles"},{"id":"tusd-yvault","symbol":"yvtusd","name":"TUSD yVault"},{"id":"tutela","symbol":"tutl","name":"Tutela"},{"id":"tutellus","symbol":"tut","name":"Tutellus"},{"id":"tutti-frutti-finance","symbol":"tff","name":"Tutti Frutti"},{"id":"tuzlaspor","symbol":"tuzla","name":"Tuzlaspor Token"},{"id":"tvt","symbol":"tvt","name":"TVT"},{"id":"twelve-legions","symbol":"ctl","name":"Twelve Legions"},{"id":"twirl-governance-token","symbol":"tgt","name":"Twirl Governance"},{"id":"twitfi","symbol":"twt","name":"Twitfi"},{"id":"twitter-ceo-floki","symbol":"flokiceo","name":"Twitter CEO Floki"},{"id":"twitterx","symbol":"twitterx","name":"TwitterX"},{"id":"twoge-inu","symbol":"twoge","name":"Twoge Inu"},{"id":"two-monkey-juice-bar","symbol":"$tmon","name":"Two Monkey Juice Bar"},{"id":"two-paws","symbol":"twopaw","name":"Two Paws"},{"id":"txa","symbol":"txa","name":"TXA"},{"id":"txbit","symbol":"txbit","name":"Txbit"},{"id":"tycoon","symbol":"tyc","name":"Tycoon"},{"id":"typerium","symbol":"type","name":"Typerium"},{"id":"tyv","symbol":"tyv","name":"TYV"},{"id":"tzbtc","symbol":"tzbtc","name":"tzBTC"},{"id":"uangmarket","symbol":"uang","name":"UangMarket"},{"id":"ubeswap","symbol":"ube","name":"Ubeswap"},{"id":"ubiq","symbol":"ubq","name":"Ubiq"},{"id":"ubix-network","symbol":"ubx","name":"UBIX Network"},{"id":"ubxs-token","symbol":"ubxs","name":"UBXS"},{"id":"uca","symbol":"uca","name":"UCA Coin"},{"id":"ucash","symbol":"ucash","name":"U.CASH"},{"id":"uconetwork","symbol":"ucoil","name":"UCONetwork"},{"id":"ucrowdme","symbol":"ucm","name":"UCROWDME"},{"id":"ucx","symbol":"ucx","name":"UCX"},{"id":"udder-chaos-milk","symbol":"milk","name":"MILK"},{"id":"udinese-calcio-fan-token","symbol":"udi","name":"Udinese Calcio Fan Token"},{"id":"uerii","symbol":"uerii","name":"UERII"},{"id":"ufc-fan-token","symbol":"ufc","name":"UFC Fan Token"},{"id":"ufocoin","symbol":"ufo","name":"Uniform Fiscal Object"},{"id":"ufo-gaming","symbol":"ufo","name":"UFO Gaming"},{"id":"uhive","symbol":"hve2","name":"Uhive"},{"id":"uka-doge-coin","symbol":"udoge","name":"Uka Doge Coin"},{"id":"ukrainedao-flag-nft","symbol":"love","name":"UkraineDAO Flag NFT"},{"id":"ulanco","symbol":"uac","name":"Ulanco"},{"id":"uland","symbol":"uland","name":"ULAND"},{"id":"ulord","symbol":"ut","name":"Ulord"},{"id":"ultimate-champions","symbol":"champ","name":"Ultimate Champions"},{"id":"ultimogg","symbol":"ultgg","name":"UltimoGG"},{"id":"ultra","symbol":"uos","name":"Ultra"},{"id":"ultra-clear","symbol":"ucr","name":"Ultra Clear"},{"id":"ultragate","symbol":"ulg","name":"Ultragate"},{"id":"ultrain","symbol":"ugas","name":"Ultrain"},{"id":"ultramoc","symbol":"umc","name":"Ultramoc"},{"id":"ultra-nft","symbol":"unft","name":"Ultra NFT"},{"id":"ultrasafe","symbol":"ultra","name":"UltraSafe"},{"id":"ultron","symbol":"ulx","name":"ULTRON"},{"id":"uma","symbol":"uma","name":"UMA"},{"id":"umami-finance","symbol":"umami","name":"Umami"},{"id":"umbra-network","symbol":"umbr","name":"Umbria Network"},{"id":"umbrellacoin","symbol":"umc","name":"Umbrella Coin"},{"id":"umbrella-network","symbol":"umb","name":"Umbrella Network"},{"id":"umee","symbol":"umee","name":"Umee"},{"id":"umetaworld","symbol":"umw","name":"UMetaWorld"},{"id":"umi-digital","symbol":"umi","name":"Umi Digital"},{"id":"unagii-dai","symbol":"udai","name":"Unagii Dai"},{"id":"unagii-eth","symbol":"ueth","name":"Unagii ETH"},{"id":"unagii-tether-usd","symbol":"uusdt","name":"Unagii Tether USD"},{"id":"unagii-usd-coin","symbol":"uusdc","name":"Unagii USD Coin"},{"id":"unagii-wrapped-bitcoin","symbol":"uwbtc","name":"Unagii Wrapped Bitcoin"},{"id":"unbanked","symbol":"unbnk","name":"Unbanked"},{"id":"unbound-finance","symbol":"unb","name":"Unbound Finance"},{"id":"uncl","symbol":"uncl","name":"UNCL"},{"id":"unclemine","symbol":"um","name":"UncleMine"},{"id":"undead-blocks","symbol":"undead","name":"Undead Blocks"},{"id":"undead-finance","symbol":"undead","name":"Undead Finance"},{"id":"u-network","symbol":"uuu","name":"U Network"},{"id":"unfederalreserve","symbol":"ersdl","name":"unFederalReserve"},{"id":"unia-farms","symbol":"unia","name":"UNIA Farms"},{"id":"unibright","symbol":"ubt","name":"Unibright"},{"id":"unicly","symbol":"unic","name":"Unicly"},{"id":"unicly-cryptopunks-collection","symbol":"upunk","name":"Unicly CryptoPunks Collection"},{"id":"unicly-fewocious-collection","symbol":"ufewo","name":"Unicly Fewocious Collection"},{"id":"unicorn-milk","symbol":"unim","name":"Unicorn Milk"},{"id":"unicorn-token","symbol":"uni","name":"UNICORN"},{"id":"unicrypt-2","symbol":"uncx","name":"UNCX Network"},{"id":"unidef","symbol":"u","name":"Unidef"},{"id":"unidex","symbol":"unidx","name":"UniDex"},{"id":"unido-ep","symbol":"udo","name":"Unido"},{"id":"unidogefinance-token","symbol":"unido","name":"UnidogeFinance Token"},{"id":"unifarm","symbol":"ufarm","name":"UniFarm"},{"id":"unifees","symbol":"fees","name":"Unifees"},{"id":"unifi","symbol":"unifi","name":"Covenants"},{"id":"unification","symbol":"fund","name":"Unification"},{"id":"unifi-protocol","symbol":"up","name":"UniFi Protocol"},{"id":"unifi-protocol-dao","symbol":"unfi","name":"Unifi Protocol DAO"},{"id":"unifty","symbol":"nif","name":"Unifty"},{"id":"unifund","symbol":"ifund","name":"Unifund"},{"id":"unilab-network","symbol":"ulab","name":"Unilab"},{"id":"unilayer","symbol":"layer","name":"UniLayer"},{"id":"unilock-network-2","symbol":"unl","name":"Unilock.Network"},{"id":"unimex-network","symbol":"umx","name":"UniMex Network"},{"id":"unimoon-umoon","symbol":"umoon","name":"Unimoon"},{"id":"union-protocol-governance-token","symbol":"unn","name":"UNION Protocol Governance"},{"id":"unipilot","symbol":"pilot","name":"Unipilot"},{"id":"uniplay","symbol":"unp","name":"UniPlay"},{"id":"unipower","symbol":"power","name":"UniPower"},{"id":"uniqly","symbol":"uniq","name":"Uniqly"},{"id":"uniqo","symbol":"uniqo","name":"Uniqo"},{"id":"unique-fans","symbol":"fans","name":"Unique Fans"},{"id":"unique-network","symbol":"unq","name":"Unique Network"},{"id":"unique-one","symbol":"rare","name":"Unique One"},{"id":"unique-utility-token","symbol":"unqt","name":"Unique Utility"},{"id":"unisocks","symbol":"socks","name":"Unisocks"},{"id":"unistake","symbol":"unistake","name":"Unistake"},{"id":"uniswap","symbol":"uni","name":"Uniswap"},{"id":"uniswap-wormhole","symbol":"uni","name":"Uniswap (Wormhole)"},{"id":"unite","symbol":"unite","name":"Unite"},{"id":"unitech","symbol":"utc","name":"Unitech"},{"id":"unitedcrowd","symbol":"uct","name":"UnitedCrowd"},{"id":"united-emirate-decentralized-coin","symbol":"uedc","name":"United Emirate Decentralized Coin"},{"id":"united-states-property-coin","symbol":"uspc","name":"United States Property Coin"},{"id":"united-token","symbol":"uted","name":"United"},{"id":"united-traders-token","symbol":"utt","name":"United Traders"},{"id":"unit-network","symbol":"unit","name":"Unit Network"},{"id":"unit-protocol-duck","symbol":"duck","name":"Unit Protocol"},{"id":"unitrade","symbol":"trade","name":"Unitrade"},{"id":"unitus","symbol":"uis","name":"Unitus"},{"id":"unity-network","symbol":"unt","name":"Unity Network"},{"id":"unityventures","symbol":"uv","name":"Unityventures"},{"id":"unium","symbol":"unm","name":"UNIUM"},{"id":"universal-basic-income","symbol":"ubi","name":"Universal Basic Income"},{"id":"universal-basic-offset","symbol":"ubo","name":"Universal Basic Offset"},{"id":"universal-eth","symbol":"unieth","name":"Universal ETH"},{"id":"universal-floki-coin","symbol":"ufloki","name":"Universal Floki Coin"},{"id":"universal-liquidity-union","symbol":"ulu","name":"Universal Liquidity Union"},{"id":"universal-pickle","symbol":"$upl","name":"Universal Pickle"},{"id":"universe-coin","symbol":"unis","name":"Universe Coin"},{"id":"universe-island","symbol":"uim","name":"Universe Island"},{"id":"universe-xyz","symbol":"xyz","name":"Universe.XYZ"},{"id":"universidad-de-chile-fan-token","symbol":"uch","name":"Universidad de Chile Fan Token"},{"id":"uniwhale","symbol":"unw","name":"Uniwhale"},{"id":"uniwhales","symbol":"uwl","name":"UniWhales"},{"id":"uniworld","symbol":"unw","name":"UniWorld"},{"id":"uniwswap","symbol":"uniw","name":"UniWswap"},{"id":"unix","symbol":"unix","name":"UniX"},{"id":"uni-yvault","symbol":"yvuni","name":"UNI yVault"},{"id":"unizen","symbol":"zcx","name":"Unizen"},{"id":"unkai","symbol":"unkai","name":"Unkai"},{"id":"unlend-finance","symbol":"uft","name":"UniLend Finance"},{"id":"unlimitedip","symbol":"uip","name":"UnlimitedIP"},{"id":"unlock","symbol":"unlock","name":"UNLOCK"},{"id":"unlock-protocol","symbol":"udt","name":"Unlock Protocol"},{"id":"unmarshal","symbol":"marsh","name":"Unmarshal"},{"id":"unobtanium","symbol":"uno","name":"Unobtanium"},{"id":"unobtanium-tezos","symbol":"uno","name":"Unobtanium Tezos"},{"id":"uno-re","symbol":"uno","name":"Uno Re"},{"id":"unq","symbol":"unq","name":"Unique Venture clubs"},{"id":"unreal-finance","symbol":"ugt","name":"Unreal Finance"},{"id":"unsheth","symbol":"ush","name":"unshETH"},{"id":"unslashed-finance","symbol":"usf","name":"Unslashed Finance"},{"id":"uns-token","symbol":"uns","name":"UNS Token"},{"id":"unvest","symbol":"unv","name":"Unvest"},{"id":"upbots","symbol":"ubxn","name":"UpBots"},{"id":"upcoin","symbol":"upcoin","name":"Upcoin"},{"id":"updog","symbol":"updog","name":"UpDog"},{"id":"upfi-network","symbol":"ups","name":"UPFI Network"},{"id":"upfire","symbol":"upr","name":"Upfire"},{"id":"upfiring","symbol":"ufr","name":"Upfiring"},{"id":"uplexa","symbol":"upx","name":"uPlexa"},{"id":"uplift","symbol":"lift","name":"Uplift"},{"id":"uponly-token","symbol":"upo","name":"UpOnly"},{"id":"upshib","symbol":"upshib","name":"upShib"},{"id":"upsorber","symbol":"up","name":"Upsorber"},{"id":"up-spiral","symbol":"spiral","name":"Spiral"},{"id":"upstabletoken","symbol":"ustx","name":"UpStable"},{"id":"uquid-coin","symbol":"uqc","name":"Uquid Coin"},{"id":"uramaki","symbol":"maki","name":"Uramaki"},{"id":"uraniumx","symbol":"urx","name":"UraniumX"},{"id":"ureeqa","symbol":"urqa","name":"UREEQA"},{"id":"urubit","symbol":"urub","name":"Urubit"},{"id":"urust-global","symbol":"urust","name":"Urust Global"},{"id":"urus-token","symbol":"urus","name":"Aurox"},{"id":"usd","symbol":"usd+","name":"USD+"},{"id":"usd-balance","symbol":"usdb","name":"USD Balance"},{"id":"usd-bancor","symbol":"usdb","name":"USD Bancor"},{"id":"usd-coin","symbol":"usdc","name":"USD Coin"},{"id":"usd-coin-avalanche-bridged-usdc-e","symbol":"usdc","name":"USD Coin Avalanche Bridged (USDC.e)"},{"id":"usd-coin-celer","symbol":"ceusdc","name":"USD Coin - Celer"},{"id":"usd-coin-nomad","symbol":"nomadusdc","name":"USD Coin - Nomad"},{"id":"usd-coin-plenty-bridge","symbol":"usdc.e","name":"USD Coin (Plenty Bridge)"},{"id":"usd-coin-pos-wormhole","symbol":"usdcpo","name":"USD Coin (PoS) (Wormhole)"},{"id":"usd-coin-wormhole-from-ethereum","symbol":"usdcet","name":"USD Coin (Wormhole from Ethereum)"},{"id":"usdc-rainbow-bridge","symbol":"usdc.e","name":"USD Coin (Rainbow Bridge)"},{"id":"usdc-yvault","symbol":"yvusdc","name":"USDC yVault"},{"id":"usdd","symbol":"usdd","name":"USDD"},{"id":"usdex-stablecoin","symbol":"usdex","name":"USDEX"},{"id":"usd-freedom","symbol":"usdf","name":"USD Freedom"},{"id":"usdh","symbol":"usdh","name":"USDH"},{"id":"usdk","symbol":"usdk","name":"USDK"},{"id":"usd-mars","symbol":"usdm","name":"USD Mars"},{"id":"usdo","symbol":"usdo","name":"USDO"},{"id":"usdp","symbol":"usdp","name":"USDP Stablecoin"},{"id":"usdtez","symbol":"usdtz","name":"USDtez"},{"id":"usdt-yvault","symbol":"yvusdt","name":"USDT yVault"},{"id":"usdx","symbol":"usdx","name":"USDX"},{"id":"usd-zee","symbol":"usdz","name":"USD ZEE"},{"id":"useless-2","symbol":"use","name":"Useless"},{"id":"usgold","symbol":"usg","name":"USGold"},{"id":"ushare","symbol":"ushare","name":"USHARE"},{"id":"ushark","symbol":"usha","name":"uShark"},{"id":"ushi","symbol":"ushi","name":"Ushi"},{"id":"usk","symbol":"usk","name":"USK"},{"id":"usp","symbol":"usp","name":"USP"},{"id":"utility-ape","symbol":"$banana","name":"Utility Ape"},{"id":"utility-web3shot","symbol":"uw3s","name":"Utility Web3Shot"},{"id":"utip","symbol":"utip","name":"uTip"},{"id":"utopia","symbol":"crp","name":"Crypton"},{"id":"utopia-usd","symbol":"uusd","name":"Utopia USD"},{"id":"utrust","symbol":"utk","name":"Utrust"},{"id":"utu-coin","symbol":"utu","name":"UTU Coin"},{"id":"uvtoken","symbol":"uvt","name":"UvToken"},{"id":"uwu-lend","symbol":"uwu","name":"UwU Lend"},{"id":"uxd-protocol-token","symbol":"uxp","name":"UXD Protocol"},{"id":"uxd-stablecoin","symbol":"uxd","name":"UXD Stablecoin"},{"id":"uzumaki-inu","symbol":"uzumaki","name":"Uzumaki Inu"},{"id":"uzurocks","symbol":"uzrs","name":"UZUROCKS"},{"id":"v3s-share","symbol":"vshare","name":"V3S Share"},{"id":"vabble","symbol":"vab","name":"Vabble"},{"id":"vader-protocol","symbol":"vader","name":"Vader Protocol"},{"id":"vagabond","symbol":"vgo","name":"Vagabond"},{"id":"vai","symbol":"vai","name":"Vai"},{"id":"vaiot","symbol":"vai","name":"Vaiot"},{"id":"valas-finance","symbol":"valas","name":"Valas Finance"},{"id":"valencia-cf-fan-token","symbol":"vcf","name":"Valencia CF Fan Token"},{"id":"valentine-floki","symbol":"flov","name":"Valentine Floki"},{"id":"valobit","symbol":"vbit","name":"VALOBIT"},{"id":"value-finance","symbol":"vft","name":"Value Finance"},{"id":"value-liquidity","symbol":"value","name":"Value DeFi"},{"id":"vancat","symbol":"vancat","name":"Vancat [OLD]"},{"id":"vancat-2","symbol":"vancat","name":"Vancat"},{"id":"vanguard-real-estate-tokenized-stock-defichain","symbol":"dvnq","name":"Vanguard Real Estate Tokenized Stock Defichain"},{"id":"vanguard-sp-500-etf-tokenized-stock-defichain","symbol":"dvoo","name":"Vanguard S\u0026P 500 ETF Tokenized Stock Defichain"},{"id":"vanilla-network","symbol":"vnla","name":"Vanilla Network"},{"id":"vanity","symbol":"vny","name":"Vanity"},{"id":"vankia-chain","symbol":"vkt","name":"Vankia Chain"},{"id":"vanspor-token","symbol":"van","name":"Vanspor Token"},{"id":"vaporfi","symbol":"vape","name":"VaporFi"},{"id":"vapornodes","symbol":"vpnd","name":"VaporNodes"},{"id":"vaporwave","symbol":"vwave","name":"Vaporwave"},{"id":"varen","symbol":"vrn","name":"Varen"},{"id":"vasco-da-gama-fan-token","symbol":"vasco","name":"Vasco da Gama Fan Token"},{"id":"vault","symbol":"vault","name":"VAULT"},{"id":"vaulteum","symbol":"vault","name":"Vaulteum"},{"id":"vault-hill-city","symbol":"vhc","name":"Vault Hill City"},{"id":"vaulty-token","symbol":"vlty","name":"Vaulty"},{"id":"vbswap","symbol":"vbswap","name":"vBSWAP"},{"id":"vcash","symbol":"xvc","name":"Vcash"},{"id":"vcgamers","symbol":"vcg","name":"VCGamers"},{"id":"veax","symbol":"veax","name":"Veax"},{"id":"vechain","symbol":"vet","name":"VeChain"},{"id":"veco","symbol":"veco","name":"Veco"},{"id":"vecrv-dao-yvault","symbol":"yve-crvdao","name":"veCRV-DAO yVault"},{"id":"vector-finance","symbol":"vtx","name":"Vector Finance"},{"id":"vectorium","symbol":"vect","name":"Vectorium"},{"id":"vectorspace","symbol":"vxv","name":"Vectorspace AI"},{"id":"vedao","symbol":"weve","name":"veDAO"},{"id":"veed","symbol":"veed","name":"VEED"},{"id":"vee-finance","symbol":"vee","name":"Vee Finance"},{"id":"vega-coin","symbol":"vega","name":"Vega Coin"},{"id":"vegannation-greencoin","symbol":"grnc","name":"VeganNation GreenCoin"},{"id":"vega-protocol","symbol":"vega","name":"Vega Protocol"},{"id":"vegasino","symbol":"vegas","name":"Vegasino"},{"id":"vega-sport","symbol":"vega","name":"Vega Sport"},{"id":"veggiecoin","symbol":"vegi","name":"VeggieCoin"},{"id":"veil","symbol":"veil","name":"VEIL"},{"id":"velas","symbol":"vlx","name":"Velas"},{"id":"velaspad","symbol":"vlxpad","name":"VelasPad"},{"id":"vela-token","symbol":"vela","name":"Vela Token"},{"id":"veldorabsc","symbol":"vdora","name":"VeldoraBSC"},{"id":"velhalla","symbol":"scar","name":"Velhalla"},{"id":"velo","symbol":"velo","name":"Velo"},{"id":"velocimeter-flow","symbol":"flow","name":"Velocimeter FLOW"},{"id":"velocore","symbol":"vc","name":"Velocore"},{"id":"velodrome-finance","symbol":"velo","name":"Velodrome Finance"},{"id":"velorex","symbol":"vex","name":"Velorex"},{"id":"vemate","symbol":"vmt","name":"Vemate"},{"id":"vempire-ddao","symbol":"vemp","name":"VEMP"},{"id":"vendetta-finance","symbol":"ven","name":"Vendetta Finance"},{"id":"venera","symbol":"vsw","name":"Venera"},{"id":"venify","symbol":"vfy","name":"Venify"},{"id":"veno","symbol":"veno","name":"Veno"},{"id":"veno-finance","symbol":"vno","name":"Veno Finance"},{"id":"venom","symbol":"venom","name":"Venom"},{"id":"venox","symbol":"vnx","name":"Venox"},{"id":"vent-finance","symbol":"vent","name":"Vent Finance"},{"id":"vention","symbol":"vention","name":"Vention"},{"id":"ventiswap","symbol":"vst","name":"VentiSwap"},{"id":"venus","symbol":"xvs","name":"Venus"},{"id":"venus-bch","symbol":"vbch","name":"Venus BCH"},{"id":"venus-beth","symbol":"vbeth","name":"Venus BETH"},{"id":"venus-btc","symbol":"vbtc","name":"Venus BTC"},{"id":"venus-busd","symbol":"vbusd","name":"Venus BUSD"},{"id":"venus-dai","symbol":"vdai","name":"Venus DAI"},{"id":"venus-doge","symbol":"vdoge","name":"Venus DOGE"},{"id":"venus-dot","symbol":"vdot","name":"Venus DOT"},{"id":"venus-eth","symbol":"veth","name":"Venus ETH"},{"id":"venus-fil","symbol":"vfil","name":"Venus FIL"},{"id":"venus-link","symbol":"vlink","name":"Venus LINK"},{"id":"venus-ltc","symbol":"vltc","name":"Venus LTC"},{"id":"venus-reward-token","symbol":"vrt","name":"Venus Reward"},{"id":"venus-sxp","symbol":"vsxp","name":"Venus SXP"},{"id":"venus-usdc","symbol":"vusdc","name":"Venus USDC"},{"id":"venus-usdt","symbol":"vusdt","name":"Venus USDT"},{"id":"venus-xrp","symbol":"vxrp","name":"Venus XRP"},{"id":"venus-xvs","symbol":"vxvs","name":"Venus XVS"},{"id":"vera","symbol":"vera","name":"Vera"},{"id":"vera-exchange","symbol":"vera","name":"VERA Exchange"},{"id":"veraone","symbol":"vro","name":"VeraOne"},{"id":"verasity","symbol":"vra","name":"Verasity"},{"id":"verge","symbol":"xvg","name":"Verge"},{"id":"veriblock","symbol":"vbk","name":"VeriBlock"},{"id":"vericoin","symbol":"vrc","name":"VeriCoin"},{"id":"veridocglobal","symbol":"vdg","name":"VeriDocGlobal"},{"id":"veritaseum","symbol":"veri","name":"Veritaseum"},{"id":"veritise","symbol":"vts","name":"Veritise"},{"id":"verox","symbol":"vrx","name":"Verox"},{"id":"versacoin","symbol":"vcn","name":"VersaCoin"},{"id":"versagames","symbol":"versa","name":"VersaGames"},{"id":"versailles-heroes","symbol":"vrh","name":"Versailles Heroes"},{"id":"verse-bitcoin","symbol":"verse","name":"Verse"},{"id":"verso","symbol":"vso","name":"Verso"},{"id":"versoview","symbol":"vvt","name":"VersoView"},{"id":"vertcoin","symbol":"vtc","name":"Vertcoin"},{"id":"vertek","symbol":"vrtk","name":"Vertek"},{"id":"vertex-protocol","symbol":"vrtx","name":"Vertex Protocol"},{"id":"verus-coin","symbol":"vrsc","name":"Verus Coin"},{"id":"verve","symbol":"verve","name":"Verve"},{"id":"very-banking","symbol":"vb","name":"Very Banking"},{"id":"very-special-dragon","symbol":"vito","name":"Very Special Dragon"},{"id":"vesper-finance","symbol":"vsp","name":"Vesper Finance"},{"id":"vesper-vdollar","symbol":"vusd","name":"Vesper V-Dollar"},{"id":"vesq","symbol":"vsq","name":"VESQ"},{"id":"vesta-finance","symbol":"vsta","name":"Vesta Finance"},{"id":"vesta-stable","symbol":"vst","name":"Vesta Stable"},{"id":"vestige","symbol":"vest","name":"Vestige"},{"id":"vethor-token","symbol":"vtho","name":"VeThor"},{"id":"vetme","symbol":"vetme","name":"VetMe"},{"id":"vetter-skylabs","symbol":"vsl","name":"Vetter Skylabs"},{"id":"vetter-token","symbol":"vetter","name":"Vetter"},{"id":"veusd","symbol":"veusd","name":"VeUSD"},{"id":"vexanium","symbol":"vex","name":"Vexanium"},{"id":"vfox","symbol":"vfox","name":"VFOX"},{"id":"viacoin","symbol":"via","name":"Viacoin"},{"id":"viagra-token","symbol":"viagra","name":"Viagra"},{"id":"vibe","symbol":"vibe","name":"VIBE"},{"id":"viberate","symbol":"vib","name":"Viberate"},{"id":"vibing","symbol":"vbg","name":"Vibing"},{"id":"vicat","symbol":"vicat","name":"ViCat"},{"id":"vica-token","symbol":"vica","name":"ViCA"},{"id":"vicdao-nelum","symbol":"nelum","name":"VICDAO NELUM"},{"id":"vicewrld","symbol":"vicedao","name":"ViceWRLD DAO"},{"id":"vicmove","symbol":"vim","name":"VicMove"},{"id":"victoria-vr","symbol":"vr","name":"Victoria VR"},{"id":"victorum","symbol":"vcc","name":"Victorum"},{"id":"victory-gem","symbol":"vtg","name":"Victory Gem"},{"id":"vicuna","symbol":"vina","name":"VICUNA"},{"id":"viddli","symbol":"mty","name":"Viddli"},{"id":"videocoin","symbol":"vid","name":"Vivid Labs"},{"id":"vidiachange","symbol":"vida","name":"Vidiachange"},{"id":"vidt-dao","symbol":"vidt","name":"VIDT DAO"},{"id":"vidulum","symbol":"vdl","name":"Vidulum"},{"id":"vidy","symbol":"vidy","name":"VIDY"},{"id":"vidya","symbol":"vidya","name":"Vidya"},{"id":"vidyx","symbol":"vidyx","name":"VidyX"},{"id":"vig","symbol":"vig","name":"Vigor"},{"id":"vigorus","symbol":"vis","name":"Vigorus"},{"id":"viking-elon","symbol":"velon","name":"Viking Elon"},{"id":"vince-chain","symbol":"vce","name":"Vince Chain"},{"id":"vindax-coin","symbol":"vd","name":"VinDax Coin"},{"id":"vip-coin","symbol":"vip","name":"Vip Coin"},{"id":"viper","symbol":"viper","name":"Viper"},{"id":"vips-token","symbol":"vips","name":"VIPS Token"},{"id":"vip-token","symbol":"vip","name":"VIP"},{"id":"vira-lata-finance","symbol":"reau","name":"Vira-Lata Finance"},{"id":"viral-inu","symbol":"vinu","name":"Viral Inu"},{"id":"viralup","symbol":"viral","name":"ViralUp"},{"id":"vires-finance","symbol":"vires","name":"Vires Finance"},{"id":"virgo","symbol":"vgo","name":"Virgo"},{"id":"virtualmeta","symbol":"vma","name":"Virtual Meta"},{"id":"virtual-reality-game-world","symbol":"vrgw","name":"Virtual Reality Game World"},{"id":"virtual-reality-glasses","symbol":"vrg","name":"Virtual Reality Glasses"},{"id":"virtual-ride-token","symbol":"vrt","name":"Virtual Ride Token"},{"id":"virtual-tourist","symbol":"vt","name":"Virtual Tourist"},{"id":"virtual-trader","symbol":"vtr","name":"Virtual Trader"},{"id":"virtue","symbol":"virtue","name":"Virtue"},{"id":"virtue-poker","symbol":"vpp","name":"Virtue Poker Points"},{"id":"visio","symbol":"visio","name":"Visio"},{"id":"visiongame","symbol":"vision","name":"VisionGame"},{"id":"vision-metaverse","symbol":"vs","name":"Vision Metaverse"},{"id":"visor","symbol":"visr","name":"Visor"},{"id":"vist","symbol":"vist","name":"VIST"},{"id":"vitadao","symbol":"vita","name":"VitaDAO"},{"id":"vita-inu","symbol":"vinu","name":"Vita Inu"},{"id":"vitality","symbol":"vita","name":"Vitality"},{"id":"vital-network","symbol":"vital","name":"Vital Network"},{"id":"vitalxp","symbol":"vital","name":"VitalXP"},{"id":"vitamin-coin","symbol":"vitc","name":"Vitamin Coin"},{"id":"vite","symbol":"vite","name":"Vite"},{"id":"viterium","symbol":"vt","name":"Viterium"},{"id":"vitex","symbol":"vx","name":"ViteX Coin"},{"id":"vitoge","symbol":"vitoge","name":"Vitoge"},{"id":"vitteey","symbol":"vity","name":"Vitteey"},{"id":"viva","symbol":"viva","name":"Viva"},{"id":"viva-classic-2","symbol":"viva","name":"Viva Classic"},{"id":"vixco","symbol":"vix","name":"Vixco"},{"id":"vizslaswap","symbol":"vizslaswap","name":"VizslaSwap"},{"id":"vlaunch","symbol":"vpad","name":"VLaunch"},{"id":"vm-tycoons-businesses","symbol":"businesses","name":"VM Tycoons Businesses"},{"id":"vndc","symbol":"vndc","name":"VNDC"},{"id":"vnx-euro","symbol":"veur","name":"VNX EURO"},{"id":"vnx-exchange","symbol":"vnxlu","name":"VNX Exchange"},{"id":"vnx-gold","symbol":"vnxau","name":"VNX Gold"},{"id":"vnx-swiss-franc","symbol":"vchf","name":"VNX Swiss Franc"},{"id":"vodra","symbol":"vdr","name":"Vodra"},{"id":"voice-street","symbol":"vst","name":"Voice Street"},{"id":"void-ad9a561a-8bca-4c17-9a3f-483f5cf20ac0","symbol":"void","name":"VOID"},{"id":"void-games","symbol":"void","name":"Void Games"},{"id":"volare-network","symbol":"volr","name":"Volare Network"},{"id":"volentix-vtx","symbol":"vtx","name":"Volentix"},{"id":"voltage","symbol":"volt","name":"Voltage"},{"id":"volta-protocol","symbol":"volta","name":"Volta Protocol"},{"id":"volt-inu","symbol":"volt","name":"Volt Inu [OLD]"},{"id":"volt-inu-2","symbol":"volt","name":"Volt Inu"},{"id":"voltswap","symbol":"volt","name":"VoltSwap"},{"id":"vortex-defi","symbol":"vtx","name":"Vortex DeFi"},{"id":"vortex-protocol","symbol":"vp","name":"Vortex Protocol"},{"id":"voucher-dot","symbol":"vdot","name":"Voucher DOT"},{"id":"voucher-eth","symbol":"veth","name":"Voucher ETH"},{"id":"voucher-ethereum-2-0","symbol":"veth","name":"Voucher Ethereum 2.0"},{"id":"voucher-glmr","symbol":"vglmr","name":"Voucher GLMR"},{"id":"voucher-ksm","symbol":"vksm","name":"Voucher KSM"},{"id":"voucher-movr","symbol":"vmovr","name":"Voucher MOVR"},{"id":"vow","symbol":"vow","name":"Vow"},{"id":"voxel-x-network","symbol":"vxl","name":"Voxel X Network"},{"id":"vox-finance","symbol":"vox","name":"Vox.Finance"},{"id":"vox-finance-2-0","symbol":"vox2.0","name":"Vox Finance 2.0"},{"id":"voxies","symbol":"voxel","name":"Voxies"},{"id":"voxnet","symbol":"vxon","name":"VoxNET"},{"id":"voyce","symbol":"voyce","name":"Voyce"},{"id":"vpncoin","symbol":"vash","name":"VPNCoin"},{"id":"vres","symbol":"vrs","name":"VRES"},{"id":"vrjam","symbol":"vrjam","name":"VRJAM"},{"id":"vrmars","symbol":"vrm","name":"VRMARS"},{"id":"vsolidus","symbol":"vsol","name":"VSolidus"},{"id":"v-systems","symbol":"vsys","name":"V.SYSTEMS"},{"id":"vulcan-forged","symbol":"pyr","name":"Vulcan Forged"},{"id":"vulcano-2","symbol":"vulc","name":"Vulcano"},{"id":"vulkania-2","symbol":"vlk","name":"Vulkania"},{"id":"vulture-peak","symbol":"vpk","name":"Vulture Peak"},{"id":"vvs-finance","symbol":"vvs","name":"VVS Finance"},{"id":"vxxl","symbol":"vxxl","name":"VXXL"},{"id":"vyfinance","symbol":"vyfi","name":"VyFinance"},{"id":"vynk-chain","symbol":"vync","name":"VYNK Chain"},{"id":"wabi","symbol":"wabi","name":"Wabi"},{"id":"wadzpay-token","symbol":"wtk","name":"WadzPay"},{"id":"wagerr","symbol":"wgr","name":"Wagerr"},{"id":"waggle-network","symbol":"wag","name":"Waggle Network"},{"id":"wagie","symbol":"wagie","name":"WAGIE"},{"id":"wagmi","symbol":"wagmi","name":"WAGMI"},{"id":"wagmi-game-2","symbol":"wagmigames","name":"WAGMI Game"},{"id":"wagmi-on-solana","symbol":"wagmi","name":"WAGMI On Solana"},{"id":"wagmi-token","symbol":"wag","name":"WAGMI Token"},{"id":"wagyuswap","symbol":"wag","name":"WagyuSwap"},{"id":"waifer","symbol":"waif","name":"Waifer"},{"id":"waifu","symbol":"waifu","name":"Waifu"},{"id":"waifu-token","symbol":"waif","name":"Waifu Genesis Card Collection"},{"id":"wait","symbol":"wait","name":"WAIT"},{"id":"wakanda-inu","symbol":"wkd","name":"Wakanda Inu"},{"id":"walken","symbol":"wlkn","name":"Walken"},{"id":"wallax","symbol":"wlx","name":"Wallax"},{"id":"wallet-defi","symbol":"wdf","name":"Wallet Defi"},{"id":"walletnow","symbol":"wnow","name":"WalletNow"},{"id":"wallet-safu","symbol":"wsafu","name":"Wallet SAFU"},{"id":"wallet-swap","symbol":"wswap","name":"Wallet Swap"},{"id":"wallfair","symbol":"wfair","name":"Wallfair"},{"id":"wallstreetbets-com","symbol":"wsb","name":"Wallstreetbets.com"},{"id":"wall-street-bets-dapp","symbol":"wsb","name":"WallStreetBets DApp"},{"id":"wall-street-games","symbol":"wsg","name":"Wall Street Games"},{"id":"wallstreetninja","symbol":"wsn","name":"WallStreetNinja"},{"id":"walrus","symbol":"wlrs","name":"Walrus"},{"id":"walter-inu","symbol":"$winu","name":"Walter Inu"},{"id":"waltonchain","symbol":"wtc","name":"Waltonchain"},{"id":"wam","symbol":"wam","name":"Wam"},{"id":"wanaka-farm","symbol":"wana","name":"Wanaka Farm"},{"id":"wanaka-farm-wairere-token","symbol":"wai","name":"Wanaka Farm WAIRERE"},{"id":"wanbtc","symbol":"wanbtc","name":"wanBTC"},{"id":"wanchain","symbol":"wan","name":"Wanchain"},{"id":"wanda-exchange","symbol":"we","name":"Wanda Exchange"},{"id":"waneth","symbol":"waneth","name":"wanETH"},{"id":"wannaswap","symbol":"wanna","name":"WannaSwap"},{"id":"wanswap","symbol":"wasp","name":"WanSwap"},{"id":"wanusdc","symbol":"wanusdc","name":"wanUSDC"},{"id":"wanusdt","symbol":"wanusdt","name":"wanUSDT"},{"id":"wanxrp","symbol":"wanxrp","name":"wanXRP"},{"id":"war-bond","symbol":"wbond","name":"War Bond"},{"id":"warena","symbol":"rena","name":"Warena"},{"id":"warp-cash","symbol":"warp","name":"Warp Cash"},{"id":"warp-finance","symbol":"warp","name":"Warp Finance"},{"id":"warrior-empires","symbol":"chaos","name":"Warrior Empires"},{"id":"warrior-rare-essentials","symbol":"ware","name":"Warrior Rare Essentials"},{"id":"wasabix","symbol":"wasabi","name":"WasabiX"},{"id":"wasdaq-finance","symbol":"wsdq","name":"Wasdaq Finance"},{"id":"wasder","symbol":"was","name":"Wasder"},{"id":"waste-coin","symbol":"waco","name":"Waste Digital Coin"},{"id":"watchdo","symbol":"wdo","name":"WatchDO"},{"id":"wateenswap","symbol":"wtn","name":"Wateenswap"},{"id":"waterfall-finance","symbol":"waterfall","name":"Waterfall Finance"},{"id":"waterfall-governance-token","symbol":"wtf","name":"Waterfall Governance"},{"id":"wattton","symbol":"watt","name":"WATTTON"},{"id":"waultswap","symbol":"wex","name":"WaultSwap"},{"id":"wavelength","symbol":"wave","name":"Wavelength"},{"id":"waves","symbol":"waves","name":"Waves"},{"id":"waves-ducks","symbol":"egg","name":"Waves Ducks"},{"id":"waves-enterprise","symbol":"west","name":"Waves Enterprise"},{"id":"waves-exchange","symbol":"wx","name":"WX Network Token"},{"id":"wavesgo","symbol":"wgo","name":"WavesGo"},{"id":"wax","symbol":"waxp","name":"WAX"},{"id":"waxe","symbol":"waxe","name":"WAXE"},{"id":"wayawolfcoin","symbol":"ww","name":"WayaWolfCoin"},{"id":"waykichain","symbol":"wicc","name":"WaykiChain"},{"id":"waykichain-governance-coin","symbol":"wgrt","name":"WaykiChain Governance Coin"},{"id":"wazirx","symbol":"wrx","name":"WazirX"},{"id":"wb-mining","symbol":"wbm","name":"WB-Mining"},{"id":"wbnb","symbol":"wbnb","name":"Wrapped BNB"},{"id":"wbtc-plenty-bridge","symbol":"wbtc.e","name":"WBTC (Plenty Bridge)"},{"id":"wbtc-yvault","symbol":"yvwbtc","name":"WBTC yVault"},{"id":"wcapes","symbol":"wca","name":"WCAPES"},{"id":"wdot","symbol":"wdot","name":"WDOT"},{"id":"wealthsecrets","symbol":"wsc","name":"WealthSecrets"},{"id":"web3camp","symbol":"3p","name":"Web3Camp"},{"id":"web3-inu","symbol":"web3","name":"WEB3 Inu"},{"id":"web3tools","symbol":"web3t","name":"Web3Tools"},{"id":"web4-ai","symbol":"web4","name":"WEB4 AI"},{"id":"web-ai","symbol":"webai","name":"Web AI"},{"id":"webcash","symbol":"web","name":"Webcash"},{"id":"webchain","symbol":"mintme","name":"MintMe.com Coin"},{"id":"webflix","symbol":"wfx","name":"WebFlix"},{"id":"web-four","symbol":"webfour","name":"WEBFOUR"},{"id":"weble-ecosystem-token","symbol":"wet","name":"Weble Ecosystem"},{"id":"webooswap","symbol":"weboo","name":"WebooSwap"},{"id":"webuy","symbol":"we","name":"WeBuy"},{"id":"wecoown","symbol":"wcx","name":"WeCoOwn"},{"id":"wednesday","symbol":"wd","name":"Wednesday"},{"id":"wegro","symbol":"wegro","name":"WeGro"},{"id":"weld","symbol":"weld","name":"WELD"},{"id":"welltrado","symbol":"wtl","name":"Welltrado"},{"id":"welups-blockchain","symbol":"welups","name":"Welups Blockchain"},{"id":"wemergetoken","symbol":"mrg","name":"WemergeToken"},{"id":"wemix-dollar","symbol":"wemix$","name":"WEMIX Dollar"},{"id":"wemix-token","symbol":"wemix","name":"WEMIX"},{"id":"wenlambo-2","symbol":"wlbo","name":"Wenlambo"},{"id":"wepiggy-coin","symbol":"wpc","name":"WePiggy Coin"},{"id":"wepower","symbol":"wpr","name":"WePower"},{"id":"wesendit","symbol":"wsi","name":"WeSendit"},{"id":"wesleep","symbol":"wez","name":"WeSleep"},{"id":"westarter","symbol":"war","name":"WeStarter"},{"id":"wetc-hebeswap","symbol":"wetc","name":"WETC (HebeSwap)"},{"id":"weth","symbol":"weth","name":"WETH"},{"id":"weth-plenty-bridge","symbol":"weth.p","name":"Polygon WETH (Plenty Bridge)"},{"id":"weth-plenty-bridge-65aa5342-507c-4f67-8634-1f4376ffdf9a","symbol":"weth.e","name":"WETH (Plenty Bridge)"},{"id":"weth-yvault","symbol":"yvweth","name":"WETH yVault"},{"id":"wettok-market","symbol":"wto","name":"Wettok Market"},{"id":"weway","symbol":"wwy","name":"WeWay"},{"id":"weyu","symbol":"weyu","name":"WEYU"},{"id":"wfdp","symbol":"wfdp","name":"WFDP"},{"id":"wgmi","symbol":"wgmi","name":"WGMI"},{"id":"whale","symbol":"whale","name":"WHALE"},{"id":"whale-fall","symbol":"whale","name":"Whale Fall"},{"id":"whale-maker-fund","symbol":"wmf","name":"Whale Maker Fund"},{"id":"whaleroom","symbol":"whl","name":"WhaleRoom"},{"id":"wheat","symbol":"wheat","name":"Wheat"},{"id":"wheat-token","symbol":"wheat","name":"Wheat (BSC)"},{"id":"whey","symbol":"whey","name":"WHEY"},{"id":"whey-token","symbol":"whey","name":"Shredded Apes Whey"},{"id":"whisper","symbol":"wisp","name":"Whisper"},{"id":"whitebit","symbol":"wbt","name":"WhiteBIT Token"},{"id":"whitecoin","symbol":"xwc","name":"Whitecoin"},{"id":"white-ethereum","symbol":"white","name":"White Ethereum"},{"id":"whiteheart","symbol":"white","name":"Whiteheart"},{"id":"whole-earth-coin","symbol":"wec","name":"Whole Earth Coin"},{"id":"whole-network","symbol":"node","name":"Whole Network"},{"id":"wibx","symbol":"wbx","name":"WiBX"},{"id":"wicrypt","symbol":"wnt","name":"Wicrypt"},{"id":"widi-soul","symbol":"wso","name":"Widi Soul"},{"id":"wifedoge","symbol":"wifedoge","name":"Wifedoge"},{"id":"wifi","symbol":"wifi","name":"WiFi Map"},{"id":"wiggly-finance","symbol":"wgl","name":"Wiggly Finance"},{"id":"wigoswap","symbol":"wigo","name":"WigoSwap"},{"id":"wiki-cat","symbol":"wkc","name":"Wiki Cat"},{"id":"wilder-world","symbol":"wild","name":"Wilder World"},{"id":"wild-island-game","symbol":"wild","name":"Wild Island Game"},{"id":"windex","symbol":"wdex","name":"Windex"},{"id":"windfall-token","symbol":"wft","name":"Windfall"},{"id":"windoge95","symbol":"wndg95","name":"Windoge95"},{"id":"winerz","symbol":"$wnz","name":"Winerz"},{"id":"wine-shares","symbol":"wine","name":"Wine Shares"},{"id":"wing-finance","symbol":"wing","name":"Wing Finance"},{"id":"wingriders","symbol":"wrt","name":"WingRiders"},{"id":"wings","symbol":"wings","name":"Wings"},{"id":"wingswap","symbol":"wis","name":"WingSwap"},{"id":"wink","symbol":"win","name":"WINkLink"},{"id":"winklink-bsc","symbol":"win","name":"WINkLink BSC"},{"id":"winr-protocol","symbol":"winr","name":"WINR Protocol"},{"id":"winry-inu","symbol":"winry","name":"Winry Inu"},{"id":"winter","symbol":"winter","name":"Winter"},{"id":"winterdog","symbol":"wdog","name":"Winterdog"},{"id":"wipemyass","symbol":"wipe","name":"WipeMyAss"},{"id":"wirex","symbol":"wxt","name":"WXT Token"},{"id":"wirtual","symbol":"wirtual","name":"Wirtual"},{"id":"wise-token11","symbol":"wise","name":"Wise"},{"id":"wish-finance-2","symbol":"wish","name":"Wish Finance"},{"id":"witch-token","symbol":"witch","name":"Witch Token"},{"id":"witnet","symbol":"wit","name":"Witnet"},{"id":"wizardia","symbol":"wzrd","name":"Wizardia"},{"id":"wizard-token","symbol":"wizard","name":"Wizard BSC"},{"id":"wizard-token-8fc587d7-4b79-4f5a-89c9-475f528c6d47","symbol":"wizt","name":"Wizard Token"},{"id":"wizard-vault-nftx","symbol":"wizard","name":"WIZARD Vault (NFTX)"},{"id":"wizarre-scroll","symbol":"scrl","name":"Wizarre Scroll"},{"id":"wiz-protocol","symbol":"wiz","name":"WIZ Protocol"},{"id":"wjewel","symbol":"wjewel","name":"WJEWEL"},{"id":"wliti","symbol":"wliti","name":"wLITI"},{"id":"wlitidao","symbol":"wld","name":"wLitiDAO"},{"id":"wmatic","symbol":"wmatic","name":"Wrapped Matic"},{"id":"wmatic-plenty-bridge","symbol":"wmatic.p","name":"WMATIC (Plenty Bridge)"},{"id":"wodex","symbol":"wmt","name":"Wodex"},{"id":"wohlstand-token","symbol":"wt","name":"Wohlstand"},{"id":"wojak-finance","symbol":"woj","name":"Wojak Finance"},{"id":"wolfcoin","symbol":"wolf","name":"WOLFCOIN"},{"id":"wolfecoin","symbol":"wolfe","name":"Wolfecoin"},{"id":"wolf-game-wool","symbol":"wool","name":"Wolf Game Wool"},{"id":"wolf-pups-2","symbol":"wolfies","name":"WOLF PUPS"},{"id":"wolfsafepoorpeople","symbol":"wspp","name":"WolfSafePoorPeople"},{"id":"wolfsafepoorpeople-polygon","symbol":"wspp","name":"WolfSafePoorPeople Polygon"},{"id":"wolf-town-wool","symbol":"wtwool","name":"Wolf Town Wool"},{"id":"wolf-ventures","symbol":"$wv","name":"Wolf Ventures"},{"id":"wolfy","symbol":"wolfy","name":"WOLFY"},{"id":"wolverinu-2","symbol":"wolverinu","name":"Wolverinu"},{"id":"wombat","symbol":"wombat","name":"Wombat"},{"id":"wombat-exchange","symbol":"wom","name":"Wombat Exchange"},{"id":"wombex","symbol":"wmx","name":"Wombex"},{"id":"women-empowerment-token","symbol":"wemp","name":"Women Empowerment"},{"id":"wom-token","symbol":"wom","name":"WOM Protocol"},{"id":"wonderhero","symbol":"wnd","name":"WonderHero [OLD]"},{"id":"wonderhero-2","symbol":"wnd","name":"WonderHero"},{"id":"wonderhero-hon","symbol":"hon","name":"WonderHero HON"},{"id":"wonderland","symbol":"time","name":"Wonderland TIME"},{"id":"wonderly-finance","symbol":"afx","name":"Wonderly Finance"},{"id":"wonderly-finance-xeth","symbol":"xeth","name":"Wonderly Finance xETH"},{"id":"wonderman-nation","symbol":"wndr","name":"Wonderman Nation"},{"id":"wonderverse","symbol":"wonder","name":"Wonderverse"},{"id":"woodcoin","symbol":"log","name":"Woodcoin"},{"id":"woof-token","symbol":"woof","name":"WOOF"},{"id":"woofwork-io","symbol":"woof","name":"WoofWork.io"},{"id":"woofy","symbol":"woofy","name":"Woofy"},{"id":"wool-token","symbol":"wool","name":"Wool"},{"id":"woo-network","symbol":"woo","name":"WOO Network"},{"id":"woonkly-power","symbol":"woop","name":"Woonkly Power"},{"id":"woop","symbol":"woop","name":"WOOP"},{"id":"woozoo-music","symbol":"wzm","name":"Woozoo Music"},{"id":"wordlex","symbol":"wdx","name":"Wordlex"},{"id":"work-quest","symbol":"wqt","name":"Work Quest"},{"id":"world-bet-inu","symbol":"wbi","name":"World Bet Inu"},{"id":"worldcoin","symbol":"wdc","name":"WorldCoin"},{"id":"worldcore","symbol":"wrc","name":"Worldcore"},{"id":"worldcup-fan-token-pow","symbol":"wtf","name":"WorldCup Fan Token PoW"},{"id":"world-cup-willie","symbol":"willie","name":"World Cup Willie"},{"id":"world-mobile-token","symbol":"wmt","name":"World Mobile Token"},{"id":"world-of-defish","symbol":"wod","name":"World of Defish"},{"id":"world-of-farms","symbol":"wof","name":"World of Farms"},{"id":"world-of-legends","symbol":"wol","name":"World of Legends"},{"id":"world-pay-coin","symbol":"wpc","name":"World Pay Coin"},{"id":"worthwhile","symbol":"whe","name":"Worthwhile"},{"id":"wownero","symbol":"wow","name":"Wownero"},{"id":"wowswap","symbol":"wow","name":"WOWswap"},{"id":"wow-token","symbol":"wow","name":"WOWNFT"},{"id":"wozx","symbol":"wozx","name":"Efforce"},{"id":"wpp-token","symbol":"wpp","name":"WPP Token"},{"id":"wpt-investing-corp","symbol":"wpt","name":"WPT Investing Corp"},{"id":"wrap-governance-token","symbol":"wrap","name":"WRAP Governance"},{"id":"wrapped-accumulate","symbol":"wacme","name":"Wrapped Accumulate"},{"id":"wrapped-ada","symbol":"wada","name":"Wrapped ADA"},{"id":"wrapped-algo","symbol":"xalgo","name":"Wrapped ALGO"},{"id":"wrapped-ampleforth","symbol":"wampl","name":"Wrapped Ampleforth"},{"id":"wrapped-anatha","symbol":"wanatha","name":"Wrapped ANATHA"},{"id":"wrapped-ar","symbol":"war","name":"Wrapped AR"},{"id":"wrapped-astar","symbol":"wastr","name":"Wrapped Astar"},{"id":"wrapped-atromg8","symbol":"wag8","name":"Wrapped ATROMG8"},{"id":"wrapped-avax","symbol":"wavax","name":"Wrapped AVAX"},{"id":"wrapped-bch","symbol":"wbch","name":"Wrapped BCH"},{"id":"wrapped-bitcoin","symbol":"wbtc","name":"Wrapped Bitcoin"},{"id":"wrapped-bitcoin-celer","symbol":"cewbtc","name":"Wrapped Bitcoin - Celer"},{"id":"wrapped-bitcoin-sollet","symbol":"sobtc","name":"Wrapped Bitcoin (Sollet)"},{"id":"wrapped-bitcoin-stacks","symbol":"xbtc","name":"Wrapped Bitcoin-Stacks"},{"id":"wrapped-bnb-celer","symbol":"cewbnb","name":"Wrapped BNB - Celer"},{"id":"wrapped-brise","symbol":"wbrise","name":"Wrapped Brise"},{"id":"wrapped-btt","symbol":"wbtt","name":"Wrapped BTT"},{"id":"wrapped-busd","symbol":"wbusd","name":"Wrapped BUSD"},{"id":"wrapped-busd-allbridge-from-bsc","symbol":"abbusd","name":"Wrapped BUSD (Allbridge from BSC)"},{"id":"wrapped-centrifuge","symbol":"wcfg","name":"Wrapped Centrifuge"},{"id":"wrapped-ckb","symbol":"wckb","name":"Wrapped CKB"},{"id":"wrapped-conflux","symbol":"wcfx","name":"Wrapped Conflux"},{"id":"wrapped-core","symbol":"wcore","name":"Wrapped CORE"},{"id":"wrapped-cro","symbol":"wcro","name":"Wrapped CRO"},{"id":"wrapped-cube","symbol":"wcube","name":"Wrapped Cube"},{"id":"wrapped-cusd-allbridge-from-celo","symbol":"acusd","name":"Wrapped CUSD (Allbridge from Celo)"},{"id":"wrapped-ecomi","symbol":"womi","name":"Wrapped ECOMI"},{"id":"wrapped-elastos","symbol":"wela","name":"Wrapped Elastos"},{"id":"wrapped-elrond","symbol":"wegld","name":"Wrapped EGLD"},{"id":"wrapped-energi","symbol":"wnrg","name":"Wrapped Energi"},{"id":"wrapped-ether-celer","symbol":"ceweth","name":"Wrapped Ether - Celer"},{"id":"wrapped-ethereum-sollet","symbol":"soeth","name":"Wrapped Ethereum (Sollet)"},{"id":"wrapped-ethw","symbol":"wethw","name":"Wrapped ETHW"},{"id":"wrapped-ever","symbol":"wever","name":"Wrapped Ever"},{"id":"wrapped-fantom","symbol":"wftm","name":"Wrapped Fantom"},{"id":"wrapped-fantom-celer","symbol":"cewftm","name":"Wrapped Fantom - Celer"},{"id":"wrapped-fio","symbol":"wfio","name":"Wrapped FIO"},{"id":"wrapped-flare","symbol":"wflr","name":"Wrapped Flare"},{"id":"wrapped-flow","symbol":"wflow","name":"Wrapped Flow"},{"id":"wrapped-gen-0-cryptokitties","symbol":"wg0","name":"Wrapped Gen-0 CryptoKitties"},{"id":"wrapped-hec","symbol":"wshec","name":"Wrapped HEC"},{"id":"wrapped-huobi-token","symbol":"wht","name":"Wrapped Huobi"},{"id":"wrapped-iotex","symbol":"wiotx","name":"Wrapped IoTex"},{"id":"wrapped-kava","symbol":"wkava","name":"Wrapped Kava"},{"id":"wrapped-kcs","symbol":"wkcs","name":"Wrapped KCS"},{"id":"wrapped-klay","symbol":"wklay","name":"Wrapped KLAY"},{"id":"wrapped-leo","symbol":"wleo","name":"Wrapped LEO"},{"id":"wrapped-memory","symbol":"wmemo","name":"Wonderful Memories"},{"id":"wrapped-metrix","symbol":"mrxb","name":"Wrapped Metrix"},{"id":"wrapped-millix","symbol":"wmlx","name":"Wrapped Millix"},{"id":"wrapped-minima","symbol":"wminima","name":"Wrapped Minima"},{"id":"wrapped-moonbeam","symbol":"wglmr","name":"Wrapped Moonbeam"},{"id":"wrapped-ncg","symbol":"wncg","name":"Wrapped NCG"},{"id":"wrapped-near","symbol":"wnear","name":"Wrapped Near"},{"id":"wrapped-newyorkcoin","symbol":"wnyc","name":"Wrapped NewYorkCoin"},{"id":"wrapped-nxm","symbol":"wnxm","name":"Wrapped NXM"},{"id":"wrapped-oas","symbol":"woas","name":"Wrapped OAS"},{"id":"wrapped-okt","symbol":"wokt","name":"Wrapped OKT"},{"id":"wrapped-one","symbol":"wone","name":"Wrapped One"},{"id":"wrapped-paycoin","symbol":"wpci","name":"Wrapped Paycoin"},{"id":"wrapped-pkt","symbol":"wpkt","name":"Wrapped PKT"},{"id":"wrapped-pom","symbol":"wpom","name":"Wrapped POM"},{"id":"wrapped-reflect","symbol":"wrft","name":"Wrapped REFLECT"},{"id":"wrapped-shiden-network","symbol":"sdn","name":"Wrapped Shiden Network"},{"id":"wrapped-solana","symbol":"sol","name":"Wrapped Solana"},{"id":"wrapped-songbird","symbol":"wsgb","name":"Wrapped Songbird"},{"id":"wrapped-staked-scr","symbol":"wsscr","name":"Wrapped Staked SCR"},{"id":"wrapped-star","symbol":"wstr","name":"Wrapped Star"},{"id":"wrapped-statera","symbol":"wsta","name":"Wrapped Statera"},{"id":"wrapped-steth","symbol":"wsteth","name":"Wrapped stETH"},{"id":"wrapped-strax","symbol":"wstrax","name":"Wrapped Strax"},{"id":"wrapped-syscoin","symbol":"wsys","name":"Wrapped Syscoin"},{"id":"wrapped-tao","symbol":"wtao","name":"Wrapped TAO"},{"id":"wrapped-telos","symbol":"wtlos","name":"Wrapped Telos"},{"id":"wrapped-terra","symbol":"lunc","name":"Wrapped Terra Classic"},{"id":"wrapped-tezos","symbol":"wxtz","name":"StakerDAO Wrapped Tezos"},{"id":"wrapped-tezos-2","symbol":"wtz","name":"Wrapped Tezos"},{"id":"wrapped-thunderpokt","symbol":"wtpokt","name":"Wrapped ThunderPOKT"},{"id":"wrapped-tomo","symbol":"wtomo","name":"Wrapped TOMO"},{"id":"wrapped-tron","symbol":"wtrx","name":"Wrapped Tron"},{"id":"wrapped-turtlecoin","symbol":"wtrtl","name":"Wrapped TurtleCoin"},{"id":"wrapped-usdc","symbol":"xusd","name":"Wrapped USDC"},{"id":"wrapped-usdr","symbol":"wusdr","name":"Wrapped USDR"},{"id":"wrapped-usdt","symbol":"wusdt","name":"Wrapped USDT"},{"id":"wrapped-usdt-allbridge-from-polygon","symbol":"apusdt","name":"Wrapped USDT (Allbridge from Polygon)"},{"id":"wrapped-ust","symbol":"ustc","name":"Wrapped USTC"},{"id":"wrapped-velas","symbol":"wvlx","name":"Wrapped Velas"},{"id":"wrapped-virgin-gen-0-cryptokitties","symbol":"wvg0","name":"Wrapped Virgin Gen-0 CryptoKittties"},{"id":"wrapped-wan","symbol":"wwan","name":"Wrapped Wan"},{"id":"wrapped-wdoge","symbol":"wwdoge","name":"Wrapped WDOGE"},{"id":"wrapped-xbtc","symbol":"wxbtc","name":"Wrapped xBTC"},{"id":"wrapped-xdai","symbol":"wxdai","name":"Wrapped XDAI"},{"id":"wrapped-xdc","symbol":"wxdc","name":"Wrapped XDC"},{"id":"wrapped-xmr-btse","symbol":"wxmr","name":"Wrapped XMR by BTSE"},{"id":"wrapped-xrp","symbol":"wxrp","name":"Wrapped XRP"},{"id":"wrestling-shiba","symbol":"wwe","name":"Wrestling Shiba"},{"id":"wsb-sh","symbol":"wsbt","name":"WSB.sh"},{"id":"wtf-token","symbol":"wtf","name":"Fees.wtf"},{"id":"wusd","symbol":"wusd","name":"Wrapped USD"},{"id":"wwemix","symbol":"wwemix","name":"WWEMIX"},{"id":"wynd","symbol":"wynd","name":"WYND"},{"id":"x2","symbol":"x2","name":"X2"},{"id":"x-2","symbol":"x","name":"X"},{"id":"x2y2","symbol":"x2y2","name":"X2Y2"},{"id":"x42-protocol","symbol":"x42","name":"X42 Protocol"},{"id":"x7101","symbol":"x7101","name":"X7101"},{"id":"x7102","symbol":"x7102","name":"X7102"},{"id":"x7103","symbol":"x7103","name":"X7103"},{"id":"x7104","symbol":"x7104","name":"X7104"},{"id":"x7105","symbol":"x7105","name":"X7105"},{"id":"x7-coin","symbol":"x7c","name":"X7 Coin"},{"id":"x7dao","symbol":"x7dao","name":"X7DAO"},{"id":"x7r","symbol":"x7r","name":"X7R"},{"id":"x8-project","symbol":"x8x","name":"X8X"},{"id":"xai","symbol":"xai","name":"XAI Stablecoin"},{"id":"xana","symbol":"xeta","name":"XANA"},{"id":"xaurum","symbol":"xaur","name":"Xaurum"},{"id":"xave-coin","symbol":"xvc","name":"Xave Coin"},{"id":"xave-token","symbol":"xav","name":"Xave"},{"id":"xbe-token","symbol":"xbe","name":"XBE"},{"id":"xbit","symbol":"xbt","name":"Xbit"},{"id":"x-blox","symbol":"xbx","name":"X-BLOX"},{"id":"xblue-finance","symbol":"xb","name":"XBlue Finance"},{"id":"xbn","symbol":"xbn","name":"Elastic BNB"},{"id":"xbn-community-token","symbol":"xbc","name":"XBN Community"},{"id":"xbullion","symbol":"gold","name":"XBullion"},{"id":"xbullion_silver","symbol":"silv","name":"XBullion Silver"},{"id":"xcad-network","symbol":"xcad","name":"XCAD Network"},{"id":"xcad-network-play","symbol":"play","name":"XCAD Network PLAY"},{"id":"xcarnival","symbol":"xcv","name":"XCarnival"},{"id":"x-cash","symbol":"xcash","name":"X-CASH"},{"id":"xcavator","symbol":"xca","name":"Xcavator"},{"id":"xcdot","symbol":"xcdot","name":"xcDOT"},{"id":"xcel-swap","symbol":"xld","name":"Xcel Defi"},{"id":"xceltoken-plus","symbol":"xlab","name":"XCELTOKEN PLUS"},{"id":"xcf-token","symbol":"xcf","name":"XCF Token"},{"id":"xcksm","symbol":"xcksm","name":"xcKSM"},{"id":"xcom","symbol":"xc","name":"XCOM"},{"id":"x-consoles","symbol":"game","name":"X-Consoles"},{"id":"xcrx","symbol":"xcrx","name":"xCRX"},{"id":"xcusdt","symbol":"xcusdt","name":"xcUSDT"},{"id":"xdai","symbol":"xdai","name":"XDAI"},{"id":"xdai-native-comb","symbol":"xcomb","name":"xDai Native Comb"},{"id":"xdai-stake","symbol":"stake","name":"STAKE"},{"id":"xdao","symbol":"xdao","name":"XDAO"},{"id":"xdce-crowd-sale","symbol":"xdc","name":"XDC Network"},{"id":"xdefi","symbol":"xdefi","name":"XDEFI"},{"id":"xdefi-governance-token","symbol":"xdex","name":"XDEFI Governance"},{"id":"xdeus","symbol":"xdeus","name":"xDEUS"},{"id":"xdoge","symbol":"xdoge","name":"Xdoge"},{"id":"xdollar-stablecoin","symbol":"xusd","name":"xDollar Stablecoin"},{"id":"xels","symbol":"xels","name":"XELS"},{"id":"xen-crypto","symbol":"xen","name":"XEN Crypto"},{"id":"xend-finance","symbol":"xend","name":"Xend Finance"},{"id":"xenios","symbol":"xnc","name":"Xenios"},{"id":"xenoruntoken","symbol":"xrt","name":"XenoRunToken"},{"id":"xeno-token","symbol":"xno","name":"Xeno"},{"id":"xerium","symbol":"xerm","name":"Xerium"},{"id":"xeta","symbol":"x3ta","name":"XETA"},{"id":"xeus","symbol":"xeus","name":"XEUS"},{"id":"xfarmer","symbol":"xf","name":"xFarmer"},{"id":"xfinance","symbol":"xfi","name":"Xfinance"},{"id":"xfinite-entertainment-token","symbol":"xet","name":"Xfinite Entertainment"},{"id":"xfit","symbol":"xfit","name":"XFai"},{"id":"xfuel","symbol":"xfuel","name":"XFUEL"},{"id":"xfund","symbol":"xfund","name":"xFUND"},{"id":"xgold-coin","symbol":"xgold","name":"Xgold Coin"},{"id":"x-hash","symbol":"xsh","name":"X-HASH"},{"id":"xhashtag","symbol":"xtag","name":"xHashtag"},{"id":"xhype","symbol":"xhp","name":"XHYPE"},{"id":"xiasi-inu","symbol":"xiasi","name":"Xiasi Inu"},{"id":"xiden","symbol":"xden","name":"Xiden"},{"id":"xido-finance","symbol":"xido","name":"Xido Finance"},{"id":"xiglute-coin","symbol":"xgc","name":"Xiglute Coin"},{"id":"xio","symbol":"xio","name":"Blockzero Labs"},{"id":"xion-finance","symbol":"xgt","name":"Xion Finance"},{"id":"xi-token","symbol":"xi","name":"Xi"},{"id":"xjewel","symbol":"xjewel","name":"xJEWEL"},{"id":"xlist","symbol":"xlist","name":"XList"},{"id":"xlp-finance","symbol":"xlpfi","name":"xLP Finance"},{"id":"xmark","symbol":"xmark","name":"xMARK"},{"id":"x-mask","symbol":"xmc","name":"X-MASK"},{"id":"xmatic","symbol":"xmatic","name":"xMATIC"},{"id":"xmax","symbol":"xmx","name":"XMax"},{"id":"xmetaversal","symbol":"xmeta","name":"XMetaversal"},{"id":"x-metaverse","symbol":"xmeta","name":"X-Metaverse"},{"id":"xmine","symbol":"xmn","name":"Xmine"},{"id":"xmon","symbol":"xmon","name":"XMON"},{"id":"xmooney","symbol":"xm","name":"xMooney"},{"id":"xnft","symbol":"xnft","name":"xNFT Protocol"},{"id":"xodex","symbol":"xodex","name":"Xodex"},{"id":"xoloitzcuintli","symbol":"xolo","name":"xoloitzcuintli"},{"id":"xoycoin","symbol":"xoy","name":"XOYCoin"},{"id":"xp","symbol":"xp","name":"XP"},{"id":"xpansion-game","symbol":"xps","name":"Xpansion Game"},{"id":"xpendium","symbol":"xpnd","name":"Xpendium"},{"id":"xperps","symbol":"xperps","name":"xPERPS"},{"id":"xpla","symbol":"xpla","name":"XPLA"},{"id":"xplus-token","symbol":"xpt","name":"XPLUS Token"},{"id":"xpmarket","symbol":"xpm","name":"XPmarket"},{"id":"xp-network","symbol":"xpnet","name":"XP Network"},{"id":"xpool","symbol":"xpo","name":"Xpool"},{"id":"xproject","symbol":"xpro","name":"XPROJECT"},{"id":"x-protocol","symbol":"pot","name":"X Protocol"},{"id":"xptp","symbol":"xptp","name":"xPTP"},{"id":"xquake","symbol":"xqk","name":"XQuake"},{"id":"xrdoge","symbol":"xrdoge","name":"XRdoge"},{"id":"xreators","symbol":"ort","name":"XREATORS"},{"id":"xrhodium","symbol":"xrc","name":"xRhodium"},{"id":"xrice-token","symbol":"xrice","name":"xRice Token"},{"id":"xrpaynet","symbol":"xrpaynet","name":"XRPayNet"},{"id":"xrp-classic-2","symbol":"xrpc","name":"XRP Classic"},{"id":"xrpfarm","symbol":"xf","name":"XRPFarm"},{"id":"xrp-healthcare","symbol":"xrph","name":"XRP Healthcare"},{"id":"xr-shiba-inu","symbol":"xrshib","name":"XR Shiba Inu"},{"id":"xrun","symbol":"xrun","name":"XRun"},{"id":"xsauce","symbol":"xsauce","name":"xSAUCE"},{"id":"xsgd","symbol":"xsgd","name":"XSGD"},{"id":"xshare","symbol":"xshare","name":"SpaceShipX"},{"id":"xshrap","symbol":"xshrap","name":"xShrap"},{"id":"xsigma","symbol":"sig","name":"xSigma"},{"id":"xsl-labs","symbol":"syl","name":"myDid"},{"id":"x-social-network","symbol":"x-ai","name":"X Social Network"},{"id":"xspectar","symbol":"xspectar","name":"xSPECTAR"},{"id":"xstudio","symbol":"txs","name":"XStudio"},{"id":"xsushi","symbol":"xsushi","name":"xSUSHI"},{"id":"xswap","symbol":"xsp","name":"XSwap"},{"id":"xswap-protocol","symbol":"xsp","name":"XSwap Protocol"},{"id":"xswap-treasure","symbol":"xtt","name":"XSwap Treasure"},{"id":"xtal","symbol":"xtal","name":"XTAL"},{"id":"xtblock-token","symbol":"xtt-b20","name":"XTblock"},{"id":"xtcom-token","symbol":"xt","name":"XT.com"},{"id":"xtime","symbol":"xtm","name":"XTime"},{"id":"xtoken","symbol":"xtk","name":"xToken"},{"id":"xtrabytes","symbol":"xby","name":"XTRABYTES"},{"id":"xtremcoin","symbol":"xtr","name":"Xtremcoin"},{"id":"xtusd","symbol":"xtusd","name":"XT Stablecoin XTUSD"},{"id":"xusd","symbol":"xusd","name":"xUSD"},{"id":"xusd-babelfish","symbol":"xusd","name":"XUSD (BabelFish)"},{"id":"xusd-token","symbol":"xusd","name":"xUSD Token"},{"id":"xwalrus","symbol":"xwlrs","name":"xWalrus"},{"id":"xwin-finance","symbol":"xwin","name":"xWIN Finance"},{"id":"x-world-games","symbol":"xwg","name":"X World Games"},{"id":"xxcoin","symbol":"xx","name":"XX Network"},{"id":"xy-finance","symbol":"xy","name":"XY Finance"},{"id":"xyo-network","symbol":"xyo","name":"XYO Network"},{"id":"xysl","symbol":"xysl","name":"xYSL"},{"id":"y2b","symbol":"y2b","name":"Y2B"},{"id":"y2k","symbol":"y2k","name":"Y2K"},{"id":"y-5-finance","symbol":"y5","name":"Y5 Crypto"},{"id":"yadacoin","symbol":"yda","name":"YadaCoin"},{"id":"yaki-gold","symbol":"yag","name":"Yaki Gold"},{"id":"yaku","symbol":"yaku","name":"Yaku"},{"id":"yam-2","symbol":"yam","name":"YAM"},{"id":"yamanote-sen","symbol":"ymnt","name":"Yamanote-Sen"},{"id":"yamp-finance","symbol":"yamp","name":"Yamp Finance"},{"id":"yam-v2","symbol":"yamv2","name":"YAM v2"},{"id":"yarloo","symbol":"yarl","name":"Yarloo"},{"id":"yasha-dao","symbol":"yasha","name":"YASHA"},{"id":"yawww","symbol":"yaw","name":"Yawww"},{"id":"yaxis","symbol":"yaxis","name":"yAxis"},{"id":"yay-games","symbol":"yay","name":"YAY Network"},{"id":"yayswap","symbol":"yay","name":"YaySwap"},{"id":"ycash","symbol":"yec","name":"Ycash"},{"id":"yclub","symbol":"syc","name":"YCLUB"},{"id":"y-coin","symbol":"yco","name":"Y Coin"},{"id":"ydragon","symbol":"ydr","name":"YDragon"},{"id":"yearn-classic-finance","symbol":"earn","name":"Yearn Classic Finance"},{"id":"yearn-crv","symbol":"ycrv","name":"Yearn CRV"},{"id":"yearn-finance","symbol":"yfi","name":"yearn.finance"},{"id":"yearnlab","symbol":"ylb","name":"Yearnlab"},{"id":"yearn-secure","symbol":"ysec","name":"Yearn Secure"},{"id":"yee","symbol":"yee","name":"Yee"},{"id":"yel-finance","symbol":"yel","name":"Yel.Finance"},{"id":"yellowheart-protocol","symbol":"hrts","name":"YellowHeart Protocol"},{"id":"yellow-road","symbol":"road","name":"Yellow Road"},{"id":"yeni-malatyaspor-token","symbol":"yms","name":"Yeni Malatyaspor Token"},{"id":"yenten","symbol":"ytn","name":"YENTEN"},{"id":"yeon","symbol":"yeon","name":"Yeon"},{"id":"yesorno","symbol":"yon","name":"YESorNO"},{"id":"yes-token","symbol":"yes","name":"YES Token"},{"id":"yes-world","symbol":"yes","name":"Yes World"},{"id":"yeticoin","symbol":"yetic","name":"YetiCoin"},{"id":"yeti-finance","symbol":"yeti","name":"Yeti Finance"},{"id":"yfdai-finance","symbol":"yf-dai","name":"YfDAI.finance"},{"id":"yffi-finance","symbol":"yffi","name":"yffi finance"},{"id":"yfii-finance","symbol":"yfii","name":"DFI.money"},{"id":"yfione","symbol":"yfo","name":"YFIONE"},{"id":"yfi-yvault","symbol":"yvyfi","name":"YFI yVault"},{"id":"yflink","symbol":"yfl","name":"YF Link"},{"id":"yfx","symbol":"yfx","name":"Your Futures Exchange"},{"id":"yield-app","symbol":"yld","name":"Yield App"},{"id":"yieldblox","symbol":"ybx","name":"YieldBlox"},{"id":"yieldfarming-index","symbol":"yfx","name":"YieldFarming Index"},{"id":"yield-generating-enreach","symbol":"ygnrch","name":"Yield Generating Enreach"},{"id":"yield-guild-games","symbol":"ygg","name":"Yield Guild Games"},{"id":"yieldification","symbol":"ydf","name":"Yieldification"},{"id":"yieldly","symbol":"yldy","name":"Yieldly"},{"id":"yield-optimization-platform","symbol":"yop","name":"Yield Optimization Platform \u0026 Protocol"},{"id":"yield-parrot","symbol":"lory","name":"Yield Parrot"},{"id":"yield-protocol","symbol":"yield","name":"Yield Protocol"},{"id":"yieldwatch","symbol":"watch","name":"Yieldwatch"},{"id":"yield-yak","symbol":"yak","name":"Yield Yak"},{"id":"yield-yak-avax","symbol":"yyavax","name":"Yield Yak AVAX"},{"id":"yin-finance","symbol":"yin","name":"YIN Finance"},{"id":"yinyang","symbol":"yy","name":"YinYang"},{"id":"ymplepay","symbol":"ympa","name":"YmplePay"},{"id":"yobit-token","symbol":"yo","name":"Yobit"},{"id":"yocoin","symbol":"yoc","name":"Yocoin"},{"id":"yocoinyoco","symbol":"yoco","name":"YocoinYOCO"},{"id":"yoda-coin-swap","symbol":"jedals","name":"Yoda Coin Swap"},{"id":"yodeswap","symbol":"yode","name":"YodeSwap"},{"id":"yofune-nushi","symbol":"koyo","name":"Yofune Nushi"},{"id":"yogi","symbol":"yogi","name":"Yogi"},{"id":"yogo","symbol":"yogo","name":"Yogo"},{"id":"yokaiswap","symbol":"yok","name":"YokaiSwap"},{"id":"yolo-cash","symbol":"ylc","name":"YOLOCash"},{"id":"yooshi","symbol":"yooshi","name":"YooShi"},{"id":"yoshi-exchange","symbol":"yoshi","name":"Yoshi.exchange"},{"id":"youbie","symbol":"$youb","name":"Youbie"},{"id":"youcash","symbol":"youc","name":"YOUcash"},{"id":"you-chain","symbol":"you","name":"YOU Chain"},{"id":"youclout","symbol":"yct","name":"Youclout"},{"id":"youcoin-metaverse","symbol":"ucon","name":"UCON"},{"id":"youminter","symbol":"umint","name":"YouMinter"},{"id":"young-boys-fan-token","symbol":"ybo","name":"Young Boys Fan Token"},{"id":"youngparrot","symbol":"ypc","name":"YoungParrot"},{"id":"yourkiss","symbol":"yks","name":"YourKiss"},{"id":"your-open-metaverse","symbol":"yom","name":"YOM"},{"id":"yourwallet","symbol":"yourwallet","name":"YourWallet"},{"id":"yourwallet-eth","symbol":"yourwallet","name":"YourWallet ETH"},{"id":"youves-uusd","symbol":"uusd","name":"Youves uUSD"},{"id":"youves-you-governance","symbol":"you","name":"Youves YOU Governance"},{"id":"youwho","symbol":"you","name":"Youwho"},{"id":"yoyow","symbol":"yoyow","name":"YOYOW"},{"id":"ysl","symbol":"ysl","name":"YSL"},{"id":"ytofu","symbol":"ytofu","name":"yTOFU"},{"id":"yuan-chain-coin","symbol":"ycc","name":"Yuan Chain Coin"},{"id":"yummi-universe","symbol":"yummi","name":"Yummi Universe"},{"id":"yummy","symbol":"yummy","name":"Yummy"},{"id":"yuna","symbol":"yuna","name":"Yuna"},{"id":"yup","symbol":"yup","name":"Yup"},{"id":"yusd-stablecoin","symbol":"yusd","name":"YUSD Stablecoin"},{"id":"yuse","symbol":"yuse","name":"Yuse"},{"id":"yvault-lp-ycurve","symbol":"yvault-lp-ycurve","name":"yUSD"},{"id":"yvboost","symbol":"yvboost","name":"Yearn Compounding veCRV yVault"},{"id":"yvdai","symbol":"yvdai","name":"yvDAI"},{"id":"yvs-finance","symbol":"yvs","name":"YVS Finance"},{"id":"z7dao","symbol":"z7","name":"Z7DAO"},{"id":"zada","symbol":"zada","name":"Zada"},{"id":"zaif-token","symbol":"zaif","name":"Zaif"},{"id":"zakumifi","symbol":"zafi","name":"ZakumiFi"},{"id":"zambesigold","symbol":"zgd","name":"ZambesiGold"},{"id":"zam-io","symbol":"zam","name":"Zam.io"},{"id":"zamzam","symbol":"zamzam","name":"ZAMZAM"},{"id":"zano","symbol":"zano","name":"Zano"},{"id":"zap","symbol":"zap","name":"Zap"},{"id":"zappy","symbol":"zap","name":"Zappy"},{"id":"zasset-zusd","symbol":"zusd","name":"Zasset zUSD"},{"id":"zatcoin-2","symbol":"zpro","name":"ZAT Project"},{"id":"zb-token","symbol":"zb","name":"ZB"},{"id":"zcash","symbol":"zec","name":"Zcash"},{"id":"zclassic","symbol":"zcl","name":"Zclassic"},{"id":"zcoin","symbol":"firo","name":"Firo"},{"id":"zcore","symbol":"zcr","name":"ZCore"},{"id":"zcore-finance","symbol":"zefi","name":"ZCore Finance"},{"id":"z-cubed","symbol":"z3","name":"Z-Cubed"},{"id":"zebec-protocol","symbol":"zbc","name":"Zebec Protocol"},{"id":"zebi","symbol":"zco","name":"Zebi"},{"id":"zebra-dao","symbol":"zebra","name":"Zebra DAO"},{"id":"zed-run","symbol":"zed","name":"ZED RUN"},{"id":"zedxion","symbol":"zedxion","name":"Zedxion"},{"id":"zedxion-usdz","symbol":"usdz","name":"Zedxion USDZ"},{"id":"zeedex","symbol":"zdex","name":"Zeedex"},{"id":"zeepin","symbol":"zpt","name":"Zeepin"},{"id":"zeeverse","symbol":"vee","name":"Zeeverse"},{"id":"zeitcoin","symbol":"zeit","name":"Zeitcoin"},{"id":"zeitgeist","symbol":"ztg","name":"Zeitgeist"},{"id":"zelaapayae","symbol":"zpae","name":"ZelaaPayAE"},{"id":"zelcash","symbol":"flux","name":"Flux"},{"id":"zelda-inu","symbol":"zlda","name":"Zelda Inu"},{"id":"zeloop-eco-reward","symbol":"erw","name":"ZeLoop Eco Reward"},{"id":"zelwin","symbol":"zlw","name":"Zelwin"},{"id":"zencash","symbol":"zen","name":"Horizen"},{"id":"zenc-coin","symbol":"zenc","name":"Zenc Coin"},{"id":"zenex","symbol":"znx","name":"ZENEX"},{"id":"zenfuse","symbol":"zefu","name":"Zenfuse"},{"id":"zeniq","symbol":"zeniq","name":"ZENIQ"},{"id":"zenith-chain","symbol":"zenith","name":"Zenith Chain"},{"id":"zenithereum","symbol":"zen-ai","name":"Zenithereum"},{"id":"zenithswap","symbol":"zsp","name":"ZenithSwap"},{"id":"zenland","symbol":"zenf","name":"Zenland"},{"id":"zenlink-network-token","symbol":"zlk","name":"Zenlink Network"},{"id":"zenon","symbol":"znn","name":"Zenon"},{"id":"zensports","symbol":"sports","name":"ZenSports"},{"id":"zent-cash","symbol":"ztc","name":"Zent Cash"},{"id":"zenzo","symbol":"znz","name":"ZENZO"},{"id":"zeon","symbol":"zeon","name":"ZEON Network"},{"id":"zeos","symbol":"zeos","name":"ZEOS"},{"id":"zeptagram","symbol":"zptc","name":"Zeptacoin"},{"id":"zer0zer0","symbol":"00","name":"00 Token"},{"id":"zero","symbol":"zer","name":"Zero"},{"id":"zero-exchange","symbol":"zero","name":"0.exchange"},{"id":"zeroliquid","symbol":"zero","name":"ZeroLiquid"},{"id":"zeroswap","symbol":"zee","name":"ZeroSwap"},{"id":"zero-tech","symbol":"zero","name":"Zero Tech"},{"id":"zero-utility-token","symbol":"zut","name":"Zero Utility"},{"id":"zerozed","symbol":"x0z","name":"Zerozed"},{"id":"zetacoin","symbol":"zet","name":"Zetacoin"},{"id":"zeus10000","symbol":"zeus10000","name":"ZEUS10000"},{"id":"zeus-ai","symbol":"zeus","name":"Zeus AI"},{"id":"zeus-finance","symbol":"zeus","name":"Zeus Finance"},{"id":"zeusshield","symbol":"zsc","name":"Zeusshield"},{"id":"zfmcoin","symbol":"zfm","name":"ZFMCOIN"},{"id":"zhc-zero-hour-cash","symbol":"zhc","name":"ZHC : Zero Hour Cash"},{"id":"zibu","symbol":"zibu","name":"Zibu"},{"id":"ziesha","symbol":"zsh","name":"Ziesha"},{"id":"zignaly","symbol":"zig","name":"Zignaly"},{"id":"zigzag-2","symbol":"zz","name":"ZigZag"},{"id":"zik-token","symbol":"zik","name":"Ziktalk"},{"id":"zillion-aakar-xo","symbol":"zillionxo","name":"Zillion Aakar XO"},{"id":"zilliqa","symbol":"zil","name":"Zilliqa"},{"id":"zilpay-wallet","symbol":"zlp","name":"ZilPay Wallet"},{"id":"zilstream","symbol":"stream","name":"ZilStream"},{"id":"zilswap","symbol":"zwap","name":"ZilSwap"},{"id":"zimbocash","symbol":"zash","name":"ZIMBOCASH"},{"id":"zin","symbol":"zin","name":"Zin"},{"id":"zinari","symbol":"zina","name":"ZINARI"},{"id":"zinja","symbol":"z","name":"Zinja"},{"id":"ziobitx","symbol":"zbtx","name":"ZiobitX"},{"id":"zion","symbol":"zion","name":"Zion"},{"id":"ziontopia","symbol":"zion","name":"ZionTopia"},{"id":"ziot","symbol":"ziot","name":"Ziot"},{"id":"zip","symbol":"zip","name":"Zipper Network"},{"id":"zipmex-token","symbol":"zmt","name":"Zipmex"},{"id":"zipswap","symbol":"zip","name":"ZipSwap"},{"id":"zircon-gamma-token","symbol":"zrg","name":"Zircon Gamma Token"},{"id":"zjoe","symbol":"zjoe","name":"zJOE"},{"id":"zkb","symbol":"zkb","name":"ZK Cross Chain Bridge"},{"id":"zkdoge","symbol":"zkdoge","name":"zkDoge"},{"id":"zk-inu","symbol":"$zkinu","name":"ZK inu"},{"id":"zklend","symbol":"zend","name":"zkLend"},{"id":"zklotto","symbol":"zklotto","name":"zkLotto"},{"id":"zknftex","symbol":"$zkn","name":"zkNFTex"},{"id":"zkproof","symbol":"zkp","name":"zkProof"},{"id":"zkshib","symbol":"zkshib","name":"zkShib"},{"id":"zkspace","symbol":"zks","name":"ZKSpace"},{"id":"zktsunami","symbol":":zkt:","name":"ZkTsunami"},{"id":"zmine","symbol":"zmn","name":"ZMINE"},{"id":"zodiacsv2","symbol":"zdcv2","name":"ZodiacsV2"},{"id":"zodium","symbol":"zodi","name":"Zodium"},{"id":"zogi","symbol":"zogi","name":"ZOGI"},{"id":"zoid-pay","symbol":"zpay","name":"ZoidPay"},{"id":"zombie-inu","symbol":"zinu","name":"Zombie Inu (OLD)"},{"id":"zombie-inu-2","symbol":"zinu","name":"Zombie Inu"},{"id":"zombie-runner","symbol":"zombie","name":"Zombie Runner"},{"id":"zombie-skrat","symbol":"zskrat","name":"ZOMBIE SKRAT"},{"id":"zombie-world-z","symbol":"zwz","name":"Zombie World Z"},{"id":"zomfi","symbol":"zomfi","name":"Zomfi"},{"id":"zone","symbol":"zone","name":"Zone"},{"id":"zonecoin","symbol":"zne","name":"Zonecoin"},{"id":"zone-of-avoidance","symbol":"zoa","name":"Zone of Avoidance"},{"id":"zoo-coin","symbol":"zoo","name":"ZooCoin"},{"id":"zoo-crypto-world","symbol":"zoo","name":"ZOO Crypto World"},{"id":"zoodao","symbol":"zoo","name":"ZooDAO"},{"id":"zookeeper","symbol":"zoo","name":"ZooKeeper"},{"id":"zoomswap","symbol":"zm","name":"ZoomSwap"},{"id":"zooshi","symbol":"zooshi","name":"Zooshi"},{"id":"zoo-token","symbol":"zoot","name":"Zoo"},{"id":"zoracles","symbol":"zora","name":"Zoracles"},{"id":"zoro-inu","symbol":"zoro","name":"Zoro Inu"},{"id":"zrcoin","symbol":"zrc","name":"ZrCoin"},{"id":"zro","symbol":"zro","name":"Carb0n.fi"},{"id":"zsol","symbol":"zsol","name":"zSOL"},{"id":"zudgezury","symbol":"zzc","name":"ZudgeZury"},{"id":"zuki-moba","symbol":"zuki","name":"Zuki Moba"},{"id":"zum-token","symbol":"zum","name":"ZUM"},{"id":"zuna","symbol":"zuna","name":"Zuna"},{"id":"zurrency","symbol":"zurr","name":"ZURRENCY"},{"id":"zusd","symbol":"zusd","name":"ZUSD"},{"id":"z-versus-project","symbol":"zversus","name":"Z Versus Project"},{"id":"zyberswap","symbol":"zyb","name":"Zyberswap"},{"id":"zynecoin","symbol":"zyn","name":"Zynecoin"},{"id":"zynergy","symbol":"zyn","name":"Zynergy"},{"id":"zyro","symbol":"zyro","name":"Zyro"},{"id":"zyrri","symbol":"zyr","name":"Zyrri"},{"id":"zyx","symbol":"zyx","name":"ZYX"}] \ No newline at end of file diff --git a/crates/chains-info/src/lib.rs b/crates/chains-info/src/lib.rs new file mode 100644 index 000000000..12a96cc26 --- /dev/null +++ b/crates/chains-info/src/lib.rs @@ -0,0 +1,26 @@ +//! Webb Chains Information +//! +//! This crate contains the information about the chains that are supported by the relayer. +//! The information is used to generate the `chains.rs` file and could be used by +//! the relayer to get the information about the chains. + +include!(concat!(env!("OUT_DIR"), "/chains.rs")); + +/// Get the chains information. +#[must_use] +#[inline] +pub fn chains_info() -> &'static chains::ChainsInfo { + &chains::CHAINS_INFO +} + +/// Get the chain information by the chain identifier. +#[must_use] +pub fn chain_info_by_chain_id( + chain_id: u64, +) -> Option<&'static chains::ChainInfo> { + chains::CHAINS_INFO + .binary_search_by_key(&chain_id, |(id, _)| *id) + .ok() + .and_then(|index| chains::CHAINS_INFO.get(index)) + .map(|(_, info)| info) +} diff --git a/crates/chains-info/supported_chains.toml b/crates/chains-info/supported_chains.toml new file mode 100644 index 000000000..343b57983 --- /dev/null +++ b/crates/chains-info/supported_chains.toml @@ -0,0 +1,30 @@ +chain-ids = [ + 1, # Ethereum Mainnet + 5, # Goerli Testnet + 137, # Polygon Mainnet + 80001, # Polygon Mumbai Testnet + 42161, # Arbitrum One Mainnet + 43113, # Avalance Fuji Testnet + 421613, # Arbitrum Goerli Testnet + 534352, # Scroll Mainnet + 534353, # Scroll Alpha Testnet + 11155111, # Sepolia Testnet + 1284, # Moonbeam Mainnet + 1285, # Moonriver + 1287, # Moonbase Alpha + 3884533461, # Athena Testnet + 3884533462, # Hermes Testnet + 3884533463, # Demeter Testnet +] + +# Override the coingecko coin Id for the Moonbase Alpha network +# Since the Moonbase Alpha network is not yet listed on CoinGecko. +# so we use the mainnet coin id instead. +[overrides.1287] +coingecko-coin-id = "moonbeam" + +# Override the coingecko coin Id for Arbitrum Goerli Testnet +# Since the Arbitrum Goerli Testnet is not yet listed on CoinGecko. +# so we use the mainnet coin id instead. +[overrides.421613] +coingecko-coin-id = "ethereum" diff --git a/crates/circom-proving/Cargo.toml b/crates/circom-proving/Cargo.toml new file mode 100644 index 000000000..f462c054f --- /dev/null +++ b/crates/circom-proving/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "webb-circom-proving" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ark-ff = { version = "0.3.0", default-features = false, features = [ "asm"] } +ark-ec = { version = "^0.3.0", default-features = false } +ark-relations = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } +ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", default-features = false, features = ["parallel"] } +ark-std = { version = "^0.3.0", default-features = false } +ark-bn254 = { version = "0.3.0" } +ark-circom = { git = "https://github.com/vacp2p/ark-circom", branch = "wasm", default-features = false, features = ["circom-2"] } +num-bigint = { version = "0.4", default-features = false, features = ["rand", "serde"] } +# For ark-circom +once_cell = "1.14.0" +cfg-if = "1.0" +wasmer = { version = "2.3.0", default-features = false } +num-traits = "0.2.11" +thiserror = "1.0.0" +color-eyre = "0.6.1" +serde = { version = "^1", default-features = false, features = ["derive"] } +serde_json = "1.0.48" +arkworks-native-gadgets = { version = "1.2.0", default-features = false } +ark-crypto-primitives = { version = "^0.3.0", features = ["r1cs"], default-features = false } + +[features] +default = ["wasmer/sys-default"] + diff --git a/crates/circom-proving/src/lib.rs b/crates/circom-proving/src/lib.rs new file mode 100644 index 000000000..1e8e9ef0c --- /dev/null +++ b/crates/circom-proving/src/lib.rs @@ -0,0 +1,135 @@ +use ark_bn254::{Bn254, Fr}; +use ark_circom::{CircomReduction, WitnessCalculator}; + +use ark_groth16::{ + create_proof_with_reduction_and_matrices, prepare_verifying_key, + verify_proof as ark_verify_proof, Proof as ArkProof, ProvingKey, + VerifyingKey, +}; +use ark_relations::r1cs::{ConstraintMatrices, SynthesisError}; + +use ark_std::{rand::thread_rng, UniformRand}; +use cfg_if::cfg_if; +use num_bigint::BigInt; +use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; +use std::sync::Mutex; +use thiserror::Error; +use wasmer::{Module, Store}; +// use ark_std::vec::Vec; + +#[derive(Error, Debug)] +pub enum ProofError { + #[error("Error reading circuit key: {0}")] + CircuitKeyError(#[from] std::io::Error), + #[error("Error producing witness: {0}")] + WitnessError(color_eyre::Report), + #[error("Error producing proof: {0}")] + SynthesisError(#[from] SynthesisError), +} + +#[cfg(not(target_arch = "wasm32"))] +static WITNESS_CALCULATOR: OnceCell> = OnceCell::new(); +// Initializes the witness calculator using a bytes vector +#[cfg(not(target_arch = "wasm32"))] +pub fn circom_from_raw( + wasm_buffer: Vec, +) -> &'static Mutex { + WITNESS_CALCULATOR.get_or_init(|| { + let store = Store::default(); + let module = Module::new(&store, wasm_buffer) + .expect("Failed to produce wasm module"); + let result = WitnessCalculator::from_module(module) + .expect("Failed to create witness calculator"); + Mutex::new(result) + }) +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ProverPath { + pub zkey: String, + pub wasm: String, +} +impl ProverPath { + pub fn new(zkey: String, wasm: String) -> Self { + Self { zkey, wasm } + } +} + +// Initializes the witness calculator +#[cfg(not(target_arch = "wasm32"))] +pub fn circom_from_folder( + wasm_path: &str, +) -> &'static Mutex { + // We read the wasm file + let wasm_buffer = + std::fs::read(wasm_path).expect("Could not find file at provided path"); + circom_from_raw(wasm_buffer) +} + +pub fn generate_proof( + #[cfg(not(target_arch = "wasm32"))] witness_calculator: &Mutex< + WitnessCalculator, + >, + #[cfg(target_arch = "wasm32")] witness_calculator: &mut WitnessCalculator, + proving_key: &(ProvingKey, ConstraintMatrices), + witness: [(&str, Vec); N], +) -> Result<(ArkProof, Vec), ProofError> { + let inputs = witness + .iter() + .map(|(name, values)| (name.to_string(), values.clone())); + + cfg_if! { + if #[cfg(target_arch = "wasm32")] { + let full_assignment = witness_calculator + .calculate_witness_element::(inputs, false) + .map_err(ProofError::WitnessError)?; + } else { + let full_assignment = witness_calculator + .lock() + .expect("witness_calculator mutex should not get poisoned") + .calculate_witness_element::(inputs, false) + .map_err(ProofError::WitnessError)?; + } + } + + // Random Values + let mut rng = thread_rng(); + let r = Fr::rand(&mut rng); + let s = Fr::rand(&mut rng); + + // TODO: Need to understand why this function is generating proofs even if parameters are + // invalid. `test_gen_proof_fails_with_incorrect_root` should not be able to generate a + // proof since new_root has an incorrect value (path_elements[0] instead) + let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>( + &proving_key.0, + r, + s, + &proving_key.1, + proving_key.1.num_instance_variables, + proving_key.1.num_constraints, + full_assignment.as_slice(), + )?; + + Ok((proof, full_assignment)) +} + +/// Verifies a given proof +/// +/// # Errors +/// +/// Returns a [`ProofError`] if verifying fails. Verification failure does not +/// necessarily mean the proof is incorrect. +pub fn verify_proof( + verifying_key: &VerifyingKey, + proof: &ArkProof, + inputs: Vec, +) -> Result { + // Check that the proof is valid + let pvk = prepare_verifying_key(verifying_key); + //let pr: ArkProof = (*proof).into(); + + let verified = ark_verify_proof(&pvk, proof, &inputs)?; + + Ok(verified) +} diff --git a/crates/event-watcher-traits/Cargo.toml b/crates/event-watcher-traits/Cargo.toml index d1a5270aa..3050098b3 100644 --- a/crates/event-watcher-traits/Cargo.toml +++ b/crates/event-watcher-traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-event-watcher-traits" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -27,6 +27,7 @@ native-tls = { workspace = true } webb-proposals = { workspace = true } tracing-test = "0.2" sled = { version = "^0.34" } +sp-core = { workspace = true } [features] default = ["std", "evm", "substrate"] @@ -36,4 +37,4 @@ evm = [ ] substrate = [ "webb-relayer-context/substrate", -] \ No newline at end of file +] diff --git a/crates/event-watcher-traits/src/evm/bridge_watcher.rs b/crates/event-watcher-traits/src/evm/bridge_watcher.rs index 4d7a625c1..eb8ee1161 100644 --- a/crates/event-watcher-traits/src/evm/bridge_watcher.rs +++ b/crates/event-watcher-traits/src/evm/bridge_watcher.rs @@ -22,8 +22,7 @@ use super::{event_watcher::EventWatcher, *}; #[async_trait::async_trait] pub trait BridgeWatcher: EventWatcher where - Self::Store: ProposalStore - + QueueStore + Self::Store: QueueStore + QueueStore, { /// A method to be called with the [`BridgeCommand`] information to @@ -43,26 +42,27 @@ where #[tracing::instrument( skip_all, fields( - chain_id = ?client.get_chainid().await, address = %contract.address(), tag = %Self::TAG, ), )] async fn run( &self, - client: Arc>, + client: Arc, store: Arc, contract: Self::Contract, metrics: Arc>, ) -> webb_relayer_utils::Result<()> { let backoff = backoff::backoff::Constant::new(Duration::from_secs(1)); let task = || async { - let my_chain_id = client + let chain_id = client .get_chainid() .map_err(Into::into) .map_err(backoff::Error::transient) .await?; - let bridge_key = BridgeKey::new(my_chain_id); + let typed_chain_id = + webb_proposals::TypedChainId::Evm(chain_id.as_u32()); + let bridge_key = BridgeKey::new(typed_chain_id); let key = SledQueueKey::from_bridge_key(bridge_key); loop { let result = match store.dequeue_item(key)? { @@ -81,13 +81,13 @@ where }; match result { Ok(_) => { - tracing::debug!(?key, "Handled command successfully"); + tracing::debug!(?key, %chain_id, "Handled command successfully"); continue; } Err(e) => { - tracing::error!("Error while handle_cmd {}", e); + tracing::error!(%chain_id, "Error while handle_cmd {}", e); // this a transient error, so we will retry again. - tracing::warn!("Restarting bridge event watcher ..."); + tracing::warn!(%chain_id, "Restarting bridge event watcher ..."); // metric for when the bridge watcher enters back off let metrics = metrics.lock().await; metrics.bridge_watcher_back_off.inc(); diff --git a/crates/event-watcher-traits/src/evm/event_watcher.rs b/crates/event-watcher-traits/src/evm/event_watcher.rs index d56a97f08..51079c03d 100644 --- a/crates/event-watcher-traits/src/evm/event_watcher.rs +++ b/crates/event-watcher-traits/src/evm/event_watcher.rs @@ -13,10 +13,24 @@ // limitations under the License. use tokio::sync::Mutex; -use webb_relayer_utils::retry; +use webb::evm::ethers::prelude::TimeLag; +use webb_relayer_utils::{multi_provider::MultiProvider, retry}; use super::*; +/// Ethereum client using Ethers, that includes a retry strategy. +pub type EthersClient = + providers::Provider>>; + +/// Ethereum TimeLag client using Ethers, that includes a retry strategy. +pub type EthersTimeLagClient = TimeLag< + Arc< + providers::Provider< + providers::RetryClient>, + >, + >, +>; + /// A watchable contract is a contract used in the [EventWatcher] pub trait WatchableContract: Send + Sync { /// The block number where this contract is deployed. @@ -49,7 +63,7 @@ pub trait EventWatcher { /// A Helper tag used to identify the event watcher during the logs. const TAG: &'static str; /// The contract that this event watcher is watching. - type Contract: Deref>> + type Contract: Deref> + WatchableContract; /// The Events that this event watcher is interested in. type Events: contract::EthLogDecode + Clone; @@ -60,14 +74,13 @@ pub trait EventWatcher { #[tracing::instrument( skip_all, fields( - chain_id = ?client.get_chainid().await, address = %contract.address(), tag = %Self::TAG, ), )] async fn run( &self, - client: Arc>, + client: Arc, store: Arc, contract: Self::Contract, handlers: Vec>, @@ -76,123 +89,117 @@ pub trait EventWatcher { let backoff = backoff::backoff::Constant::new(Duration::from_secs(1)); let task = || async { let step = contract.max_blocks_per_step().as_u64(); - // saves the last time we printed sync progress. - let mut instant = std::time::Instant::now(); let metrics = &ctx.metrics; let chain_id: u32 = client + .inner() .get_chainid() .map_err(Into::into) .map_err(backoff::Error::transient) .await? .as_u32(); - let chain_config = - ctx.config.evm.get(&chain_id.to_string()).ok_or_else(|| { - webb_relayer_utils::Error::ChainNotFound { - chain_id: chain_id.clone().to_string(), - } - })?; - // no of blocks confirmation required before processing it - let block_confirmations: u64 = - chain_config.block_confirmations.into(); // now we start polling for new events. + // create history store key + let src_target_system = TargetSystem::new_contract_address( + contract.address().to_fixed_bytes(), + ); + let src_typed_chain_id = TypedChainId::Evm(chain_id); + let history_store_key = + ResourceId::new(src_target_system, src_typed_chain_id); + + // saves the last time we printed sync progress. + let mut instant = std::time::Instant::now(); + // we only query this once, at the start of the events watcher. + // then we will update it later once we fully synced. + + let mut target_block_number = client + .get_block_number() + .map_err(Into::into) + .map_err(backoff::Error::transient) + .await? + .as_u64(); + + // Save the target block number in the store + // so other things can use it. + store.set_target_block_number( + history_store_key, + target_block_number, + )?; + loop { - // create history store key - let src_target_system = TargetSystem::new_contract_address( - contract.address().to_fixed_bytes(), - ); - let src_typed_chain_id = TypedChainId::Evm(chain_id); - let history_store_key = - ResourceId::new(src_target_system, src_typed_chain_id); let block = store.get_last_block_number( history_store_key, contract.deployed_at().as_u64(), )?; - let current_block_number = client - .get_block_number() + let dest_block = + core::cmp::min(block + step, target_block_number); + + let events_filter = contract + .event_with_filter::(Default::default()) + .from_block(block + 1) + .to_block(dest_block); + let found_events = events_filter + .query_with_meta() .map_err(Into::into) .map_err(backoff::Error::transient) .await?; - let current_block_number = current_block_number.as_u64(); - tracing::trace!( - "Latest block number: #{}", - current_block_number - ); - // latest finalized block after n block_confirmations - let latest_finalized_block = - current_block_number.saturating_sub(block_confirmations); - let dest_block = cmp::min(block + step, latest_finalized_block); - // check if we are now on the latest block. - let should_cooldown = dest_block == latest_finalized_block; - tracing::trace!("Reading from #{} to #{}", block, dest_block); - // Only handle events from found blocks if they are new - if dest_block != block { - let events_filter = contract - .event_with_filter::(Default::default()) - .from_block(block + 1) - .to_block(dest_block); - let found_events = events_filter - .query_with_meta() - .map_err(Into::into) - .map_err(backoff::Error::transient) - .await?; - tracing::trace!("Found #{} events", found_events.len()); - - for (event, log) in found_events { - // wraps each handler future in a retry logic, that will retry the handler - // if it fails, up to `MAX_RETRY_COUNT`, after this it will ignore that event for - // that specific handler. - const MAX_RETRY_COUNT: usize = 5; - let tasks = handlers.iter().map(|handler| { - // a constant backoff with maximum retry count is used here. - let backoff = retry::ConstantWithMaxRetryCount::new( - Duration::from_millis(100), - MAX_RETRY_COUNT, - ); - handler.handle_event_with_retry( - store.clone(), - &contract, - (event.clone(), log.clone()), - backoff, - metrics.clone(), - ) - }); - let result = futures::future::join_all(tasks).await; - // this event will be marked as handled if at least one handler succeeded. - // this because, for the failed events, we arleady tried to handle them - // many times (at this point), and there is no point in trying again. - let mark_as_handled = result.iter().any(|r| r.is_ok()); - // also, for all the failed event handlers, we should print what went - // wrong. - result.iter().for_each(|r| { + let number_of_events = found_events.len(); + tracing::trace!("Found #{number_of_events} events"); + for (event, log) in found_events { + // wraps each handler future in a retry logic, that will retry the handler + // if it fails, up to `MAX_RETRY_COUNT`, after this it will ignore that event for + // that specific handler. + const MAX_RETRY_COUNT: usize = 5; + let tasks = handlers.iter().map(|handler| { + // a constant backoff with maximum retry count is used here. + let backoff = retry::ConstantWithMaxRetryCount::new( + Duration::from_millis(100), + MAX_RETRY_COUNT, + ); + handler.handle_event_with_retry( + store.clone(), + &contract, + (event.clone(), log.clone()), + backoff, + metrics.clone(), + ) + }); + let result = futures::future::join_all(tasks).await; + // this event will be marked as handled if at least one handler succeeded. + // this because, for the failed events, we arleady tried to handle them + // many times (at this point), and there is no point in trying again. + let mark_as_handled = result.iter().any(Result::is_ok); + // also, for all the failed event handlers, we should print what went + // wrong. + result.iter().for_each(|r| { if let Err(e) = r { - tracing::error!("{}", e); + tracing::error!(?e, %chain_id, "Error while handling the event"); } }); - if mark_as_handled { - store.set_last_block_number( - history_store_key, - log.block_number.as_u64(), - )?; - tracing::trace!( - "event handled successfully. at #{}", - log.block_number - ); - } else { - tracing::error!("Error while handling event, all handlers failed."); - tracing::warn!("Restarting event watcher ..."); - // this a transient error, so we will retry again. - return Err(backoff::Error::transient( - webb_relayer_utils::Error::ForceRestart, - )); - } + if mark_as_handled { + store.set_last_block_number( + history_store_key, + log.block_number.as_u64(), + )?; + tracing::trace!( + %chain_id, + %log.block_number, + "event handled successfully", + ); + } else { + tracing::error!(%chain_id, "Error while handling event, all handlers failed."); + tracing::warn!(%chain_id, "Restarting event watcher ..."); + // this a transient error, so we will retry again. + return Err(backoff::Error::transient( + webb_relayer_utils::Error::ForceRestart, + )); } - // move forward. - store - .set_last_block_number(history_store_key, dest_block)?; - tracing::trace!("Last saved block number: #{}", dest_block); } - tracing::trace!("Polled from #{} to #{}", block, dest_block); + + // move the block pointer to the destination block + store.set_last_block_number(history_store_key, dest_block)?; + // if we fully synced, we can update the target block number + let should_cooldown = dest_block == target_block_number; if should_cooldown { let duration = contract.polling_interval(); tracing::trace!( @@ -200,34 +207,47 @@ pub trait EventWatcher { duration.as_millis() ); tokio::time::sleep(duration).await; + // update the latest block number + target_block_number = client + .get_block_number() + .map_err(Into::into) + .map_err(backoff::Error::transient) + .await? + .as_u64(); + store.set_target_block_number( + history_store_key, + target_block_number, + )?; } - // only print the progress if 7 seconds (by default) is passed. if contract.print_progress_interval() != Duration::from_millis(0) && instant.elapsed() > contract.print_progress_interval() { - // calculate sync progress. - let total = current_block_number as f64; - let current_value = dest_block as f64; - let diff = total - current_value; - let percentage = (diff / current_value) * 100.0; - // should be always less that 100. - // and this would be our current progress. - let sync_progress = 100.0 - percentage; + let currently_at = store.get_last_block_number( + history_store_key, + contract.deployed_at().as_u64(), + )?; + let diff = currently_at.saturating_sub(block); + let progress = currently_at as f64 + / target_block_number as f64 + * 100.0; + let is_syncing = progress < 99.99; tracing::info!( - "🔄 #{} of #{} ({:.4}%)", - dest_block, - current_block_number, - sync_progress + target_block = target_block_number, + currently_at, + diff, + is_syncing, + progress, + %chain_id ); tracing::event!( target: webb_relayer_utils::probe::TARGET, tracing::Level::TRACE, kind = %webb_relayer_utils::probe::Kind::Sync, + %chain_id, %block, %dest_block, - %sync_progress, ); instant = std::time::Instant::now(); } @@ -245,7 +265,7 @@ pub trait EventWatcher { pub trait EventHandler { /// The Type of contract this handler is for, Must be the same as the contract type in the /// watcher. - type Contract: Deref>> + type Contract: Deref> + WatchableContract; /// The type of event this handler is for. type Events: contract::EthLogDecode + Clone; @@ -271,7 +291,7 @@ pub trait EventHandler { /// Whether any of the events could be handled by the handler async fn can_handle_events( &self, - event: Self::Events, + (event, log): (Self::Events, contract::LogMeta), wrapper: &Self::Contract, ) -> webb_relayer_utils::Result; } @@ -299,7 +319,10 @@ pub trait EventHandlerWithRetry: EventHandler { backoff: impl backoff::backoff::Backoff + Send + Sync + 'static, metrics: Arc>, ) -> webb_relayer_utils::Result<()> { - if !self.can_handle_events(event.clone(), contract).await? { + if !self + .can_handle_events((event.clone(), log.clone()), contract) + .await? + { return Ok(()); }; diff --git a/crates/event-watcher-traits/src/evm/mod.rs b/crates/event-watcher-traits/src/evm/mod.rs index 5e1ace759..d7a28fe6f 100644 --- a/crates/event-watcher-traits/src/evm/mod.rs +++ b/crates/event-watcher-traits/src/evm/mod.rs @@ -16,7 +16,6 @@ //! # EVM Events Watcher Traits 🕸️ use futures::prelude::*; -use std::cmp; use std::ops::Deref; use std::sync::Arc; use std::time::Duration; @@ -31,8 +30,7 @@ use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; use webb_relayer_context::RelayerContext; use webb_relayer_store::sled::SledQueueKey; use webb_relayer_store::{ - BridgeCommand, BridgeKey, EventHashStore, HistoryStore, ProposalStore, - QueueStore, + BridgeCommand, BridgeKey, EventHashStore, HistoryStore, QueueStore, }; use webb_relayer_utils::metric; diff --git a/crates/event-watcher-traits/src/substrate/bridge_watcher.rs b/crates/event-watcher-traits/src/substrate/bridge_watcher.rs index 891d3a1ac..0686f8de1 100644 --- a/crates/event-watcher-traits/src/substrate/bridge_watcher.rs +++ b/crates/event-watcher-traits/src/substrate/bridge_watcher.rs @@ -13,7 +13,9 @@ // limitations under the License. use super::{event_watcher::SubstrateEventWatcher, *}; - +use sp_core::sr25519::Pair as Sr25519Pair; +use webb::substrate::subxt::OnlineClient; +use webb_relayer_context::RelayerContext; // A Substrate Bridge Watcher is a trait for Signature Bridge Pallet that is not specific for watching events from that pallet, /// instead it watches for commands sent from other event watchers or services, it helps decouple the event watchers /// from the actual action that should be taken depending on the event. @@ -22,8 +24,7 @@ pub trait SubstrateBridgeWatcher: SubstrateEventWatcher where RuntimeConfig: subxt::Config + Send + Sync + 'static, - Self::Store: ProposalStore - + QueueStore, + Self::Store: QueueStore, { /// A method that is called when a command is received that needs to be /// handled and executed. @@ -31,7 +32,8 @@ where &self, chain_id: u32, store: Arc, - client: Arc, + client: Arc>, + pair: Sr25519Pair, cmd: BridgeCommand, ) -> webb_relayer_utils::Result<()>; @@ -47,15 +49,21 @@ where async fn run( &self, chain_id: u32, - client: Arc, + ctx: RelayerContext, + pair: Sr25519Pair, store: Arc, ) -> webb_relayer_utils::Result<()> { let backoff = backoff::backoff::Constant::new(Duration::from_secs(1)); let task = || async { - let my_chain_id = webb_proposals::TypedChainId::Substrate(chain_id); - let bridge_key = BridgeKey::new(my_chain_id); + let typed_chain_id = + webb_proposals::TypedChainId::Substrate(chain_id); + let bridge_key = BridgeKey::new(typed_chain_id); let key = SledQueueKey::from_bridge_key(bridge_key); + let client = + ctx.substrate_provider::(chain_id).await?; + let client = Arc::new(client); + loop { let result = match store.dequeue_item(key)? { Some(cmd) => { @@ -63,6 +71,7 @@ where chain_id, store.clone(), client.clone(), + pair.clone(), cmd, ) .await diff --git a/crates/event-watcher-traits/src/substrate/event_watcher.rs b/crates/event-watcher-traits/src/substrate/event_watcher.rs index 3fa3f0357..d9a82fe04 100644 --- a/crates/event-watcher-traits/src/substrate/event_watcher.rs +++ b/crates/event-watcher-traits/src/substrate/event_watcher.rs @@ -13,8 +13,9 @@ // limitations under the License. use tokio::sync::Mutex; -use webb::substrate::subxt::config::Header; +use webb::substrate::subxt::{config::Header, OnlineClient}; use webb_relayer_config::event_watcher::EventsWatcherConfig; +use webb_relayer_context::RelayerContext; use webb_relayer_utils::{metric, retry}; use super::*; @@ -23,7 +24,7 @@ use super::*; pub type EventHandlerFor = Box< dyn EventHandler< RuntimeConfig, - Client = >::Client, + Client = OnlineClient, Store = >::Store, > + Send + Sync, @@ -125,8 +126,6 @@ where /// The name of the pallet that this event watcher is watching. const PALLET_NAME: &'static str; - /// The Runtime Client that can be used to perform API calls. - type Client: OnlineClientT + Send + Sync; /// The Storage backend, used by the event watcher to store its state. type Store: HistoryStore; @@ -143,15 +142,33 @@ where async fn run( &self, chain_id: u32, - client: Arc, + ctx: RelayerContext, store: Arc, event_watcher_config: EventsWatcherConfig, handlers: Vec>, metrics: Arc>, ) -> webb_relayer_utils::Result<()> { - let backoff = backoff::backoff::Constant::new(Duration::from_secs(1)); + const MAX_RETRY_COUNT: usize = 5; + + let backoff = backoff::ExponentialBackoff { + max_elapsed_time: None, + ..Default::default() + }; let metrics_clone = metrics.clone(); let task = || async { + let maybe_client = + ctx.substrate_provider::(chain_id).await; + let client = match maybe_client { + Ok(client) => client, + Err(err) => { + tracing::error!( + "Failed to connect with substrate client for chain_id: {}, retrying...!", + chain_id + ); + return Err(backoff::Error::transient(err)); + } + }; + let client = Arc::new(client); let mut instant = std::time::Instant::now(); let step = 1u64; let rpc = client.rpc(); @@ -244,7 +261,6 @@ where // wraps each handler future in a retry logic, that will retry the handler // if it fails, up to `MAX_RETRY_COUNT`, after this it will ignore that event for // that specific handler. - const MAX_RETRY_COUNT: usize = 5; let tasks = handlers.iter().map(|handler| { // a constant backoff with maximum retry count is used here. let backoff = retry::ConstantWithMaxRetryCount::new( @@ -264,7 +280,7 @@ where // this event will be marked as handled if at least one handler succeeded. // this because, for the failed events, we arleady tried to handle them // many times (at this point), and there is no point in trying again. - let mark_as_handled = result.iter().any(|r| r.is_ok()); + let mark_as_handled = result.iter().any(Result::is_ok); // also, for all the failed event handlers, we should print what went // wrong. result.iter().for_each(|r| { diff --git a/crates/event-watcher-traits/src/substrate/mod.rs b/crates/event-watcher-traits/src/substrate/mod.rs index 95e523917..1468f334a 100644 --- a/crates/event-watcher-traits/src/substrate/mod.rs +++ b/crates/event-watcher-traits/src/substrate/mod.rs @@ -18,18 +18,12 @@ use futures::prelude::*; use std::cmp; use std::sync::Arc; use std::time::Duration; -use webb::substrate::subxt::{ - self, - client::{OfflineClientT, OnlineClientT}, - config::Header, -}; +use webb::substrate::subxt::{self, client::OnlineClientT, config::Header}; use webb_proposals::{ ResourceId, SubstrateTargetSystem, TargetSystem, TypedChainId, }; use webb_relayer_store::sled::SledQueueKey; -use webb_relayer_store::{ - BridgeCommand, BridgeKey, HistoryStore, ProposalStore, QueueStore, -}; +use webb_relayer_store::{BridgeCommand, BridgeKey, HistoryStore, QueueStore}; /// Event watching traits mod event_watcher; diff --git a/crates/event-watcher-traits/src/tests.rs b/crates/event-watcher-traits/src/tests.rs index b73bbaf94..2003b4b7d 100644 --- a/crates/event-watcher-traits/src/tests.rs +++ b/crates/event-watcher-traits/src/tests.rs @@ -14,8 +14,8 @@ use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::dkg_runtime::api::system; use webb::substrate::subxt::{self, Config, OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api::system; use webb_relayer_config::event_watcher::EventsWatcherConfig; use webb_relayer_context::RelayerContext; use webb_relayer_store::sled::SledStore; @@ -33,8 +33,6 @@ impl SubstrateEventWatcher for TestEventsWatcher { const PALLET_NAME: &'static str = "System"; - type Client = OnlineClient; - type Store = SledStore; } @@ -83,16 +81,15 @@ async fn substrate_event_watcher_should_work() -> webb_relayer_utils::Result<()> { let chain_id = 5u32; let store = SledStore::temporary()?; - let client = OnlineClient::::new().await?; let watcher = TestEventsWatcher::default(); let config = webb_relayer_config::WebbRelayerConfig::default(); - let ctx = RelayerContext::new(config, store.clone()); + let ctx = RelayerContext::new(config, store.clone())?; let metrics = ctx.metrics.clone(); let event_watcher_config = EventsWatcherConfig::default(); watcher .run( chain_id, - client.into(), + ctx, Arc::new(store), event_watcher_config, vec![Box::::default()], diff --git a/crates/price-oracle-backends/Cargo.toml b/crates/price-oracle-backends/Cargo.toml new file mode 100644 index 000000000..0ca30e6f4 --- /dev/null +++ b/crates/price-oracle-backends/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "webb-price-oracle-backends" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +documentation = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } + +[dependencies] +webb-relayer-utils = { workspace = true } +webb-relayer-store = { workspace = true } +webb-chains-info = { workspace = true } + +async-trait = { workspace = true } +futures = { workspace = true } +serde = { workspace = true } +webb = { workspace = true } +# Used by ethers (but we need it to be vendored with the lib). +native-tls = { workspace = true, optional = true } + +chrono = { version = "0.4.23", features = ["serde"] } +typed-builder = { workspace = true } +# Backends for the price oracle. +reqwest = { workspace = true, features = ["json"], optional = true } + + +[dev-dependencies] +tokio = { workspace = true } +axum = { workspace = true, features = ["macros"] } + +[features] +default = ["std"] +coingecko = ["dep:reqwest"] +std = [] diff --git a/crates/price-oracle-backends/src/cached.rs b/crates/price-oracle-backends/src/cached.rs new file mode 100644 index 000000000..ec9b1d680 --- /dev/null +++ b/crates/price-oracle-backends/src/cached.rs @@ -0,0 +1,210 @@ +use std::{collections::HashSet, time::Duration}; + +use chrono::{DateTime, NaiveDateTime, Utc}; +use webb_relayer_store::TokenPriceCacheStore; +use webb_relayer_utils::Result; + +/// A price backend that caches the price data in a local database +/// +/// The cache is used to reduce the number of requests to the source and to improve the performance. +/// +/// **Note:** depending on the configuration, this backend may be used to return the last saved price +/// data even if the source is unavailable, which may lead to incorrect price data. +#[derive(Debug, Clone, typed_builder::TypedBuilder)] +pub struct CachedPriceBackend { + /// The price backend + backend: B, + /// The local data store used for caching + store: S, + /// The cache expiration time. + /// + /// If the cache is older than this value, it will be refreshed + /// from the source backend. + /// + /// If the value is `None`, the cache will never expire + /// and will never be refreshed. **This may lead to incorrect price data.** + /// + /// Use this option only if you are sure that the source backend is always available. + /// Otherwise, use a reasonable value, The default value is `15 minutes`. + /// see [`Self::use_cache_if_source_unavailable`] and [`Self::even_if_expired`] + /// for fine tuning the cache behavior. + #[builder(default = Some(Duration::from_secs(15 * 60)))] + cache_expiration: Option, + /// Specifies whether the cache should be returned even if the source is unavailable + /// + /// If the value is `true`, the cache will be returned even if the source + /// backend is unavailable unless the cache is expired. + /// + /// see [`Self::even_if_expired`] if you want to return the cache even if it is expired. + #[builder(setter(strip_bool))] + use_cache_if_source_unavailable: bool, + /// Specifies whether the cache should be returned even if it is expired + /// in case the source is unavailable. + /// + /// see [`Self::use_cache_if_source_unavailable`] if you want to return the cache + /// even if the source is unavailable. + #[builder(setter(strip_bool))] + even_if_expired: bool, +} + +/// A cached price data +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub struct CachedPrice { + /// The Cached Price of the token + pub price: f64, + /// The timestamp of the cached price + pub timestamp: i64, +} + +impl CachedPriceBackend +where + B: super::PriceBackend, + S: TokenPriceCacheStore, +{ + /// Returns the cache expiration duration + pub const fn cache_expiration(&self) -> Option { + self.cache_expiration + } + + /// Returns `true` if the cache should be returned even if the source is unavailable + pub const fn use_cache_if_source_unavailable_enabled(&self) -> bool { + self.use_cache_if_source_unavailable + } + + /// Returns the inner price backend + pub const fn inner(&self) -> &B { + &self.backend + } + + /// Returns the inner data store + /// The data store is used for caching the price data + pub const fn store(&self) -> &S { + &self.store + } +} + +#[async_trait::async_trait] +impl super::PriceBackend for CachedPriceBackend +where + B: super::PriceBackend + Clone + 'static, + S: TokenPriceCacheStore + Clone + Send + Sync + 'static, +{ + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + vs_currency: super::FiatCurrency, + ) -> Result { + // The returned prices map + let mut prices = super::PricesMap::new(); + // The tokens that need to be fetched from the source + let mut tokens_to_fetch = HashSet::new(); + + for token in tokens { + let token_key = format!("{token}/{vs_currency}"); + // Check if the token is cached + if let Some(cached) = self.store.get_price(&token_key)? { + let expired = + self.cache_expiration.map_or(false, |expiration| { + let ts = NaiveDateTime::from_timestamp_opt( + cached.timestamp + expiration.as_secs() as i64, + Default::default(), + ) + .expect("Time went backwards"); + DateTime::::from_utc(ts, Utc) < Utc::now() + }); + // If the cache is expired, add the token to the list of tokens to fetch + if expired { + tokens_to_fetch.insert(token.to_owned()); + } else { + prices.insert((*token).to_owned(), cached.price); + } + } else { + // If the token is not cached, add it to the list of tokens to fetch + tokens_to_fetch.insert(token.to_owned()); + } + } + if !tokens_to_fetch.is_empty() { + // Fetch the prices from the source + let token_ids = tokens_to_fetch.iter().copied().collect::>(); + let result = self + .backend + .get_prices_vs_currency(&token_ids, vs_currency) + .await; + let source_unavailable = result.is_err(); + let updated_prices = match result { + Ok(updated_prices) => updated_prices, + Err(err) => { + // If the source is unavailable and the cache is enabled, return the cache + if self.use_cache_if_source_unavailable { + super::PricesMap::new() + } else { + return Err(err); + } + } + }; + + // If the source is unavailable and the cache is enabled and `even_if_expired` is enabled, + // return the cache + if source_unavailable + && self.use_cache_if_source_unavailable + && self.even_if_expired + { + // refetch the cache, and ignore the expiration + for token in tokens { + let token_key = format!("{token}/{vs_currency}"); + if let Some(cached) = self.store.get_price(&token_key)? { + prices.insert((*token).to_owned(), cached.price); + } + } + } + + // Update the cache, only if the source is available + let source_available = !source_unavailable; + if source_available { + for (token, price) in updated_prices { + let token_key = format!("{token}/{vs_currency}"); + prices.insert(token.clone(), price); + self.store.insert_price( + &token_key, + CachedPrice { + price, + timestamp: Utc::now().timestamp(), + }, + )?; + } + } + } + Ok(prices) + } +} + +#[cfg(test)] +mod tests { + use crate::PriceBackend; + + use super::*; + + fn make_backend() -> crate::DummyPriceBackend { + let prices = crate::PricesMap::from_iter([ + (String::from("tTNT"), 0.10), + (String::from("WETH"), 1000.0), + (String::from("USDC"), 1.0), + ]); + crate::DummyPriceBackend::new(prices) + } + + fn make_store() -> webb_relayer_store::InMemoryStore { + webb_relayer_store::InMemoryStore::default() + } + + #[tokio::test] + async fn it_works() { + let backend = CachedPriceBackend::builder() + .backend(make_backend()) + .store(make_store()) + .build(); + let prices = backend.get_prices(&["USDC"]).await.unwrap(); + assert_eq!(prices.len(), 1); + assert_eq!(prices.get("USDC"), Some(&1.0)); + } +} diff --git a/crates/price-oracle-backends/src/coingecko.rs b/crates/price-oracle-backends/src/coingecko.rs new file mode 100644 index 000000000..de630fd0a --- /dev/null +++ b/crates/price-oracle-backends/src/coingecko.rs @@ -0,0 +1,393 @@ +//! Price Backend implementation for `CoinGecko` + +use std::{collections::HashMap, sync::Arc}; + +use futures::TryFutureExt; +use serde::de::DeserializeOwned; +use webb_relayer_utils::Result; + +/// A backend for fetching prices from `CoinGecko` +#[derive(Clone, Debug, typed_builder::TypedBuilder)] +pub struct CoinGeckoBackend { + #[builder( + default = String::from("https://api.coingecko.com/api/v3"), + setter(into) + )] + host: String, + #[builder(default = Arc::new(reqwest::Client::new()))] + client: Arc, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct SimplePriceResponse { + pub(crate) usd: Option, +} + +impl CoinGeckoBackend { + async fn get(&self, endpoint: &str) -> Result { + let url = format!("{}/{}", self.host, endpoint); + self.client + .get(&url) + .send() + .await? + .json() + .await + .map_err(Into::into) + } + + async fn price, Curr: AsRef>( + &self, + ids: &[Id], + vs_currencies: &[Curr], + ) -> Result> { + let ids = ids.iter().map(AsRef::as_ref).collect::>(); + let vs_currencies = + vs_currencies.iter().map(AsRef::as_ref).collect::>(); + let req = format!( + "simple/price?ids={}&vs_currencies={}", + ids.join("%2C"), + vs_currencies.join("%2C") + ); + self.get(&req).await + } +} + +#[async_trait::async_trait] +impl super::PriceBackend for CoinGeckoBackend { + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + currency: super::FiatCurrency, + ) -> Result { + // map token names to coingecko ids + let mut id_to_token = HashMap::new(); + let chains_info = webb_chains_info::chains_info(); + for token in tokens { + let id = chains_info + .iter() + .find_map(|(_, info)| { + info.native_currency + .symbol + .eq(*token) + .then_some(info.native_currency.coingecko_coin_id) + }) + .flatten() + .unwrap_or(*token); + id_to_token.insert(id, *token); + } + let ids = id_to_token.keys().collect::>(); + + let prices: crate::PricesMap = self + .price(&ids, &[currency.to_string().to_lowercase()]) + .map_ok(|m| { + m.into_iter() + .filter_map(|(k, v)| v.usd.map(|price| (k, price))) + .collect() + }) + .await?; + // remap the ids back to token names + let prices = prices + .into_iter() + .filter_map(|(id, price)| { + id_to_token + .get(id.as_str()) + .map(|t| ((*t).to_string(), price)) + }) + .collect(); + Ok(prices) + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use crate::PriceBackend; + + use super::*; + use coingecko_mocked_server::*; + + mod coingecko_mocked_server { + use axum::extract::{Query, State}; + use axum::response::IntoResponse; + use axum::{response::Json, routing::get, Router}; + use std::collections::HashMap; + use std::net::SocketAddr; + use std::sync::atomic::Ordering; + use std::{ + sync::{atomic::AtomicBool, Arc}, + time::Duration, + }; + + fn random_free_port() -> u16 { + std::net::TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap() + .port() + } + + #[derive(Debug, Clone, typed_builder::TypedBuilder)] + pub struct MockServer { + hard_coded_prices: crate::PricesMap, + #[builder( + default = Arc::new(Duration::from_secs(0)), + setter(transform = |d: Duration| Arc::new(d)) + )] + simulated_delay: Arc, + #[builder( + default, + setter(transform = |b: bool| Arc::new(AtomicBool::new(b))), + )] + simulat_server_error: Arc, + } + + pub struct MockedServerHandle { + simulat_server_error: Arc, + backend: super::CoinGeckoBackend, + server_thread: tokio::task::JoinHandle<()>, + } + + impl Drop for MockedServerHandle { + fn drop(&mut self) { + self.simulat_server_error.store(true, Ordering::Relaxed); + self.server_thread.abort(); + } + } + + impl MockedServerHandle { + pub fn backend(&self) -> super::CoinGeckoBackend { + self.backend.clone() + } + + /// Simulate a server error, this will cause all requests to fail + pub fn simulate_server_error(&self, v: bool) { + self.simulat_server_error.store(v, Ordering::Relaxed); + } + } + + #[derive(serde::Deserialize)] + struct RequestQuery { + ids: String, + #[allow(dead_code)] + vs_currencies: String, + } + + #[derive(Clone)] + struct MockState { + hard_coded_prices: crate::PricesMap, + simulated_delay: Arc, + simulat_server_error: Arc, + } + + async fn prices_handler( + Query(query): Query, + State(mock_state): State, + ) -> impl IntoResponse { + if mock_state.simulat_server_error.load(Ordering::Relaxed) { + return Err(Json("Simulated Server Error")); + } + + let mut prices = HashMap::new(); + for token in query.ids.split(',') { + if let Some(price) = mock_state.hard_coded_prices.get(token) { + prices.insert( + token.to_string(), + super::SimplePriceResponse { usd: Some(*price) }, + ); + } + } + + tokio::time::sleep(*mock_state.simulated_delay).await; + Ok(Json(prices)) + } + + impl MockServer { + pub fn spwan(self) -> MockedServerHandle { + let simulat_server_error = self.simulat_server_error.clone(); + let port = random_free_port(); + let addr = SocketAddr::from(([127, 0, 0, 1], port)); + let url = format!("http://{addr}/api/v3"); + let backend = + super::CoinGeckoBackend::builder().host(url).build(); + let handle = tokio::spawn(async move { + let api_v3 = Router::new() + .route("/simple/price", get(prices_handler)); + let app = Router::new() + .nest("/api/v3/", api_v3) + .with_state(MockState { + hard_coded_prices: self.hard_coded_prices, + simulated_delay: self.simulated_delay.clone(), + simulat_server_error: self + .simulat_server_error + .clone(), + }); + + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); + }); + + MockedServerHandle { + simulat_server_error, + backend, + server_thread: handle, + } + } + } + } + + fn build_hard_coded_prices() -> crate::PricesMap { + let mut prices = crate::PricesMap::new(); + prices.insert(String::from("ethereum"), 1000.0); + prices.insert(String::from("matic-network"), 1.0); + prices + } + + #[tokio::test] + async fn it_works() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + let prices = backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + } + + #[tokio::test] + async fn fails_when_server_errors() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + handle.simulate_server_error(true); + let prices = backend.get_prices(&["ETH", "MATIC"]).await; + assert!(prices.is_err()); + } + + #[tokio::test] + async fn should_keep_working_if_cached() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + let cached_backend = crate::CachedPriceBackend::builder() + .backend(backend) + .store(webb_relayer_store::InMemoryStore::default()) + .use_cache_if_source_unavailable() + // Disable cache expiration + .cache_expiration(None) + .build(); + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + + // Simulate a server error + handle.simulate_server_error(true); + // The cache should still work + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + } + + #[tokio::test] + async fn should_not_work_if_the_cache_expired() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + let cached_backend = crate::CachedPriceBackend::builder() + .backend(backend) + .store(webb_relayer_store::InMemoryStore::default()) + .cache_expiration(Some(Duration::from_secs(2))) + .use_cache_if_source_unavailable() + .build(); + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + + // Simulate a server error + handle.simulate_server_error(true); + // The cache should still work + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + + // Wait for the cache to expire + tokio::time::sleep(Duration::from_secs(2)).await; + // The cache should not work + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), None); + assert_eq!(prices.get("MATIC"), None); + } + + #[tokio::test] + async fn should_keep_working_if_cache_expired() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + let cached_backend = crate::CachedPriceBackend::builder() + .backend(backend) + .store(webb_relayer_store::InMemoryStore::default()) + .cache_expiration(Some(Duration::from_secs(2))) + .use_cache_if_source_unavailable() + .even_if_expired() + .build(); + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + + // Simulate a server error + handle.simulate_server_error(true); + // The cache should still work + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + + // Wait for the cache to expire + tokio::time::sleep(Duration::from_secs(2)).await; + // The cache should still work + let prices = + cached_backend.get_prices(&["ETH", "MATIC"]).await.unwrap(); + assert_eq!(prices.get("ETH"), Some(&1000.0)); + assert_eq!(prices.get("MATIC"), Some(&1.0)); + } + + #[tokio::test] + async fn should_fail_if_token_not_listed_or_mapped() { + let mock_server = MockServer::builder() + .hard_coded_prices(build_hard_coded_prices()) + .build(); + let handle = mock_server.spwan(); + // Wait for the server to start + tokio::time::sleep(Duration::from_millis(100)).await; + let backend = handle.backend(); + let prices = backend.get_prices(&["BTC"]).await.unwrap(); + assert!(prices.is_empty()); + } +} diff --git a/crates/price-oracle-backends/src/dummy.rs b/crates/price-oracle-backends/src/dummy.rs new file mode 100644 index 000000000..a4529feac --- /dev/null +++ b/crates/price-oracle-backends/src/dummy.rs @@ -0,0 +1,73 @@ +use webb_relayer_utils::Result; + +/// A Dummy Price Oracle Backend +/// +/// This backend is useful for testing purposes, it always returns the same price data +/// that is configured initially while creating the backend. +#[derive(Debug, Clone)] +pub struct DummyPriceBackend { + /// The price data that is returned by the backend + prices: super::PricesMap, +} + +impl DummyPriceBackend { + /// Creates a new dummy price backend + #[must_use] + pub fn new(prices: super::PricesMap) -> Self { + Self { prices } + } +} + +#[async_trait::async_trait] +impl super::PriceBackend for DummyPriceBackend { + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + _currency: super::FiatCurrency, + ) -> Result { + let result = tokens + .iter() + .copied() + .filter_map(|token| { + self.prices + .get(token) + .copied() + .map(|price| (token.to_owned(), price)) + }) + .collect(); + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use crate::PriceBackend; + + use super::*; + + #[tokio::test] + async fn it_works() { + let backend = DummyPriceBackend::new( + vec![("ETH".to_string(), 100.0), ("DOT".to_string(), 10.0)] + .into_iter() + .collect(), + ); + let prices = backend.get_prices(&["ETH", "DOT"]).await.unwrap(); + assert_eq!(prices.len(), 2); + assert_eq!(prices["ETH"], 100.0); + assert_eq!(prices["DOT"], 10.0); + } + + #[tokio::test] + async fn non_existing_tokens() { + let backend = DummyPriceBackend::new( + vec![("ETH".to_string(), 100.0), ("DOT".to_string(), 10.0)] + .into_iter() + .collect(), + ); + let prices = backend.get_prices(&["ETH", "DOT", "KSM"]).await.unwrap(); + assert_eq!(prices.len(), 2); + assert_eq!(prices["ETH"], 100.0); + assert_eq!(prices["DOT"], 10.0); + } +} diff --git a/crates/price-oracle-backends/src/lib.rs b/crates/price-oracle-backends/src/lib.rs new file mode 100644 index 000000000..4c8526265 --- /dev/null +++ b/crates/price-oracle-backends/src/lib.rs @@ -0,0 +1,156 @@ +// Copyright 2022 Webb Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Price Oracle Backends +//! +//! A Price Oracle Backend is a service that provides price data for a list of tokens symbols in +//! the requested currency. The backend is responsible for fetching the price data from the source +//! and converting it to the requested currency. +//! +//! The backend may optionally cache the price data in a local database. The cache is used to +//! reduce the number of requests to the source and to improve the performance of the backend. +//! +//! As of now, the following backends are supported: +//! - [CoinGecko](https://www.coingecko.com/en/api) +//! +//! ## Usage +//! ```rust,ignore +//! use webb_price_oracle_backends::{CoinGeckoBackend, PriceBackend}; +//! let backend = CoinGeckoBackend::builder().build(); +//! let prices = backend.get_prices(&["ETH", "BTC"]).await.unwrap(); +//! let eth_price = prices.get("ETH").unwrap(); +//! let btc_price = prices.get("BTC").unwrap(); +//! ``` +//! ## With Cache +//! ```rust,ignore +//! use price_oracle_backends::{CoinGeckoBackend, PriceBackend, CachedPriceBackend}; +//! use webb_relayer_store::SledStore; +//! let store = SledStore::temporary()?; +//! let backend = CoinGeckoBackend::builder().build(); +//! let backend = CachedPriceBackend::new(backend, store); +//! let prices = backend.get_prices(&["ETH", "BTC"]).await.unwrap(); +//! let eth_price = prices.get("ETH").unwrap(); +//! let btc_price = prices.get("BTC").unwrap(); +//! ``` +//! +//! ## Features flags +//! - `coingecko` - enables `CoinGecko` backend + +#![deny(unsafe_code)] +#![warn(missing_docs)] + +use std::{fmt::Display, sync::Arc}; + +use webb_relayer_utils::Result; + +/// Cached Backend Module +mod cached; +/// `CoinGecko` Backend +#[cfg(feature = "coingecko")] +mod coingecko; +/// A Dymmy Price Backend +mod dummy; +/// Merger Backend Module +mod merger; + +#[cfg(feature = "coingecko")] +pub use crate::coingecko::CoinGeckoBackend; +pub use cached::CachedPriceBackend; +pub use dummy::DummyPriceBackend; +pub use merger::PriceOracleMerger; + +/// A List of supported fiat currencies +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum FiatCurrency { + /// United States Dollar + /// + /// The default currency + #[default] + USD, + /// Euro + EUR, + /// British Pound + GBP, +} + +impl FiatCurrency { + /// Returns the currency symbol + #[must_use] + pub fn symbol(&self) -> &'static str { + match self { + Self::USD => "$", + Self::EUR => "€", + Self::GBP => "£", + } + } +} + +impl Display for FiatCurrency { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::USD => write!(f, "USD"), + Self::EUR => write!(f, "EUR"), + Self::GBP => write!(f, "GBP"), + } + } +} + +/// A type alias for a map of token symbols to prices +type PricesMap = std::collections::HashMap; + +/// A trait for a price backend +#[async_trait::async_trait] +pub trait PriceBackend: Send + Sync { + /// Returns the prices for the given tokens in the USD currency + /// + /// This is a convenience method that calls `get_prices_vs_currency` with the default currency + async fn get_prices(&self, tokens: &[&str]) -> Result { + PriceBackend::get_prices_vs_currency( + self, + tokens, + FiatCurrency::default(), + ) + .await + } + /// Returns the prices for the given tokens in the requested currency + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + vs_currency: FiatCurrency, + ) -> Result; +} + +#[async_trait::async_trait] +impl PriceBackend for Arc +where + O: PriceBackend + ?Sized, +{ + async fn get_prices(&self, tokens: &[&str]) -> Result { + PriceBackend::get_prices_vs_currency( + self.as_ref(), + tokens, + FiatCurrency::default(), + ) + .await + } + + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + vs_currency: FiatCurrency, + ) -> Result { + PriceBackend::get_prices_vs_currency(self.as_ref(), tokens, vs_currency) + .await + } +} diff --git a/crates/price-oracle-backends/src/merger.rs b/crates/price-oracle-backends/src/merger.rs new file mode 100644 index 000000000..69c3399eb --- /dev/null +++ b/crates/price-oracle-backends/src/merger.rs @@ -0,0 +1,87 @@ +use webb_relayer_utils::Result; + +/// A Price Oracle Merger backend is a backend that builds on top of other backends and merges the +/// price data from the underlying backends. The merger backend is useful when you want to use +/// multiple backends to fetch the price data and merge the results. For example, you can use the +/// `CoinGecko` backend to fetch the price data and the `CoinMarketCap` backend to fetch the price +/// data for the tokens that are not supported by the `CoinGecko` backend. +/// +/// ## Semantics +/// +/// The merger backend will fetch the price data from the underlying backends and merge the results, +/// the following rules are applied: +/// - If the price data is available in all backends, the price data from the **last** merged backend is used. +/// - If the price is not available in any of the backends, the price data is not included in the result. +#[allow(clippy::module_name_repetitions)] +pub struct PriceOracleMerger { + /// The underlying backends + backends: Vec>, +} + +impl PriceOracleMerger { + /// Creates a new `PriceOracleMergerBuilder` + #[must_use] + pub fn builder() -> PriceOracleMergerBuilder { + PriceOracleMergerBuilder { + backends: Vec::default(), + } + } +} + +/// A builder for the `PriceOracleMerger` +pub struct PriceOracleMergerBuilder { + backends: Vec>, +} + +impl PriceOracleMergerBuilder { + /// Merges the price data from the underlying backends + /// + /// The price data is merged according to the following rules: + /// - If the price data is available in all backends, the price data from the **last** merged backend is used. + /// - If the price is not available in any of the backends, the price data is not included in the result. + #[must_use] + pub fn merge(mut self, backend: Box) -> Self { + self.backends.push(backend); + self + } + + /// Builds the `PriceOracleMerger` + #[must_use] + pub fn build(self) -> PriceOracleMerger { + PriceOracleMerger { + backends: self.backends, + } + } +} + +impl PriceOracleMerger { + /// Merges the price data from the underlying backends + /// + /// The price data is merged according to the following rules: + /// - If the price data is available in all backends, the price data from the **last** merged backend is used. + /// - If the price is not available in any of the backends, the price data is not included in the result. + pub fn merge( + &mut self, + backend: Box, + ) -> &mut Self { + self.backends.push(backend); + self + } +} + +#[async_trait::async_trait] +impl super::PriceBackend for PriceOracleMerger { + async fn get_prices_vs_currency( + &self, + tokens: &[&str], + currency: super::FiatCurrency, + ) -> Result { + let mut prices = super::PricesMap::new(); + for backend in &self.backends { + let backend_prices = + backend.get_prices_vs_currency(tokens, currency).await?; + prices.extend(backend_prices); + } + Ok(prices) + } +} diff --git a/crates/proof-generation/Cargo.toml b/crates/proof-generation/Cargo.toml new file mode 100644 index 000000000..e4a6bbc4c --- /dev/null +++ b/crates/proof-generation/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "webb-proof-generation" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools = "0.10.5" +webb = { workspace = true } +hex = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +ark-bn254 = { version = "^0.3.0", default-features = true, features = ["curve"] } +ark-ff = { version = "^0.3.0", default-features = true } +arkworks-setups = { version = "1.2.1", features = ["r1cs"], default-features = false } +webb-circom-proving = { path = "../circom-proving", default-features = false } +ark-circom = { git = "https://github.com/vacp2p/ark-circom", branch = "wasm", default-features = false, features = ["circom-2"] } +num-bigint = { version = "0.4", default-features = false, features = ["rand", "serde"] } +ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", default-features = false, features = ["parallel"] } +wasmer = { version = "2.3.0", default-features = false } +ark-relations = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = ["derive"] } +[features] +default = ["wasmer/sys-default" ] diff --git a/crates/proof-generation/src/batch_prover.rs b/crates/proof-generation/src/batch_prover.rs new file mode 100644 index 000000000..148f161a2 --- /dev/null +++ b/crates/proof-generation/src/batch_prover.rs @@ -0,0 +1,188 @@ +use ark_bn254::{Bn254, Fr}; +use ark_circom::{read_zkey, WitnessCalculator}; +use ark_groth16::{Proof as ArkProof, ProvingKey}; +use ark_relations::r1cs::ConstraintMatrices; +use num_bigint::BigInt; +use std::{fs::File, sync::Mutex}; +use webb_circom_proving::{ + circom_from_folder, generate_proof, ProofError, ProverPath, +}; + +pub struct BatchProofInput { + pub old_root: BigInt, + pub new_root: BigInt, + pub path_indices: BigInt, + pub path_elements: Vec, + pub leaves: Vec, + pub args_hash: BigInt, +} + +pub struct MaspBatchProver { + pub wc: &'static Mutex, + pub zkey: (ProvingKey, ConstraintMatrices), +} + +impl MaspBatchProver { + pub fn new(path: ProverPath) -> Self { + let mut file = File::open(path.zkey) + .expect("Could not find file at provided path"); + let zkey = read_zkey(&mut file).expect("Failed to read zkey"); + + let wc = circom_from_folder(&path.wasm); + + Self { wc, zkey } + } + + pub fn gen_proof( + &self, + proof_input: BatchProofInput, + ) -> Result<(ArkProof, Vec), ProofError> { + let inputs_for_proof = [ + ("oldRoot", vec![proof_input.old_root.clone()]), + ("newRoot", vec![proof_input.new_root.clone()]), + ("pathIndices", vec![proof_input.path_indices.clone()]), + ("pathElements", proof_input.path_elements.clone()), + ("leaves", proof_input.leaves.clone()), + ("argsHash", vec![proof_input.args_hash]), + ]; + let num_inputs = self.zkey.1.num_instance_variables; + + let (proof, full_assignment) = + generate_proof(self.wc, &self.zkey, inputs_for_proof.clone())?; + + let inputs_for_verification = &full_assignment + .get(1..num_inputs) + .expect( + "could not slice full_assignment to get inputs_for_verification (should not happen)", + ); + Ok((proof, inputs_for_verification.to_vec())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use num_bigint::Sign; + use std::str::FromStr; + use webb_circom_proving::verify_proof; + + #[test] + #[ignore = "fails when this runs with the other proof test in sequence"] + fn test_gen_proof() { + let zkey_path = + "../../tests/solidity-fixtures/batch-tree/4/circuit_final.zkey"; + let wasm_path = "../../tests/solidity-fixtures/batch-tree/4/batchMerkleTreeUpdate_4.wasm"; + let prover = MaspBatchProver::new(ProverPath { + zkey: zkey_path.to_string(), + wasm: wasm_path.to_string(), + }); + + // pre-generated inputs + let old_root = BigInt::from_str("19476726467694243150694636071195943429153087843379888650723427850220480216251").unwrap(); + let new_root = BigInt::from_str("20916481022825571888408619237229551581730547986478389731865872078730284403598").unwrap(); + let path_indices = BigInt::from(0); + let path_elements = [ + "15126246733515326086631621937388047923581111613947275249184377560170833782629", + "6404200169958188928270149728908101781856690902670925316782889389790091378414", + "17903822129909817717122288064678017104411031693253675943446999432073303897479", + "11423673436710698439362231088473903829893023095386581732682931796661338615804", + "10494842461667482273766668782207799332467432901404302674544629280016211342367", + "17400501067905286947724900644309270241576392716005448085614420258732805558809", + "7924095784194248701091699324325620647610183513781643345297447650838438175245", + "3170907381568164996048434627595073437765146540390351066869729445199396390350", + "21224698076141654110749227566074000819685780865045032659353546489395159395031", + "18113275293366123216771546175954550524914431153457717566389477633419482708807", + "1952712013602708178570747052202251655221844679392349715649271315658568301659", + "18071586466641072671725723167170872238457150900980957071031663421538421560166", + "9993139859464142980356243228522899168680191731482953959604385644693217291503", + "14825089209834329031146290681677780462512538924857394026404638992248153156554", + "4227387664466178643628175945231814400524887119677268757709033164980107894508", + "177945332589823419436506514313470826662740485666603469953512016396504401819", + "4236715569920417171293504597566056255435509785944924295068274306682611080863", + "8055374341341620501424923482910636721817757020788836089492629714380498049891" + ].map(|str_val| BigInt::from_str(str_val).unwrap()); + let leaves = [ + "0x2c01fb4e73e2fddb08de19a3d9d6bed12b7e82fe0fb857443c28cd804c121a3b", + "0x15ac3d30fd4b211eab59a344dc48daf12a78d8cd3c410615e978e0089644b46a", + "0x1f42c6d6a0b3d7f5cec6be642d8b87d8457ed60dca8382a06f0c58978c1c0a94", + "0x24f90c50ec7a935db677cc5d548f0f3cdf917a56bbd3217d2006054edeb278f6" + ].map(|hex_val| BigInt::from_bytes_be(Sign::Plus, &hex::decode(&hex_val[2..]).unwrap())); + let args_hash = BigInt::from_str("14154321161286109651686014554828843243662400761161800455141360660751023937653").unwrap(); + + let (proof, inputs_for_verification) = prover + .gen_proof(BatchProofInput { + old_root, + new_root, + path_indices, + path_elements: path_elements.to_vec(), + leaves: leaves.to_vec(), + args_hash, + }) + .unwrap(); + + let did_proof_work = + verify_proof(&prover.zkey.0.vk, &proof, inputs_for_verification) + .unwrap(); + + assert!(did_proof_work, "failed proof verification"); + } + #[test] + #[ignore = "Should not be able to produce proof but it does"] + fn test_gen_proof_fails_with_incorrect_root() { + let zkey_path = + "../../tests/solidity-fixtures/batch-tree/4/circuit_final.zkey"; + let wasm_path = "../../tests/solidity-fixtures/batch-tree/4/batchMerkleTreeUpdate_4.wasm"; + let prover = MaspBatchProver::new(ProverPath { + zkey: zkey_path.to_string(), + wasm: wasm_path.to_string(), + }); + + // pre-generated inputs + let old_root = BigInt::from_str("19476726467694243150694636071195943429153087843379888650723427850220480216251").unwrap(); + let new_root = BigInt::from_str("0").unwrap(); + let path_indices = BigInt::from(0); + let path_elements = [ + "15126246733515326086631621937388047923581111613947275249184377560170833782629", + "6404200169958188928270149728908101781856690902670925316782889389790091378414", + "17903822129909817717122288064678017104411031693253675943446999432073303897479", + "11423673436710698439362231088473903829893023095386581732682931796661338615804", + "10494842461667482273766668782207799332467432901404302674544629280016211342367", + "17400501067905286947724900644309270241576392716005448085614420258732805558809", + "7924095784194248701091699324325620647610183513781643345297447650838438175245", + "3170907381568164996048434627595073437765146540390351066869729445199396390350", + "21224698076141654110749227566074000819685780865045032659353546489395159395031", + "18113275293366123216771546175954550524914431153457717566389477633419482708807", + "1952712013602708178570747052202251655221844679392349715649271315658568301659", + "18071586466641072671725723167170872238457150900980957071031663421538421560166", + "9993139859464142980356243228522899168680191731482953959604385644693217291503", + "14825089209834329031146290681677780462512538924857394026404638992248153156554", + "4227387664466178643628175945231814400524887119677268757709033164980107894508", + "177945332589823419436506514313470826662740485666603469953512016396504401819", + "4236715569920417171293504597566056255435509785944924295068274306682611080863", + "8055374341341620501424923482910636721817757020788836089492629714380498049891" + ].map(|str_val| BigInt::from_str(str_val).unwrap()); + let leaves = [ + "0x2c01fb4e73e2fddb08de19a3d9d6bed12b7e82fe0fb857443c28cd804c121a3b", + "0x15ac3d30fd4b211eab59a344dc48daf12a78d8cd3c410615e978e0089644b46a", + "0x1f42c6d6a0b3d7f5cec6be642d8b87d8457ed60dca8382a06f0c58978c1c0a94", + "0x24f90c50ec7a935db677cc5d548f0f3cdf917a56bbd3217d2006054edeb278f6" + ].map(|hex_val| BigInt::from_bytes_be(Sign::Plus, &hex::decode(&hex_val[2..]).unwrap())); + let args_hash = BigInt::from_str("14154321161286109651686014554828843243662400761161800455141360660751023937653").unwrap(); + + let proof = prover.gen_proof(BatchProofInput { + old_root, + new_root, + path_indices, + path_elements: path_elements.to_vec(), + leaves: leaves.to_vec(), + args_hash, + }); + + let (proof, inputs_for_verification) = proof.unwrap(); + let did_proof_work = + verify_proof(&prover.zkey.0.vk, &proof, inputs_for_verification) + .unwrap(); + + assert!(!did_proof_work, "worked???????"); + } +} diff --git a/crates/proof-generation/src/lib.rs b/crates/proof-generation/src/lib.rs new file mode 100644 index 000000000..e3bba750e --- /dev/null +++ b/crates/proof-generation/src/lib.rs @@ -0,0 +1,6 @@ +pub mod batch_prover; +pub mod proof_delegation; +pub mod types; +pub use batch_prover::*; +pub use proof_delegation::*; +pub use types::*; diff --git a/crates/proof-generation/src/proof_delegation.rs b/crates/proof-generation/src/proof_delegation.rs new file mode 100644 index 000000000..e2c14e9c0 --- /dev/null +++ b/crates/proof-generation/src/proof_delegation.rs @@ -0,0 +1,605 @@ +use crate::types::MaspDelegatedProofInputsJson; +use ark_bn254::{Bn254, Fr}; +use ark_circom::{read_zkey, WitnessCalculator}; +use ark_groth16::{Proof as ArkProof, ProvingKey}; +use ark_relations::r1cs::ConstraintMatrices; +use itertools::izip; +use num_bigint::{BigInt, ParseBigIntError}; +use serde::{Deserialize, Serialize}; +use std::{fs::File, str::FromStr, sync::Mutex}; +use webb_circom_proving::{ + circom_from_folder, generate_proof, ProofError, ProverPath, +}; + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct MaspAssetInfo { + pub asset_id: BigInt, + pub token_id: BigInt, +} +impl MaspAssetInfo { + pub fn from_str_values( + asset_id: &str, + token_id: &str, + ) -> Result { + Ok(Self { + asset_id: BigInt::from_str(asset_id)?, + token_id: BigInt::from_str(token_id)?, + }) + } +} +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct MaspInUtxo { + /// The nullifier of the utxo + pub nullifier: BigInt, + /// The amount of the utxo + pub amount: BigInt, + /// The blinding of the utxo + pub blinding: BigInt, + /// Path indices for utxo + pub path_indices: BigInt, + /// Path elements for utxo + pub path_elements: Vec, +} +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct MaspSignature { + pub signature: BigInt, + pub r8x: BigInt, + pub r8y: BigInt, +} +impl MaspSignature { + pub fn from_str_values( + signature: &str, + r8x: &str, + r8y: &str, + ) -> Result { + Ok(Self { + signature: BigInt::from_str(signature)?, + r8x: BigInt::from_str(r8x)?, + r8y: BigInt::from_str(r8y)?, + }) + } +} + +impl MaspInUtxo { + pub fn from_str_values( + nullifier: &str, + amount: &str, + blinding: &str, + path_indices: &str, + path_elements: Vec, + ) -> Result { + Ok(Self { + nullifier: BigInt::from_str(nullifier)?, + amount: BigInt::from_str(amount)?, + blinding: BigInt::from_str(blinding)?, + path_indices: BigInt::from_str(path_indices)?, + path_elements: path_elements + .iter() + .map(|x| BigInt::from_str(x).expect("Error parsing BigInt")) + .collect(), + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct MaspOutUtxo { + /// Commitment of the utxo + pub commitment: BigInt, + /// chain_id of the utxo + pub chain_id: BigInt, + /// public key ((x, y) values) of the utxo + pub pk: Point, + /// amount of the utxo + pub amount: BigInt, + /// blinding of the utxo + pub blinding: BigInt, +} +impl MaspOutUtxo { + pub fn from_str_values( + commitment: &str, + chain_id: &str, + pk_x: &str, + pk_y: &str, + amount: &str, + blinding: &str, + ) -> Result { + Ok(Self { + commitment: BigInt::from_str(commitment)?, + chain_id: BigInt::from_str(chain_id)?, + pk: AuthKey { + x: BigInt::from_str(pk_x)?, + y: BigInt::from_str(pk_y)?, + }, + amount: BigInt::from_str(amount)?, + blinding: BigInt::from_str(blinding)?, + }) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct Point { + pub x: BigInt, + pub y: BigInt, +} +type AuthKey = Point; +impl Point { + pub fn from_str_values(x: &str, y: &str) -> Result { + Ok(Point { + x: BigInt::from_str(x)?, + y: BigInt::from_str(y)?, + }) + } +} + +/// Proof data object for Masp proof delegation. This include the private variables. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct MaspDelegatedProofInput { + /// Public amount of transaction + pub public_amount: BigInt, + /// ext data hash of transaction + pub ext_data_hash: BigInt, + // private asset information for transaction + pub asset: MaspAssetInfo, + // public asset information for transaction + pub public_asset: MaspAssetInfo, + + // data for transaction inputs + pub in_utxos: Vec, + + pub in_signature: MaspSignature, + pub auth_key: AuthKey, + + // data for transaction outputs + pub out_utxos: Vec, + + pub out_signature: MaspSignature, + /// Chain ID of the transaction + pub chain_id: BigInt, + /// roots for membership check on MASP + pub roots: Vec, + + pub whitelisted_asset_ids: Vec, + pub fee_asset: MaspAssetInfo, + + // data for fee transaction inputs + pub fee_in_utxos: Vec, + + pub fee_in_signature: MaspSignature, + // data for fee transaction outputs + pub fee_out_utxos: Vec, + pub fee_out_signature: MaspSignature, + pub fee_auth_key: AuthKey, +} + +impl MaspDelegatedProofInput { + pub fn from_json(path: &str) -> Result { + let file = + File::open(path).expect("Could not find file at provided path"); + let stringified_inputs: MaspDelegatedProofInputsJson = + serde_json::from_reader(&file).expect( + "Failed to produce MaspDelegatedProofInputsJson from file", + ); + println!("stringified_inputs: {:#?}", stringified_inputs); + + let public_amount = + BigInt::from_str(&stringified_inputs.public_amount)?; + let ext_data_hash = + BigInt::from_str(&stringified_inputs.ext_data_hash)?; + + let asset = MaspAssetInfo::from_str_values( + &stringified_inputs.asset_id, + &stringified_inputs.token_id, + )?; + + let public_asset = MaspAssetInfo::from_str_values( + &stringified_inputs.public_asset_id, + &stringified_inputs.public_token_id, + )?; + // get in_utxos + let mut in_utxos: Vec = vec![]; + for (nullifier, amount, blinding, path_indices, path_elements) in izip!( + &stringified_inputs.input_nullifier, + &stringified_inputs.in_amount, + &stringified_inputs.in_blinding, + &stringified_inputs.in_path_indices, + stringified_inputs.in_path_elements, + ) { + let utxo = MaspInUtxo::from_str_values( + nullifier, + amount, + blinding, + path_indices, + path_elements.clone(), + )?; + in_utxos.push(utxo); + } + // get input signature + let in_signature = MaspSignature::from_str_values( + &stringified_inputs.in_signature, + &stringified_inputs.in_r8x, + &stringified_inputs.in_r8y, + )?; + + // get out_utxos + let mut out_utxos: Vec = vec![]; + for (commitment, chain_id, pk_x, pk_y, amount, blinding) in izip!( + &stringified_inputs.output_commitment, + &stringified_inputs.out_chain_id, + &stringified_inputs.out_pk_x, + &stringified_inputs.out_pk_y, + &stringified_inputs.out_amount, + &stringified_inputs.out_blinding, + ) { + let utxo = MaspOutUtxo::from_str_values( + commitment, chain_id, pk_x, pk_y, amount, blinding, + )?; + out_utxos.push(utxo); + } + // get output signature + let out_signature = MaspSignature::from_str_values( + &stringified_inputs.out_signature, + &stringified_inputs.out_r8x, + &stringified_inputs.out_r8y, + )?; + + let chain_id = BigInt::from_str(&stringified_inputs.chain_id)?; + let roots_result: Result, ParseBigIntError> = + stringified_inputs + .roots + .iter() + .map(|x| BigInt::from_str(x)) + .collect(); + let roots: Vec = roots_result?; + let auth_key = AuthKey::from_str_values( + &stringified_inputs.ak_x, + &stringified_inputs.ak_y, + )?; + + let whitelisted_asset_ids_result: Result< + Vec, + ParseBigIntError, + > = stringified_inputs + .whitelisted_asset_ids + .iter() + .map(|x| BigInt::from_str(x)) + .collect(); + let whitelisted_asset_ids: Vec = whitelisted_asset_ids_result?; + + let fee_asset = MaspAssetInfo { + asset_id: BigInt::from_str(&stringified_inputs.fee_asset_id)?, + token_id: BigInt::from_str(&stringified_inputs.fee_token_id)?, + }; + // + // // get fee_in_utxos + let mut fee_in_utxos: Vec = vec![]; + for (nullifier, amount, blinding, path_indices, path_elements) in izip!( + &stringified_inputs.fee_input_nullifier, + &stringified_inputs.fee_in_amount, + &stringified_inputs.fee_in_blinding, + &stringified_inputs.fee_in_path_indices, + stringified_inputs.fee_in_path_elements, + ) { + let utxo = MaspInUtxo::from_str_values( + nullifier, + amount, + blinding, + path_indices, + path_elements.clone(), + )?; + fee_in_utxos.push(utxo); + } + // get input signature + let fee_in_signature = MaspSignature::from_str_values( + &stringified_inputs.fee_in_signature, + &stringified_inputs.fee_in_r8x, + &stringified_inputs.fee_in_r8y, + )?; + + // get fee_out_utxos + let mut fee_out_utxos: Vec = vec![]; + for (commitment, chain_id, pk_x, pk_y, amount, blinding) in izip!( + &stringified_inputs.fee_output_commitment, + &stringified_inputs.fee_out_chain_id, + &stringified_inputs.fee_out_pk_x, + &stringified_inputs.fee_out_pk_y, + &stringified_inputs.fee_out_amount, + &stringified_inputs.fee_out_blinding, + ) { + let utxo = MaspOutUtxo::from_str_values( + commitment, chain_id, pk_x, pk_y, amount, blinding, + )?; + fee_out_utxos.push(utxo); + } + + // get input signature + let fee_out_signature = MaspSignature::from_str_values( + &stringified_inputs.fee_out_signature, + &stringified_inputs.fee_out_r8x, + &stringified_inputs.fee_out_r8y, + )?; + let fee_auth_key = AuthKey::from_str_values( + &stringified_inputs.fee_ak_x, + &stringified_inputs.fee_ak_y, + )?; + Ok(Self { + public_amount, + ext_data_hash, + asset, + public_asset, + in_utxos, + in_signature, + auth_key, + out_utxos, + out_signature, + chain_id, + roots, + whitelisted_asset_ids, + fee_asset, + fee_in_utxos, + fee_in_signature, + fee_out_utxos, + fee_out_signature, + fee_auth_key, + }) + } + + pub fn preprocess(&self) -> [(&'static str, Vec); 49] { + let in_path_elements_flattened: Vec = self + .in_utxos + .iter() + .flat_map(|utxo| utxo.path_elements.clone()) + .collect(); + + let fee_in_path_elements_flattened: Vec = self + .fee_in_utxos + .iter() + .flat_map(|utxo| utxo.path_elements.clone()) + .collect(); + + return [ + ("publicAmount", vec![self.public_amount.clone()]), + ("extDataHash", vec![self.ext_data_hash.clone()]), + ("assetID", vec![self.asset.asset_id.clone()]), + ("tokenID", vec![self.asset.token_id.clone()]), + ("publicAssetID", vec![self.public_asset.asset_id.clone()]), + ("publicTokenID", vec![self.public_asset.token_id.clone()]), + ( + "inputNullifier", + self.in_utxos + .iter() + .map(|utxo| utxo.nullifier.clone()) + .collect(), + ), + ( + "inAmount", + self.in_utxos + .iter() + .map(|utxo| utxo.amount.clone()) + .collect(), + ), + ( + "inBlinding", + self.in_utxos + .iter() + .map(|utxo| utxo.blinding.clone()) + .collect(), + ), + ( + "inPathIndices", + self.in_utxos + .iter() + .map(|utxo| utxo.path_indices.clone()) + .collect(), + ), + ("inPathElements", in_path_elements_flattened), + ("inSignature", vec![self.in_signature.signature.clone()]), + ("inR8x", vec![self.in_signature.r8x.clone()]), + ("inR8y", vec![self.in_signature.r8y.clone()]), + ( + "outputCommitment", + self.out_utxos + .iter() + .map(|utxo| utxo.commitment.clone()) + .collect(), + ), + ( + "outAmount", + self.out_utxos + .iter() + .map(|utxo| utxo.amount.clone()) + .collect(), + ), + ( + "outChainID", + self.out_utxos + .iter() + .map(|utxo| utxo.chain_id.clone()) + .collect(), + ), + ( + "outPk_X", + self.out_utxos + .iter() + .map(|utxo| utxo.pk.x.clone()) + .collect(), + ), + ( + "outPk_Y", + self.out_utxos + .iter() + .map(|utxo| utxo.pk.y.clone()) + .collect(), + ), + ( + "outBlinding", + self.out_utxos + .iter() + .map(|utxo| utxo.blinding.clone()) + .collect(), + ), + ("outSignature", vec![self.out_signature.signature.clone()]), + ("outR8x", vec![self.out_signature.r8x.clone()]), + ("outR8y", vec![self.out_signature.r8y.clone()]), + ("chainID", vec![self.chain_id.clone()]), + ("roots", self.roots.clone()), + ("ak_X", vec![self.auth_key.x.clone()]), + ("ak_Y", vec![self.auth_key.y.clone()]), + ("feeAssetID", vec![self.fee_asset.asset_id.clone()]), + ("whitelistedAssetIDs", self.whitelisted_asset_ids.clone()), + ("feeTokenID", vec![self.fee_asset.token_id.clone()]), + ( + "feeInputNullifier", + self.fee_in_utxos + .iter() + .map(|utxo| utxo.nullifier.clone()) + .collect(), + ), + ( + "feeInAmount", + self.fee_in_utxos + .iter() + .map(|utxo| utxo.amount.clone()) + .collect(), + ), + ( + "feeInBlinding", + self.fee_in_utxos + .iter() + .map(|utxo| utxo.blinding.clone()) + .collect(), + ), + ( + "feeInPathIndices", + self.fee_in_utxos + .iter() + .map(|utxo| utxo.path_indices.clone()) + .collect(), + ), + ("feeInPathElements", fee_in_path_elements_flattened), + ( + "feeInSignature", + vec![self.fee_in_signature.signature.clone()], + ), + ("feeInR8x", vec![self.fee_in_signature.r8x.clone()]), + ("feeInR8y", vec![self.fee_in_signature.r8y.clone()]), + ( + "feeOutputCommitment", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.commitment.clone()) + .collect(), + ), + ( + "feeOutAmount", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.amount.clone()) + .collect(), + ), + ( + "feeOutChainID", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.chain_id.clone()) + .collect(), + ), + ( + "feeOutPk_X", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.pk.x.clone()) + .collect(), + ), + ( + "feeOutPk_Y", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.pk.y.clone()) + .collect(), + ), + ( + "feeOutBlinding", + self.fee_out_utxos + .iter() + .map(|utxo| utxo.blinding.clone()) + .collect(), + ), + ( + "feeOutSignature", + vec![self.fee_out_signature.signature.clone()], + ), + ("feeOutR8x", vec![self.fee_out_signature.r8x.clone()]), + ("feeOutR8y", vec![self.fee_out_signature.r8y.clone()]), + ("fee_ak_X", vec![self.fee_auth_key.x.clone()]), + ("fee_ak_Y", vec![self.fee_auth_key.y.clone()]), + ]; + } +} + +#[derive(Debug, Clone)] +pub struct MaspDelegatedProver { + pub wc: &'static Mutex, + pub zkey: (ProvingKey, ConstraintMatrices), +} + +impl MaspDelegatedProver { + pub fn new(path: ProverPath) -> Self { + let mut file = File::open(path.zkey) + .expect("Could not find file at provided path"); + let zkey = read_zkey(&mut file).expect("Failed to read zkey"); + + let wc = circom_from_folder(&path.wasm); + + Self { wc, zkey } + } + + pub fn gen_proof( + &self, + proof_inputs: &MaspDelegatedProofInput, + ) -> Result<(ArkProof, Vec), ProofError> { + let inputs = proof_inputs.preprocess(); + + let num_inputs = self.zkey.1.num_instance_variables; + + let (proof, full_assignment) = + generate_proof(self.wc, &self.zkey, inputs.clone())?; + let inputs_for_verification = &full_assignment + .get(1..num_inputs) + .expect( + "could not slice full_assignment to get inputs_for_verification", + ); + // todo!(); + Ok((proof, inputs_for_verification.to_vec())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use webb_circom_proving::verify_proof; + + #[test] + #[ignore] + fn test_proof_delegation() { + let zkey_path = + "../../tests/solidity-fixtures/masp_vanchor_2/2/circuit_final.zkey" + .to_string(); + let wasm_path = "../../tests/solidity-fixtures/masp_vanchor_2/2/masp_vanchor_2_2.wasm".to_string(); + + let path = ProverPath::new(zkey_path, wasm_path); + let prover = MaspDelegatedProver::new(path); + + let proof_input = + MaspDelegatedProofInput::from_json("./test_data/proofInputs.json") + .unwrap(); + let (proof, inputs_for_verification) = + prover.gen_proof(&proof_input).unwrap(); + let did_proof_work = + verify_proof(&prover.zkey.0.vk, &proof, inputs_for_verification) + .unwrap(); + + // assert!(false); + assert!(did_proof_work, "failed proof verification"); + } +} diff --git a/crates/proof-generation/src/types.rs b/crates/proof-generation/src/types.rs new file mode 100644 index 000000000..e791e505f --- /dev/null +++ b/crates/proof-generation/src/types.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; +/// Proof data object for Masp proof delegation. This include the private variables. +pub enum ProofGenerationError { + ParseBigIntError, + JsonDecodeError, +} + +/// +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MaspDelegatedProofInputsJson { + pub public_amount: String, + pub ext_data_hash: String, + pub asset_id: String, + pub token_id: String, + pub public_asset_id: String, + pub public_token_id: String, + + // data for transaction inputs + pub input_nullifier: Vec, + pub in_amount: Vec, + pub in_blinding: Vec, + pub in_path_indices: Vec, + pub in_path_elements: Vec>, + + // signature data + pub in_signature: String, + pub in_r8x: String, + pub in_r8y: String, + + // data for transaction outputs + pub output_commitment: Vec, + pub out_amount: Vec, + pub out_chain_id: Vec, + pub out_pk_x: Vec, + pub out_pk_y: Vec, + pub out_blinding: Vec, + + // signature data + pub out_signature: String, + pub out_r8x: String, + pub out_r8y: String, + + pub chain_id: String, + pub roots: Vec, + + pub ak_x: String, + pub ak_y: String, + + pub whitelisted_asset_ids: Vec, + pub fee_asset_id: String, + pub fee_token_id: String, + + // data for transaction inputs + pub fee_input_nullifier: Vec, + pub fee_in_amount: Vec, + pub fee_in_blinding: Vec, + pub fee_in_path_indices: Vec, + pub fee_in_path_elements: Vec>, + + // signature data + pub fee_in_signature: String, + pub fee_in_r8x: String, + pub fee_in_r8y: String, + + // data for transaction outputs + pub fee_output_commitment: Vec, + pub fee_out_amount: Vec, + pub fee_out_chain_id: Vec, + pub fee_out_pk_x: Vec, + pub fee_out_pk_y: Vec, + pub fee_out_blinding: Vec, + + // signature data + pub fee_out_signature: String, + pub fee_out_r8x: String, + pub fee_out_r8y: String, + + pub fee_ak_x: String, + pub fee_ak_y: String, +} diff --git a/crates/proof-generation/test_data/proofInputs.json b/crates/proof-generation/test_data/proofInputs.json new file mode 100644 index 000000000..0be4574fd --- /dev/null +++ b/crates/proof-generation/test_data/proofInputs.json @@ -0,0 +1,215 @@ +{ + "publicAmount": "0", + "extDataHash": "12107054586528137719972760883431891348393603818549313784891162274455080489370", + "assetId": "1", + "tokenId": "0", + "publicAssetId": "1", + "publicTokenId": "0", + "inputNullifier": [ + "20066442245664301779759170661417095176302734670461469744861016692526946358021", + "20115424191626773382009759027181468275184567794626144455050997401629587268509" + ], + "inAmount": [ + "100", + "0" + ], + "inBlinding": [ + "240915721111417247996976399195332430900908454136364647473689576989018624837", + "200144145232445990313774851971890220366427644357708309918046017839982407771" + ], + "inPathIndices": [ + "0", + "0" + ], + "inPathElements": [ + [ + "20561123376013470958372213577079712818154171062156436984288930045709622595725", + "1235979785422671223578916425534999063692621562155550274909409344726175766461", + "15126246733515326086631621937388047923581111613947275249184377560170833782629", + "6404200169958188928270149728908101781856690902670925316782889389790091378414", + "17903822129909817717122288064678017104411031693253675943446999432073303897479", + "11423673436710698439362231088473903829893023095386581732682931796661338615804", + "10494842461667482273766668782207799332467432901404302674544629280016211342367", + "17400501067905286947724900644309270241576392716005448085614420258732805558809", + "7924095784194248701091699324325620647610183513781643345297447650838438175245", + "3170907381568164996048434627595073437765146540390351066869729445199396390350", + "21224698076141654110749227566074000819685780865045032659353546489395159395031", + "18113275293366123216771546175954550524914431153457717566389477633419482708807", + "1952712013602708178570747052202251655221844679392349715649271315658568301659", + "18071586466641072671725723167170872238457150900980957071031663421538421560166", + "9993139859464142980356243228522899168680191731482953959604385644693217291503", + "14825089209834329031146290681677780462512538924857394026404638992248153156554", + "4227387664466178643628175945231814400524887119677268757709033164980107894508", + "177945332589823419436506514313470826662740485666603469953512016396504401819", + "4236715569920417171293504597566056255435509785944924295068274306682611080863", + "8055374341341620501424923482910636721817757020788836089492629714380498049891" + ], + [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + ], + "inSignature": "1571663267957106106299705902842075585057114077933398898010512546718945180508", + "inR8x": "14980672909305510641886434682582836673154836338537169092769586690462962810934", + "inR8y": "3390815196398694776656546963933609141307006128210912055260886375081000062339", + "outputCommitment": [ + "8686952126129969416638325574605988252045283808029620448120838830840977707473", + "14381856767946591817844253890097557453301315920346680926097437413695726462312" + ], + "outAmount": [ + "50", + "50" + ], + "outChainId": [ + "1099511659113", + "1099511659113" + ], + "outPkX": [ + "21778385006660203593977198840604957515590867200865102661727566801598433675920", + "1946554141372224410593216813208817913541512247413374474003615683956861262858" + ], + "outPkY": [ + "15480559963935683343749062970481988802497648066128532205017018817195025064050", + "8530598566996918195930157531532563080871487587403592681463007573281062079666" + ], + "outBlinding": [ + "414233075606495043384342425407650489745258238516429711891717698086260079140", + "427561548644594707822464610608838621554643795365267761898280583472582521364" + ], + "outSignature": "2056288993932049650731726505063500018484530355635091722155178369352352181456", + "outR8x": "15383845423582011243026766813070129408256417149500267036692899786547239942069", + "outR8y": "11350305182630626645720599832981008668860831116389191914357093421047426970491", + "chainId": "1099511659113", + "roots": [ + "11083015707604601786373124650930023664049903583865389023635028493522681186400", + "8055374341341620501424923482910636721817757020788836089492629714380498049891" + ], + "akX": "9189336295397074439564342079122683600131693064521587339735374421583866427222", + "akY": "17860414471178772904921812379495624479741304450620096798254511348616349806638", + "feeAssetId": "1", + "whitelistedAssetIds": [ + "1", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ], + "feeTokenId": "0", + "feeInputNullifier": [ + "20692833642837932284073130791445232319665348565733077481277042511956228391172", + "15896332140642666814181828309307637907605081926438364159110973329887227423808" + ], + "feeInAmount": [ + "10", + "0" + ], + "feeInBlinding": [ + "108676242766680556325957238399488739248824271153387795145076897074881878773", + "296546551565178457185105348449202123701052328285806077429233008576120382428" + ], + "feeInPathIndices": [ + "1", + "0" + ], + "feeInPathElements": [ + [ + "7768636828678119355666198554461987256541047943562002410694756446344190681335", + "1235979785422671223578916425534999063692621562155550274909409344726175766461", + "15126246733515326086631621937388047923581111613947275249184377560170833782629", + "6404200169958188928270149728908101781856690902670925316782889389790091378414", + "17903822129909817717122288064678017104411031693253675943446999432073303897479", + "11423673436710698439362231088473903829893023095386581732682931796661338615804", + "10494842461667482273766668782207799332467432901404302674544629280016211342367", + "17400501067905286947724900644309270241576392716005448085614420258732805558809", + "7924095784194248701091699324325620647610183513781643345297447650838438175245", + "3170907381568164996048434627595073437765146540390351066869729445199396390350", + "21224698076141654110749227566074000819685780865045032659353546489395159395031", + "18113275293366123216771546175954550524914431153457717566389477633419482708807", + "1952712013602708178570747052202251655221844679392349715649271315658568301659", + "18071586466641072671725723167170872238457150900980957071031663421538421560166", + "9993139859464142980356243228522899168680191731482953959604385644693217291503", + "14825089209834329031146290681677780462512538924857394026404638992248153156554", + "4227387664466178643628175945231814400524887119677268757709033164980107894508", + "177945332589823419436506514313470826662740485666603469953512016396504401819", + "4236715569920417171293504597566056255435509785944924295068274306682611080863", + "8055374341341620501424923482910636721817757020788836089492629714380498049891" + ], + [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + ], + "feeInSignature": "1894239725255269608691621705597977787087698801789907291891859510360735495075", + "feeInR8x": "14741724364588503638170877664053819516696001690554799034042331713969010450746", + "feeInR8y": "3594998237501884778970732699685131296827263890108415085006050641274131938223", + "feeOutputCommitment": [ + "7545660529563368526063452536531614926181976666632103854369186233528523849617", + "16958762377781779697312937664974812303538255716040226409612139245123341543565" + ], + "feeOutAmount": [ + "10", + "0" + ], + "feeOutChainId": [ + "1099511659113", + "1099511659113" + ], + "feeOutPkX": [ + "5641136164266237237540980103097857939895456072910990413862634852179270943942", + "15326483518462525705500845104268897852363901711640066991392478490533394700783" + ], + "feeOutPkY": [ + "12606480411899084254328561321635709425171391380527893117958148299140156720668", + "3839138281715422208789703902086955172370867999097601953163046165896475045221" + ], + "feeOutBlinding": [ + "446135780701220568058555226150401723199080448968956174273748563033222466266", + "51491615751767856307573817928480966847568960083437221756594942523125670251" + ], + "feeOutSignature": "2609341987950351951531407693829264399627872371554588672417684083056739032161", + "feeOutR8x": "11916820110511333685724688186906397200869320257318543141907714428837233042095", + "feeOutR8y": "5681033403856501870806510471936449253585235674335930226707876188776772240715", + "feeAkX": "9189336295397074439564342079122683600131693064521587339735374421583866427222", + "feeAkY": "17860414471178772904921812379495624479741304450620096798254511348616349806638" +} diff --git a/crates/proposal-signing-backends/Cargo.toml b/crates/proposal-signing-backends/Cargo.toml index 5863f5a82..99ea56869 100644 --- a/crates/proposal-signing-backends/Cargo.toml +++ b/crates/proposal-signing-backends/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-proposal-signing-backends" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -11,9 +11,9 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -webb-relayer-types = { path = "../relayer-types" } -webb-relayer-store = { path = "../relayer-store" } -webb-relayer-utils = { path = "../relayer-utils" } +webb-relayer-types = { workspace = true } +webb-relayer-store = { workspace = true } +webb-relayer-utils = { workspace = true } async-trait = { workspace = true } tracing = { workspace = true } @@ -28,7 +28,13 @@ native-tls = { workspace = true, optional = true } webb-proposals = { workspace = true } ethereum-types = { workspace = true } -typed-builder = "0.12.0" +parking_lot = { workspace = true } +typed-builder = { workspace = true } +impl-trait-for-tuples = "0.2" + +[dev-dependencies] +tracing-subscriber = { workspace = true } +rand = { workspace = true } [features] default = ["std"] diff --git a/crates/proposal-signing-backends/src/dkg.rs b/crates/proposal-signing-backends/src/dkg.rs index 3b7ed9f92..aebb5cc6e 100644 --- a/crates/proposal-signing-backends/src/dkg.rs +++ b/crates/proposal-signing-backends/src/dkg.rs @@ -1,36 +1,34 @@ use std::sync::Arc; -use futures::StreamExt; use tokio::sync::Mutex; -use webb::substrate::dkg_runtime::api::runtime_types::webb_proposals::header::{TypedChainId, ResourceId}; -use webb::substrate::dkg_runtime::api::runtime_types::webb_proposals::nonce::Nonce; +use webb::substrate::tangle_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::{TypedChainId, ResourceId}; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::nonce::Nonce; use webb::substrate::subxt::{OnlineClient, PolkadotConfig}; -use sp_core::sr25519::Pair as Sr25519Pair; +use webb::evm::ethers::utils; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::proposal::{Proposal, ProposalKind}; use webb_proposals::ProposalTrait; use webb::substrate::scale::{Encode, Decode}; use webb_relayer_utils::metric; -use webb::substrate::subxt::tx::{PairSigner, TxStatus as TransactionStatus}; -use webb::substrate::dkg_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb_relayer_store::{QueueStore, SledStore}; +use webb_relayer_store::sled::SledQueueKey; +use webb_relayer_utils::static_tx_payload::TypeErasedStaticTxPayload; + type DkgConfig = PolkadotConfig; type DkgClient = OnlineClient; /// A ProposalSigningBackend that uses the DKG System for Signing Proposals. +#[derive(typed_builder::TypedBuilder)] pub struct DkgProposalSigningBackend { - client: DkgClient, - pair: PairSigner, - typed_chain_id: webb_proposals::TypedChainId, -} - -impl DkgProposalSigningBackend { - pub fn new( - client: OnlineClient, - pair: PairSigner, - typed_chain_id: webb_proposals::TypedChainId, - ) -> Self { - Self { - client, - pair, - typed_chain_id, - } - } + #[builder(setter(into))] + pub client: DkgClient, + /// Something that implements the QueueStore trait. + #[builder(setter(into))] + store: Arc, + /// The chain id of the chain that this backend is running on. + /// + /// This used as the source chain id for the proposals. + #[builder(setter(into))] + src_chain_id: webb_proposals::TypedChainId, } //AnchorUpdateProposal for evm @@ -42,9 +40,8 @@ impl super::ProposalSigningBackend for DkgProposalSigningBackend { ) -> webb_relayer_utils::Result { let header = proposal.header(); let resource_id = header.resource_id(); - let src_chain_id = - webb_proposals_typed_chain_converter(self.typed_chain_id); + webb_proposals_typed_chain_converter(self.src_chain_id); let chain_nonce_addrs = RuntimeApi::storage() .dkg_proposals() .chain_nonces(&src_chain_id); @@ -84,84 +81,53 @@ impl super::ProposalSigningBackend for DkgProposalSigningBackend { async fn handle_proposal( &self, proposal: &(impl ProposalTrait + Sync + Send + 'static), - metrics: Arc>, + _metrics: Arc>, ) -> webb_relayer_utils::Result<()> { + let my_chain_id_addr = + RuntimeApi::constants().dkg_proposals().chain_identifier(); + let my_chain_id = self.client.constants().at(&my_chain_id_addr)?; + let my_chain_id = match my_chain_id { + TypedChainId::Substrate(chain_id) => chain_id, + TypedChainId::PolkadotParachain(chain_id) => chain_id, + TypedChainId::KusamaParachain(chain_id) => chain_id, + _ => return Err(webb_relayer_utils::Error::Generic( + "dkg proposal signing backend only supports substrate chains", + )), + }; let tx_api = RuntimeApi::tx().dkg_proposals(); let resource_id = proposal.header().resource_id(); let nonce = proposal.header().nonce(); let src_chain_id = - webb_proposals_typed_chain_converter(self.typed_chain_id); - let nonce = Nonce::decode(&mut nonce.encode().as_slice())?; + webb_proposals_typed_chain_converter(self.src_chain_id); tracing::debug!( - nonce = %hex::encode(nonce.encode()), - resource_id = %hex::encode(resource_id.into_bytes()), - src_chain_id = ?src_chain_id, - proposal = %hex::encode(proposal.to_vec()), + nonce = nonce.0, + resource_id = hex::encode(resource_id.into_bytes()), + src_chain_id = ?self.src_chain_id, + proposal = hex::encode(proposal.to_vec()), "sending proposal to DKG runtime" ); - let xt = tx_api.acknowledge_proposal( + + let nonce = Nonce::decode(&mut nonce.encode().as_slice())?; + let unsigned_proposal = Proposal::Unsigned { + kind: ProposalKind::AnchorUpdate, + data: BoundedVec(proposal.to_vec()), + }; + let acknowledge_proposal_tx = tx_api.acknowledge_proposal( nonce, src_chain_id, ResourceId(resource_id.into_bytes()), - proposal.to_vec(), + unsigned_proposal, ); - // TODO: here we should have a substrate based tx queue in the background - // where just send the raw xt bytes and let it handle the work for us. - // but this here for now. - let signer = &self.pair; - let mut progress = self - .client - .tx() - .sign_and_submit_then_watch_default(&xt, signer) - .await?; - - while let Some(event) = progress.next().await { - let e = match event { - Ok(e) => e, - Err(err) => { - tracing::error!(error = %err, "failed to watch for tx events"); - return Err(err.into()); - } - }; - - match e { - TransactionStatus::Future => {} - TransactionStatus::Ready => { - tracing::trace!("tx ready"); - } - TransactionStatus::Broadcast(_) => {} - TransactionStatus::InBlock(_) => { - tracing::trace!("tx in block"); - } - TransactionStatus::Retracted(_) => { - tracing::warn!("tx retracted"); - } - TransactionStatus::FinalityTimeout(_) => { - tracing::warn!("tx timeout"); - } - TransactionStatus::Finalized(v) => { - let maybe_success = v.wait_for_success().await; - match maybe_success { - Ok(_events) => { - metrics.lock().await.proposals_signed.inc(); - tracing::debug!("tx finalized"); - } - Err(err) => { - tracing::error!(error = %err, "tx failed"); - return Err(err.into()); - } - } - } - TransactionStatus::Usurped(_) => {} - TransactionStatus::Dropped => { - tracing::warn!("tx dropped"); - } - TransactionStatus::Invalid => { - tracing::warn!("tx invalid"); - } - } - } + let data_hash = + utils::keccak256(acknowledge_proposal_tx.call_data().encode()); + let tx_key = SledQueueKey::from_substrate_with_custom_key( + my_chain_id, + make_acknowledge_proposal_key(data_hash), + ); + let tx = TypeErasedStaticTxPayload::try_from(acknowledge_proposal_tx)?; + // Enqueue transaction in protocol-substrate transaction queue + QueueStore::enqueue_item(&self.store, tx_key, tx)?; Ok(()) } } @@ -189,3 +155,12 @@ fn webb_proposals_typed_chain_converter( webb_proposals::TypedChainId::Ink(id) => TypedChainId::Ink(id), } } + +pub fn make_acknowledge_proposal_key(data_hash: [u8; 32]) -> [u8; 64] { + let mut result = [0u8; 64]; + let prefix = b"acknowledge_proposal_fixed_key__"; + debug_assert!(prefix.len() == 32); + result[0..32].copy_from_slice(prefix); + result[32..64].copy_from_slice(&data_hash); + result +} diff --git a/crates/proposal-signing-backends/src/lib.rs b/crates/proposal-signing-backends/src/lib.rs index e70a54da7..413062af1 100644 --- a/crates/proposal-signing-backends/src/lib.rs +++ b/crates/proposal-signing-backends/src/lib.rs @@ -21,6 +21,8 @@ //! There are two types of proposal signing backends: //! - `MockedProposalSigningBackend`: This is a mocked proposal signing backend that is used for testing purposes. //! - `DKGProposalSigningBackend`: This is the actual proposal signing backend that is used in production. +//! +//! This crate also contains the proposal queue that is used to queue the proposals that are generated by the relayer. use std::sync::Arc; use tokio::sync::Mutex; @@ -36,6 +38,9 @@ pub mod dkg; #[doc(hidden)] pub mod mocked; +/// A module to handle the queue of proposals +pub mod queue; + /// A module that Implements the DKG Proposal Signing Backend. pub use dkg::*; /// A module that Implements the Mocked Proposal Signing Backend. @@ -45,9 +50,6 @@ use webb_relayer_utils::metric; /// A Proposal Signing Backend is responsible for signing proposal `P` where `P` is anything really depending on the /// requirement of the user of this backend. /// -/// For example, an Anchor Event Watcher that watches for `Deposit` events might need to sign an `AnchorUpdateProposal` and to do so, it will -/// require a `ProposalSigningBackend` to do so. -/// /// As of now, we have two implementations of this trait: /// /// - `DkgSigningBackend`: This is using the `DKG` protocol to sign the proposal. diff --git a/crates/proposal-signing-backends/src/mocked.rs b/crates/proposal-signing-backends/src/mocked.rs index ed9a8c511..e75251b28 100644 --- a/crates/proposal-signing-backends/src/mocked.rs +++ b/crates/proposal-signing-backends/src/mocked.rs @@ -1,4 +1,3 @@ -use ethereum_types::H256; use std::collections::HashSet; use std::sync::Arc; use tokio::sync::Mutex; @@ -36,7 +35,7 @@ where &self, chain_id: TypedChainId, ) -> webb_relayer_utils::Result { - let key = SecretKey::from_be_bytes(self.private_key.as_bytes())?; + let key = SecretKey::from_bytes(self.private_key.as_bytes().into())?; let signer = LocalWallet::from(key) .with_chain_id(chain_id.underlying_chain_id()); Ok(signer) @@ -69,7 +68,7 @@ where let signer = self.signer(dest_chain_id)?; let proposal_bytes = proposal.to_vec(); let hash = keccak256(&proposal_bytes); - let signature = signer.sign_hash(H256::from(hash)); + let signature = signer.sign_hash(TxHash(hash))?; let bridge_key = BridgeKey::new(dest_chain_id); tracing::debug!( %bridge_key, @@ -92,7 +91,7 @@ where self.store.enqueue_item( SledQueueKey::from_bridge_key(bridge_key), BridgeCommand::ExecuteProposalWithSignature { - data: proposal_bytes.to_vec(), + data: proposal_bytes.clone(), signature: signature_bytes, }, )?; diff --git a/crates/proposal-signing-backends/src/proposal_handler.rs b/crates/proposal-signing-backends/src/proposal_handler.rs index 8e0a28ab7..5f7470c07 100644 --- a/crates/proposal-signing-backends/src/proposal_handler.rs +++ b/crates/proposal-signing-backends/src/proposal_handler.rs @@ -20,13 +20,14 @@ use webb::evm::ethers::prelude::EthCall; use webb_proposals::ProposalTrait; use webb_relayer_utils::metric; -pub async fn handle_proposal

( +#[tracing::instrument(skip_all)] +pub async fn handle_proposal( proposal: &(impl ProposalTrait + Sync + Send + 'static), - proposal_signing_backend: &P, + proposal_signing_backend: &PB, metrics: Arc>, ) -> webb_relayer_utils::Result<()> where - P: ProposalSigningBackend, + PB: ProposalSigningBackend, { let can_sign_proposal = proposal_signing_backend .can_handle_proposal(proposal) @@ -38,13 +39,23 @@ where } else { tracing::warn!( proposal = ?hex::encode(proposal.to_vec()), - "Anchor update proposal is not supported by the signing backend" + "the proposal is not supported by the signing backend" ); } Ok(()) } -// create anchor update proposal for Evm target system +/// create anchor update proposal for Evm target system +#[tracing::instrument( + skip_all, + fields( + proposal_type = "AnchorUpdateProposal", + from = ?src_resource_id.typed_chain_id(), + to = ?target_resource_id.typed_chain_id(), + nonce = leaf_index, + merkle_root = hex::encode(merkle_root), + ) +)] pub fn evm_anchor_update_proposal( merkle_root: [u8; 32], leaf_index: u32, @@ -62,6 +73,7 @@ pub fn evm_anchor_update_proposal( function_signature, nonce.into(), ); + tracing::debug!("created anchor update proposal"); webb_proposals::evm::AnchorUpdateProposal::new( header, merkle_root, @@ -70,6 +82,16 @@ pub fn evm_anchor_update_proposal( } // create anchor update proposal for substrate system +#[tracing::instrument( + skip_all, + fields( + proposal_type = "AnchorUpdateProposal", + from = ?src_resource_id.typed_chain_id(), + to = ?target_resource_id.typed_chain_id(), + nonce = leaf_index, + merkle_root = hex::encode(merkle_root), + ) +)] pub fn substrate_anchor_update_proposal( merkle_root: [u8; 32], leaf_index: u32, diff --git a/crates/proposal-signing-backends/src/queue/mem.rs b/crates/proposal-signing-backends/src/queue/mem.rs new file mode 100644 index 000000000..23e052203 --- /dev/null +++ b/crates/proposal-signing-backends/src/queue/mem.rs @@ -0,0 +1,119 @@ +use parking_lot::RwLock; +use std::collections::VecDeque; +use std::sync::Arc; + +use super::{policy::*, ProposalHash, QueuedAnchorUpdateProposal}; + +/// In memory implementation of the proposals queue. +#[derive(Clone, Debug, Default)] +pub struct InMemoryProposalsQueue { + proposals: Arc>>, +} + +impl InMemoryProposalsQueue { + /// Creates a new `InMemoryProposalsQueue` that will store the proposals + /// in memory. + pub fn new() -> Self { + Self { + proposals: Arc::new(RwLock::new(Default::default())), + } + } +} + +impl super::ProposalsQueue for InMemoryProposalsQueue { + type Proposal = QueuedAnchorUpdateProposal; + + #[tracing::instrument( + skip_all, + fields( + queue = "in_memory", + proposal = hex::encode(proposal.full_hash()), + ) + )] + fn enqueue( + &self, + proposal: Self::Proposal, + policy: Policy, + ) -> webb_relayer_utils::Result<()> { + let accepted = policy.check(&proposal, self); + tracing::trace!(accepted = ?accepted, "proposal check result"); + accepted?; + self.proposals.write().push_back(proposal); + Ok(()) + } + + #[tracing::instrument(skip_all, fields(queue = "in_memory"))] + fn dequeue( + &self, + policy: Policy, + ) -> webb_relayer_utils::Result> { + if self.proposals.read().is_empty() { + tracing::trace!("no proposals to dequeue"); + return Ok(None); + } + let proposal = match self.proposals.write().pop_front() { + Some(proposal) => proposal, + None => return Ok(None), + }; + match policy.check(&proposal, self) { + Ok(_) => { + tracing::trace!( + proposal = hex::encode(proposal.full_hash()), + "proposal passed policy check before dequeue", + ); + Ok(Some(proposal)) + } + Err(e) => { + tracing::trace!( + reason = %e, + "proposal failed policy check before dequeue", + ); + // push back the proposal if it failed the policy check + // so that it can be dequeued again later + self.proposals.write().push_back(proposal); + // the caller should try again later + Ok(None) + } + } + } + + fn len(&self) -> webb_relayer_utils::Result { + let len = self.proposals.read().len(); + Ok(len) + } + + fn is_empty(&self) -> webb_relayer_utils::Result { + Ok(self.proposals.read().is_empty()) + } + + fn clear(&self) -> webb_relayer_utils::Result<()> { + self.proposals.write().clear(); + Ok(()) + } + + fn retain(&self, f: F) -> webb_relayer_utils::Result<()> + where + F: FnMut(&Self::Proposal) -> bool, + { + self.proposals.write().retain(f); + Ok(()) + } + + fn modify_in_place(&self, f: F) -> webb_relayer_utils::Result<()> + where + F: FnMut(&mut Self::Proposal) -> webb_relayer_utils::Result<()>, + { + self.proposals.write().iter_mut().try_for_each(f) + } + + fn find( + &self, + mut f: F, + ) -> webb_relayer_utils::Result> + where + F: FnMut(&Self::Proposal) -> bool, + { + let v = self.proposals.read().iter().find(|v| f(v)).cloned(); + Ok(v) + } +} diff --git a/crates/proposal-signing-backends/src/queue/mod.rs b/crates/proposal-signing-backends/src/queue/mod.rs new file mode 100644 index 000000000..a05433135 --- /dev/null +++ b/crates/proposal-signing-backends/src/queue/mod.rs @@ -0,0 +1,632 @@ +use std::sync::{atomic, Arc}; + +use tokio::sync::Mutex; +use webb::evm::ethers; +use webb_proposals::ProposalTrait; +use webb_relayer_utils::metric; + +/// A module for in-memory Proposals Queue. +pub mod mem; +/// A module for Proposals Polices. +pub mod policy; + +/// A Proposal Queue is a simple Queue that holds the proposals that are going to be +/// signed or already signed and need to be sent to the target system to be executed. +pub trait ProposalsQueue { + /// The proposal type associated with the Proposal Queue. + type Proposal: ProposalTrait + ProposalMetadata + Send + Sync + 'static; + + /// Enqueues a proposal into the queue with the specified policy. + /// + /// + /// The [`policy::ProposalsQueuePolicy`] trait is implemented for tuples of up to 5 policies. + /// These policies are chained together and applied sequentially. + /// If any of the policies in the chain rejects the proposal, the enqueue operation fails and returns an error. + /// The proposal is added to the queue, and the provided policy is applied to determine if the proposal should be accepted. + /// If the policy chain rejects the proposal, the enqueue operation fails and returns an error. + /// + /// # Arguments + /// + /// * `proposal` - A reference to the proposal to be enqueued. + /// * `policy` - The policy to be applied to the proposal. The policy determines if the proposal should be accepted. + /// + /// # Returns + /// + /// Returns a `Result` indicating whether the enqueue operation was successful or not. + /// If the enqueue operation fails, an error is returned. Possible errors include: + /// - `EnqueueQueueError` if there is an issue during the enqueue operation, such as a policy rejection. + fn enqueue( + &self, + proposal: Self::Proposal, + policy: Policy, + ) -> webb_relayer_utils::Result<()>; + + /// Dequeues a proposal from the queue with the specified policy. + /// + /// The dequeue operation retrieves the next proposal from the queue that satisfies the provided policy. + /// The policy is applied sequentially to the proposals in the queue until a matching proposal is found. + /// The [`policy::ProposalsQueuePolicy`] trait is implemented for tuples of up to 5 policies. + /// These policies are chained together and applied sequentially. + /// If any of the policies in the chain rejects the proposal, the dequeue operation fails and returns an error. + /// + /// # Arguments + /// + /// * `policy` - The policy to be applied in the dequeue operation. The policy determines if a proposal should be dequeued. + /// + /// # Returns + /// + /// Returns a `Result` containing an optional proposal. + /// - If a proposal satisfying the policy is found, it is returned as `Some(proposal)`. + /// - If no matching proposal is found, `None` is returned. + /// If the dequeue operation fails, an error is returned. Possible errors include: + /// - `DequeueQueueError` if there is an issue during the dequeue operation. + /// + fn dequeue( + &self, + policy: Policy, + ) -> webb_relayer_utils::Result>; + + /// Returns the length of the proposal queue. + /// + /// The length of the queue is the total number of proposals currently stored in it. + /// + /// # Returns + /// + /// Returns a `Result` containing the length of the queue. + fn len(&self) -> webb_relayer_utils::Result; + /// Returns `true` if the proposal queue is empty. + /// + /// # Returns + /// Returns a `Result` containing a boolean value. + fn is_empty(&self) -> webb_relayer_utils::Result { + Ok(self.len()? == 0) + } + /// Clears the proposal queue, removing all proposals. + /// + /// This operation removes all proposals from the queue, resulting in an empty queue. + /// + /// # Returns + /// + /// Returns a `Result` indicating whether the clear operation was successful or not. + /// If the clear operation fails, an error is returned. Possible errors include: + /// - `ClearQueueError` if there is an issue during the clear operation. + /// + fn clear(&self) -> webb_relayer_utils::Result<()>; + + /// Retains only the proposals in the queue that satisfy the given predicate. + /// + /// This method removes all proposals from the queue that do not satisfy the provided predicate function. + /// The predicate function takes a reference to a proposal, and returns `true` if the proposal should be retained, + /// or `false` if it should be removed from the queue. + /// + /// # Arguments + /// + /// * `f` - A mutable closure or function that determines whether a proposal should be retained or removed. + /// The closure takes a reference to a proposal, and returns a boolean value. + /// - If the closure returns `true`, the proposal is retained in the queue. + /// - If the closure returns `false`, the proposal is removed from the queue. + /// + /// # Returns + /// + /// Returns a `Result` indicating whether the retain operation was successful or not. + /// If the retain operation fails, an error is returned. Possible errors include: + /// - `RetainQueueError` if there is an issue during the retain operation. + /// + fn retain(&self, f: F) -> webb_relayer_utils::Result<()> + where + F: FnMut(&Self::Proposal) -> bool; + + /// Modifies a proposal in place using the provided closure. + /// + /// This method allows modifying a proposal in the queue using a mutable closure. + /// The closure takes a mutable reference to a proposal and performs the required modifications. + /// + /// # Arguments + /// + /// * `f` - A mutable closure or function that modifies the proposal in place. + /// The closure takes a mutable reference to the proposal and returns a `Result`. + /// - If the closure returns `Ok(())`, the modification is successful. + /// - If the closure returns `Err(_)`, the modification fails and an error is returned. + /// + /// # Returns + /// + /// Returns a `Result` indicating whether the modification operation was successful or not. + /// If the modification operation fails, an error is returned. Possible errors include: + /// - `ModifyQueueError` if there is an issue during the modification operation. + /// + fn modify_in_place(&self, f: F) -> webb_relayer_utils::Result<()> + where + F: FnMut(&mut Self::Proposal) -> webb_relayer_utils::Result<()>; + + /// Finds and returns a proposal that satisfies the given predicate. + /// The predicate function takes a reference to a proposal, and returns `true` if the proposal is a match, + /// or `false` if it is not a match. + /// The first proposal that satisfies the predicate is returned. + /// If no matching proposal is found, `None` is returned. + fn find( + &self, + f: F, + ) -> webb_relayer_utils::Result> + where + F: FnMut(&Self::Proposal) -> bool; +} + +/// Associated metadata for a queued proposal. +#[derive(Debug, Clone)] +pub struct QueuedProposalMetadata { + /// The time at which the proposal was enqueued. + /// the value is the number of secs since the UNIX epoch. + queued_at: Arc, + /// The time at which the proposal should be dequeued. + /// The value is the number of secs since the UNIX epoch. + /// + /// Intially, this value is `Zero` and is set to the current time when the proposal is enqueued. + should_be_dequeued_at: Arc, +} + +impl QueuedProposalMetadata { + /// Get the time at which the proposal should be dequeued. + /// The value is the number of secs since the UNIX epoch. + pub fn should_be_dequeued_at(&self) -> Option { + let should_be_dequeued_at = + self.should_be_dequeued_at.load(atomic::Ordering::SeqCst); + if should_be_dequeued_at == 0 { + None + } else { + Some(should_be_dequeued_at) + } + } + + /// Get the time at which the proposal was enqueued. + /// The value is the number of secs since the UNIX epoch. + pub fn queued_at(&self) -> u64 { + self.queued_at.load(atomic::Ordering::SeqCst) + } + + /// Set the time at which the proposal should be dequeued. + /// The value is the number of secs since the UNIX epoch. + pub fn set_should_be_dequeued_at(&self, should_be_dequeued_at: u64) { + self.should_be_dequeued_at + .store(should_be_dequeued_at, atomic::Ordering::SeqCst); + } +} + +impl Default for QueuedProposalMetadata { + fn default() -> Self { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + Self { + queued_at: Arc::new(atomic::AtomicU64::new(now)), + should_be_dequeued_at: Arc::new(atomic::AtomicU64::default()), + } + } +} + +/// A Small Wrapper around a proposal that adds some metadata. +#[derive(Clone)] +pub struct QueuedAnchorUpdateProposal { + inner: Arc, + metadata: QueuedProposalMetadata, +} + +impl std::fmt::Debug for QueuedAnchorUpdateProposal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("QueuedProposal") + .field("inner", &hex::encode(self.inner.to_vec())) + .field("metadata", &self.metadata) + .finish() + } +} + +impl QueuedAnchorUpdateProposal { + /// Creates a new `QueuedAnchorUpdateProposal` from a proposal. + /// This abstracts away the metadata creation. + pub fn new

(inner: P) -> Self + where + P: ProposalTrait + Send + Sync + 'static, + { + Self { + inner: Arc::new(inner), + metadata: Default::default(), + } + } +} + +/// A small trait to add more functionality to the [`ProposalTrait`]. +/// This trait is used to calculate the hash of the proposal. +pub trait ProposalHash { + /// Returns the hash of the full proposal. + /// The full proposal is the proposal header concatenated with the proposal body. + fn full_hash(&self) -> [u8; 32]; +} + +// *** Implementation of the `ProposalHash` trait for all types that implement `ProposalTrait` *** +impl

ProposalHash for P +where + P: ProposalTrait, +{ + fn full_hash(&self) -> [u8; 32] { + ethers::utils::keccak256(self.to_vec()) + } +} + +impl ProposalTrait for QueuedAnchorUpdateProposal { + fn header(&self) -> webb_proposals::ProposalHeader { + self.inner.header() + } + + fn to_vec(&self) -> Vec { + self.inner.to_vec() + } +} + +/// The `ProposalMetadata` trait is implemented by proposals that carry metadata. +/// Metadata is additional information that accompanies a proposal, like its nonce, creation time, etc. +/// +/// Implementing `ProposalMetadata` requires implementing two methods: +/// - `metadata`: Returns a reference to the metadata of the proposal. +/// +/// The metadata is stored as a `QueuedProposalMetadata` struct. +/// This metadata is intended to be used by the queueing policy to make decisions about the proposal, +/// for example, to apply a time delay or prioritize the proposal based on its nonce. +pub trait ProposalMetadata { + /// Returns a reference to the metadata of the proposal. + fn metadata(&self) -> &QueuedProposalMetadata; +} + +impl ProposalMetadata for QueuedAnchorUpdateProposal { + fn metadata(&self) -> &QueuedProposalMetadata { + &self.metadata + } +} + +/// Runs the queue in a loop that it will try +/// to dequeue proposals and sends them to the signing backend. +/// +/// This function will loop forever and should be run in a separate task. +/// it will never end unless the task is cancelled. +#[tracing::instrument(skip_all)] +pub async fn run( + queue: Queue, + dequeue_policy: Policy, + proposal_signing_backend: PSB, + metrics: Arc>, +) where + Queue: ProposalsQueue, + Policy: policy::ProposalPolicy + Clone, + PSB: super::ProposalSigningBackend, +{ + loop { + let proposal = match queue.dequeue(dequeue_policy.clone()) { + Ok(Some(proposal)) => proposal, + Ok(None) => { + tracing::trace!("No proposal to dequeue"); + // Sleep for a bit to avoid busy looping + tokio::time::sleep(core::time::Duration::from_millis(1100)) + .await; + continue; + } + Err(e) => { + tracing::error!("Failed to dequeue proposal: {:?}", e); + tokio::task::yield_now().await; + continue; + } + }; + + let result = crate::proposal_handler::handle_proposal( + &proposal, + &proposal_signing_backend, + metrics.clone(), + ) + .await; + match result { + Ok(_) => { + tracing::trace!( + proposal = ?hex::encode(proposal.to_vec()), + "the proposal was successfully handled by the signing backend" + ); + } + Err(e) => { + tracing::error!( + error = ?e, + proposal = ?hex::encode(proposal.to_vec()), + "failed to handle the proposal", + ); + } + } + } +} + +#[cfg(test)] +pub(crate) mod test_utils { + use super::*; + + pub fn setup_tracing() -> tracing::subscriber::DefaultGuard { + // Setup tracing for tests + let env_filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive( + tracing_subscriber::filter::LevelFilter::DEBUG.into(), + ) + .from_env_lossy(); + let s = tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_test_writer() + .without_time() + .with_target(false) + .compact() + .finish(); + tracing::subscriber::set_default(s) + } + + pub fn mock_target_system( + address: ethers::types::Address, + ) -> webb_proposals::TargetSystem { + webb_proposals::TargetSystem::ContractAddress(address.to_fixed_bytes()) + } + + pub fn mock_typed_chain_id(chain_id: u32) -> webb_proposals::TypedChainId { + webb_proposals::TypedChainId::Evm(chain_id) + } + + pub fn mock_resourc_id( + target_system: webb_proposals::TargetSystem, + typed_chain_id: webb_proposals::TypedChainId, + ) -> webb_proposals::ResourceId { + webb_proposals::ResourceId::new(target_system, typed_chain_id) + } + + pub fn mock_proposal_header( + target_resource_id: webb_proposals::ResourceId, + nonce: u32, + ) -> webb_proposals::ProposalHeader { + let fnsig = [0x42u8; 4]; + webb_proposals::ProposalHeader::new( + target_resource_id, + fnsig.into(), + nonce.into(), + ) + } + + pub fn mock_evm_anchor_update_proposal( + header: webb_proposals::ProposalHeader, + src_resourc_id: webb_proposals::ResourceId, + ) -> QueuedAnchorUpdateProposal { + let root = ethers::types::H256::random(); + let proposal = webb_proposals::evm::AnchorUpdateProposal::new( + header, + root.to_fixed_bytes(), + src_resourc_id, + ); + QueuedAnchorUpdateProposal::new(proposal) + } + + pub fn mock_metrics() -> Arc> { + Arc::new(Mutex::new(metric::Metrics::new().unwrap())) + } + + #[derive(Clone, Debug, Default)] + pub struct DummySigningBackend { + pub handled_proposals_count: Arc, + } + + #[async_trait::async_trait] + impl crate::ProposalSigningBackend for DummySigningBackend { + async fn can_handle_proposal( + &self, + _proposal: &(impl ProposalTrait + Sync + Send + 'static), + ) -> webb_relayer_utils::Result { + Ok(true) + } + async fn handle_proposal( + &self, + proposal: &(impl ProposalTrait + Sync + Send + 'static), + _metrics: Arc>, + ) -> webb_relayer_utils::Result<()> { + tracing::debug!( + proposal = ?hex::encode(proposal.to_vec()), + "pretending to handle proposal", + ); + self.handled_proposals_count + .fetch_add(1, atomic::Ordering::SeqCst); + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use rand::Rng; + + use super::test_utils::*; + use super::*; + + #[tokio::test] + async fn simulation() { + let _guard = setup_tracing(); + let queue = mem::InMemoryProposalsQueue::new(); + let time_delay_policy = policy::TimeDelayPolicy::builder() + .initial_delay(1) + .min_delay(1) + .max_delay(5) + .build(); + let nonce_policy = policy::AlwaysHigherNoncePolicy; + let enqueue_policy = (nonce_policy, time_delay_policy.clone()); + let dequeue_policy = time_delay_policy.clone(); + let signing_backend = DummySigningBackend::default(); + let metrics = mock_metrics(); + + let handle = tokio::spawn(run( + queue.clone(), + dequeue_policy, + signing_backend.clone(), + metrics, + )); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + // fill the queue with proposals. + let n = 5; + for nonce in 1..=n { + let header = mock_proposal_header(r_id, nonce); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + queue.enqueue(proposal, enqueue_policy.clone()).unwrap(); + tokio::time::sleep(Duration::from_millis(10)).await; + } + tokio::time::sleep(time_delay_policy.max_delay()).await; + // all proposals should be handled by now. + // the queue should be empty. + assert!(queue.is_empty().unwrap(), "the queue should be empty"); + // we should only have handled one proposal. + // the rest should be removed from the queue. + assert_eq!( + signing_backend + .handled_proposals_count + .load(atomic::Ordering::SeqCst), + 1, + "we should only have handled one proposal", + ); + handle.abort(); + } + + /// This tests simulate the case that we are running a proposal queue + /// with nonce policy and time delay policy in the enqueue operation + /// and time delay policy in the dequeue operation. + #[ignore = "this test simulate a real usage hence the wait time is long"] + #[tokio::test] + async fn manual_simulation_case1() { + let _guard = setup_tracing(); + let queue = mem::InMemoryProposalsQueue::new(); + let time_delay_policy = policy::TimeDelayPolicy::builder() + .initial_delay(30) + .min_delay(60) + .max_delay(300) + .build(); + let nonce_policy = policy::AlwaysHigherNoncePolicy; + let enqueue_policy = (nonce_policy, time_delay_policy.clone()); + let dequeue_policy = time_delay_policy.clone(); + let signing_backend = DummySigningBackend::default(); + let metrics = mock_metrics(); + + let handle = tokio::spawn(run( + queue.clone(), + dequeue_policy, + signing_backend.clone(), + metrics, + )); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let r_id = mock_resourc_id(target_system, target_chain); + + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let src_r_id = mock_resourc_id(src_system, src_chain); + + // fill the queue with proposals. + let n = 5; + for nonce in 1..=n { + let header = mock_proposal_header(r_id, nonce); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + queue.enqueue(proposal, enqueue_policy.clone()).unwrap(); + // Simulate that a time passed between each proposal. + // The time will be randomly choosed. + let delay = rand::thread_rng().gen_range(10_000..60_000); + tokio::time::sleep(Duration::from_millis(delay)).await; + } + + tokio::time::sleep(time_delay_policy.delay()).await; + // all proposals should be handled by now. + // the queue should be empty. + assert!(queue.is_empty().unwrap(), "the queue should be empty"); + // we should only have handled one proposal. + // the rest should be removed from the queue. + assert_eq!( + signing_backend + .handled_proposals_count + .load(atomic::Ordering::SeqCst), + 1, + "we should only have handled one proposal", + ); + handle.abort(); + } + + /// This tests simulate the case that we are running a proposal queue + /// with a time delay policy in the enqueue operation + /// and time delay policy in the dequeue operation. + /// + /// This test case is more specific to test if someone can DDOS the queue + /// by sending a lot of proposals which would increase the delay of the + /// queue and hence the queue will never process any proposal. + /// + /// We simulate this by sending a lot of proposals with a small delay in between + /// and we expect that the queue will process at least one proposal in a reasonable + /// time. + #[ignore = "this test simulate a real usage hence the wait time is long"] + #[tokio::test] + async fn manual_simulation_case2() { + let _guard = setup_tracing(); + let queue = mem::InMemoryProposalsQueue::new(); + let time_delay_policy = policy::TimeDelayPolicy::builder() + .initial_delay(30) + .min_delay(60) + .max_delay(90) + .build(); + let enqueue_policy = time_delay_policy.clone(); + let dequeue_policy = time_delay_policy.clone(); + let signing_backend = DummySigningBackend::default(); + let metrics = mock_metrics(); + + let handle = tokio::spawn(run( + queue.clone(), + dequeue_policy, + signing_backend.clone(), + metrics, + )); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let r_id = mock_resourc_id(target_system, target_chain); + + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let src_r_id = mock_resourc_id(src_system, src_chain); + + let queue2 = queue.clone(); + let handle2 = tokio::spawn(async move { + let mut nonce = 1; + // fill the queue with proposals. + loop { + let header = mock_proposal_header(r_id, nonce); + let proposal = + mock_evm_anchor_update_proposal(header, src_r_id); + queue2.enqueue(proposal, enqueue_policy.clone()).unwrap(); + // Simulate that a time passed between each proposal. + // The time will be randomly choosed. + let delay = rand::thread_rng().gen_range(5_000..12_000); + tokio::time::sleep(Duration::from_millis(delay)).await; + nonce += 1; + } + }); + + // wait for the queue to process at least one proposal. + tokio::time::sleep(time_delay_policy.max_delay().mul_f64(1.5)).await; + // we should only have handled at least one proposal. + assert!( + signing_backend + .handled_proposals_count + .load(atomic::Ordering::SeqCst) + .ge(&1), + "we should have at least handled one proposal", + ); + handle.abort(); + handle2.abort(); + } +} diff --git a/crates/proposal-signing-backends/src/queue/policy/mod.rs b/crates/proposal-signing-backends/src/queue/policy/mod.rs new file mode 100644 index 000000000..316faf695 --- /dev/null +++ b/crates/proposal-signing-backends/src/queue/policy/mod.rs @@ -0,0 +1,54 @@ +mod nonce; +mod time; + +pub use nonce::*; +pub use time::*; + +/// The `ProposalPolicy` trait defines the behavior of a policy that is applied to proposal in a queue. +/// A policy determines whether a proposal should be accepted or rejected based on specific criteria. +pub trait ProposalPolicy { + /// Checks whether a proposal should be accepted or rejected based on the policy's criteria. + /// + /// # Arguments + /// + /// * `proposal`: A reference to the proposal to be checked. + /// * `queue`: A reference to the proposals queue that contains the proposal. + /// + /// # Errors + /// + /// This method may return an error if the proposal fails to meet the policy's criteria. + /// + fn check( + &self, + proposal: &Q::Proposal, + queue: &Q, + ) -> webb_relayer_utils::Result<()>; +} + +#[impl_trait_for_tuples::impl_for_tuples(5)] +impl ProposalPolicy for TupleIdentifier { + fn check( + &self, + proposal: &Q::Proposal, + queue: &Q, + ) -> webb_relayer_utils::Result<()> { + for_tuples!( #( TupleIdentifier.check(proposal, queue)?; )* ); + Ok(()) + } +} + +impl

ProposalPolicy for Option

+where + P: ProposalPolicy, +{ + fn check( + &self, + proposal: &Q::Proposal, + queue: &Q, + ) -> webb_relayer_utils::Result<()> { + match self { + Some(policy) => policy.check(proposal, queue), + None => Ok(()), + } + } +} diff --git a/crates/proposal-signing-backends/src/queue/policy/nonce.rs b/crates/proposal-signing-backends/src/queue/policy/nonce.rs new file mode 100644 index 000000000..fbc187f47 --- /dev/null +++ b/crates/proposal-signing-backends/src/queue/policy/nonce.rs @@ -0,0 +1,273 @@ +use webb_proposals::ProposalTrait; + +use crate::queue::ProposalsQueue; +/// A policy that always checks if the nonce of a proposal is higher than existing proposals, +/// while also removing any proposals with a lower nonce. +/// +/// ## Expected Behavior +/// - Finds any proposal that matches the same (function signature, resource id) as the one being checked +/// and has a higher nonce. +/// - If such a proposal exists, rejects the proposal. +/// - If no such proposal exists, accepts the proposal, then +/// - Removes any proposals that match the same (function signature, resource id) as the one being checked +/// and have a lower nonce. +/// +/// This way, we ensure that the queue only contains proposals with the highest nonce for a given +/// (function signature, resource id). +/// +/// ## Note +/// This policy is stateless, hence it is cheap to copy. +#[derive(Debug, Copy, Clone, Default)] +pub struct AlwaysHigherNoncePolicy; + +impl super::ProposalPolicy for AlwaysHigherNoncePolicy { + #[tracing::instrument(skip_all)] + fn check( + &self, + proposal: &Q::Proposal, + queue: &Q, + ) -> webb_relayer_utils::Result<()> { + let header = proposal.header(); + let nonce = header.nonce(); + let r_id = header.resource_id(); + let funsig = header.function_signature(); + + let accept = queue + .find(|p| { + let p_header = p.header(); + p_header.function_signature().eq(&funsig) + && p_header.resource_id().eq(&r_id) + && p_header.nonce() > nonce + })? + .is_none(); + + tracing::trace!( + accept, + nonce = nonce.to_u32(), + "should accept proposal" + ); + + if !accept { + return Err(webb_relayer_utils::Error::Generic("Nonce is too low")); + } + + queue.retain(|p| { + let p_header = p.header(); + let same_funsig = p_header.function_signature().eq(&funsig); + if !same_funsig { + return true; + } + let same_r_id = p_header.resource_id().eq(&r_id); + if !same_r_id { + return true; + } + // now we know that the proposal is for the same resource and function + // signature. We can now check if the nonce is higher. + let should_keep = p_header.nonce() > nonce; + tracing::trace!( + should_keep, + nonce = nonce.to_u32(), + p_nonce = p_header.nonce().to_u32(), + "should keep proposal" + ); + should_keep + }) + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use webb::evm::ethers; + + use super::*; + use crate::queue::{mem::InMemoryProposalsQueue, test_utils::*}; + + type TestQueue = InMemoryProposalsQueue; + + #[test] + fn should_accept_proposals_with_higher_nonce() { + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + + let header = mock_proposal_header(r_id, 1); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal, policy).is_ok(), + "should accept proposal" + ); + } + + #[test] + fn should_reject_proposals_with_lower_nonce() { + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + + let header = mock_proposal_header(r_id, 1); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + queue.enqueue(proposal, policy).unwrap(); + + let header = mock_proposal_header(r_id, 0); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal, policy).is_err(), + "should reject proposal" + ); + } + + #[test] + fn should_remove_any_proposal_with_lower_nonce() { + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + // fill the queue with proposals. + for nonce in 1..=10 { + let header = mock_proposal_header(r_id, nonce); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + queue.enqueue(proposal, policy).unwrap(); + } + // we clean up the queue by adding a proposal with a higher nonce. + let len = queue.len().unwrap(); + assert_eq!(len, 1, "should have only one proposal"); + let prop = queue.dequeue(()).unwrap().unwrap(); + assert_eq!( + prop.header().nonce().to_u32(), + 10, + "should have the highest nonce" + ); + } + + #[test] + fn should_accept_proposals_with_equal_nonce() { + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + let header = mock_proposal_header(r_id, 1); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal.clone(), policy).is_ok(), + "should accept proposal with equal nonce" + ); + let prop = queue.dequeue(()).unwrap().unwrap(); + assert_eq!( + prop.header().nonce().to_u32(), + 1, + "should have the same nonce" + ); + assert_eq!( + proposal.header().nonce().to_u32(), + prop.header().nonce().to_u32(), + "nonce should be equal" + ); + } + + #[test] + fn should_handle_queue_with_higher_nonce_proposals() { + let _guard = setup_tracing(); + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + let n = 10; + // Fill the queue with proposals. + for nonce in 6..=n { + let header = mock_proposal_header(r_id, nonce); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal.clone(), policy).is_ok(), + "should accept proposal with higher nonce" + ); + } + let len = queue.len().unwrap(); + assert_eq!( + len, 1, + "len should be equal to one for queue with higher nonce proposals" + ); + let prop = queue.dequeue(()).unwrap().unwrap(); + let nonce = prop.header().nonce().to_u32(); + assert!(nonce == n, "nonce should equal to n"); + } + + #[test] + fn should_handle_concurrent_operations() { + use std::thread; + + let _guard = setup_tracing(); + let policy = AlwaysHigherNoncePolicy; + let queue = TestQueue::new(); + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + // Concurrently enqueue and dequeue proposals + let enqueue_handle = { + let queue = queue.clone(); + // spawn a new thread, propagating the dispatcher context + let dispatch = tracing::dispatcher::Dispatch::default(); + thread::spawn(move || { + let _guard = tracing::dispatcher::set_default(&dispatch); + for nonce in 1..=5 { + let header = mock_proposal_header(r_id, nonce); + let proposal = + mock_evm_anchor_update_proposal(header, src_r_id); + queue.enqueue(proposal, policy).unwrap(); + } + }) + }; + + let dequeue_handle = { + let queue = queue.clone(); + // spawn a new thread, propagating the dispatcher context + let dispatch = tracing::dispatcher::Dispatch::default(); + thread::spawn(move || { + let _guard = tracing::dispatcher::set_default(&dispatch); + for _ in 1..=5 { + // To make sure that the other thread has a chance to enqueue a proposal + std::thread::sleep(Duration::from_millis(50)); + let _ = queue.dequeue(()).unwrap(); + } + }) + }; + + enqueue_handle.join().unwrap(); + dequeue_handle.join().unwrap(); + + let len = queue.len().unwrap(); + assert_eq!( + len, 0, + "len should be 0 after concurrent enqueue and dequeue" + ); + } +} diff --git a/crates/proposal-signing-backends/src/queue/policy/time.rs b/crates/proposal-signing-backends/src/queue/policy/time.rs new file mode 100644 index 000000000..854e21515 --- /dev/null +++ b/crates/proposal-signing-backends/src/queue/policy/time.rs @@ -0,0 +1,400 @@ +use parking_lot::Mutex; +use std::{ + ops::Add, + sync::{atomic, Arc}, + time::Duration, +}; + +use crate::queue::{ProposalHash, ProposalMetadata, ProposalsQueue}; + +/// Initial delay in seconds +pub const INITIAL_DELAY: u64 = 30; +/// Minimum delay in seconds +pub const MIN_DELAY: u64 = 10; +/// Maximum delay in seconds +pub const MAX_DELAY: u64 = 300; +/// Sliding window size +pub const WINDOW_SIZE: usize = 5; + +/// A policy for introducing time delays based on a sliding window average. +/// +/// The `TimeDelayPolicy` adjusts the current delay based on the average delay of the recent window of delays. +/// It provides control over the initial delay, minimum delay, maximum delay, and sliding window size. +/// +/// # Example +/// +/// ```rust +/// # use webb_proposal_signing_backends::queue::policy::TimeDelayPolicy; +/// use std::time::Duration; +/// +/// // Create a TimeDelayPolicy with custom configuration +/// let policy = TimeDelayPolicy::builder() +/// .initial_delay(10) +/// .min_delay(5) +/// .max_delay(30) +/// .window_size(5) +/// .build(); +/// +/// // Get the current delay +/// let current_delay = policy.delay(); +/// assert_eq!(current_delay, Duration::from_secs(10)); +///``` +/// +#[derive(Debug, Clone, typed_builder::TypedBuilder)] +pub struct TimeDelayPolicy { + /// Initial delay in seconds + #[builder(default = INITIAL_DELAY)] + initial_delay: u64, + /// Minimum delay in seconds + #[builder(default = MIN_DELAY)] + min_delay: u64, + /// Maximum delay in seconds + #[builder(default = MAX_DELAY)] + max_delay: u64, + /// Sliding window size + #[builder(default = WINDOW_SIZE)] + window_size: usize, + /// Sliding window of delays + /// The sliding window is used to calculate the average delay + /// The average delay is used to adjust the current delay + /// Keeps the [`WINDOW_SIZE`] most recent delays + #[builder(setter(skip), default = Arc::new(Mutex::new(vec![initial_delay; window_size])))] + delays: Arc>>, + /// Current delay in seconds + #[builder(setter(skip), default = Arc::new(atomic::AtomicU64::new(initial_delay)))] + current_delay: Arc, +} + +impl TimeDelayPolicy { + /// Updates the current delay based on the average delay + /// returns true if the delay was updated + /// otherwise returns false, indecating that the delay was not updated + #[tracing::instrument(skip(self))] + pub fn update_delay( + &self, + num_proposals: usize, + ) -> webb_relayer_utils::Result { + // Add the current delay to the sliding window + let mut lock = self.delays.lock(); + + // Add the current delay to the sliding window + lock.push(self.current_delay.load(atomic::Ordering::Relaxed)); + // Remove the oldest delay from the sliding window + lock.remove(0); + + // Calculate the average delay from the sliding window + let sum: u64 = lock.iter().sum(); + let avg_delay = sum / lock.len() as u64; + // Adjust the current delay based on the average and the number of proposals + let adjusted_delay = if num_proposals > 0 { + let scaling_factor = num_proposals as u64; + let adjusted = avg_delay * scaling_factor; + adjusted.min(self.max_delay).max(self.min_delay) + } else { + self.initial_delay + }; + tracing::trace!( + adjusted_delay, + avg_delay, + self.initial_delay, + self.min_delay, + self.max_delay, + current_window_size = lock.len(), + self.window_size, + "Calculated Adjusted delay" + ); + + let old_delay = self + .current_delay + .swap(adjusted_delay, atomic::Ordering::Relaxed); + Ok(adjusted_delay != old_delay) + } + + /// Returns the current delay as a [`Duration`] + pub fn delay(&self) -> Duration { + Duration::from_secs(self.current_delay.load(atomic::Ordering::Relaxed)) + } + + /// Returns the maximum delay as a [`Duration`] + pub fn max_delay(&self) -> Duration { + Duration::from_secs(self.max_delay) + } + + /// Returns the minimum delay as a [`Duration`] + pub fn min_delay(&self) -> Duration { + Duration::from_secs(self.min_delay) + } +} + +impl super::ProposalPolicy for TimeDelayPolicy { + #[tracing::instrument( + skip_all + fields( + proposal_hash = hex::encode(proposal.full_hash()), + proposal_queued_at = proposal.metadata().queued_at(), + ) + )] + fn check( + &self, + proposal: &Q::Proposal, + queue: &Q, + ) -> webb_relayer_utils::Result<()> { + let size = queue.len()?; + // Size = len + 1 because the proposal is still in the queue + let delay_changed = self.update_delay(size + 1)?; + let delay = self.delay().as_secs(); + tracing::debug!(delay_changed, delay, queue_size = size); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + let metadata = proposal.metadata(); + // check if the proposal should be dequeued + let ret = match metadata.should_be_dequeued_at() { + Some(v) if v <= now => { + // this means we are trying to dequeue a proposal. + // and the proposal should be dequeued + tracing::trace!( + should_be_dequeued_at = v, + now, + diff_secs = now - v, + "Proposal should be dequeued. Dequeuing proposal", + ); + Ok(()) + } + Some(v) => { + tracing::trace!( + should_be_dequeued_at = v, + wait_secs = v - now, + "Proposal should not be dequeued", + ); + Err(webb_relayer_utils::Error::Generic( + "Proposal should not be dequeued", + )) + } + None => { + let queued_at = metadata.queued_at(); + let expected_to_be_dequeued_at = queued_at.add(delay); + // this means we are trying to queue a proposal. + // we set the should_be_dequeued_at value + metadata.set_should_be_dequeued_at(expected_to_be_dequeued_at); + tracing::trace!( + queued_at, + should_be_dequeued_at = expected_to_be_dequeued_at, + would_wait_secs = expected_to_be_dequeued_at - queued_at, + "Proposal should be queued", + ); + Ok(()) + } + }; + // if the delay changed, adjust all the proposals in the queue with the new delay + if delay_changed { + tracing::trace!( + "Delay changed. Adjusting all proposals in the queue" + ); + queue.modify_in_place(|p| { + let metadata = p.metadata(); + let queued_at = metadata.queued_at(); + let expected_to_be_dequeued_at = queued_at.add(delay); + let should_be_dequeued_at = metadata.should_be_dequeued_at(); + metadata.set_should_be_dequeued_at(expected_to_be_dequeued_at); + tracing::trace!( + queued_at, + old_should_be_dequeued_at = should_be_dequeued_at, + new_should_be_dequeued_at = expected_to_be_dequeued_at, + would_wait_secs = expected_to_be_dequeued_at - queued_at, + "Adjusted proposal in the queue", + ); + Ok(()) + })?; + } + ret + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + use webb::evm::ethers; + + use crate::queue::{mem::InMemoryProposalsQueue, test_utils::*}; + + type TestQueue = InMemoryProposalsQueue; + + #[test] + fn test_default_configuration() { + let policy = TimeDelayPolicy::builder().build(); + assert_eq!(policy.delay(), Duration::from_secs(INITIAL_DELAY)); + } + + #[test] + fn test_custom_configuration() { + let policy = TimeDelayPolicy::builder() + .initial_delay(10) + .min_delay(5) + .max_delay(30) + .window_size(5) + .build(); + assert_eq!(policy.delay(), Duration::from_secs(10)); + } + + #[test] + fn test_large_window_size() { + let policy = TimeDelayPolicy::builder().window_size(10).build(); + + for _ in 0..10 { + policy.update_delay(1).unwrap(); + } + assert_eq!(policy.delay(), Duration::from_secs(INITIAL_DELAY)); + } + + #[test] + fn test_zero_minimum_maximum_delay() { + let policy = + TimeDelayPolicy::builder().min_delay(0).max_delay(0).build(); + assert_eq!(policy.delay(), Duration::from_secs(INITIAL_DELAY)); + + let _ = policy.update_delay(2); + assert_eq!(policy.delay(), Duration::from_secs(0)); + } + + #[test] + fn test_minimum_maximum_delay() { + let policy = TimeDelayPolicy::builder() + .initial_delay(10) + .min_delay(10) + .max_delay(10) + .build(); + assert_eq!(policy.delay(), Duration::from_secs(10)); + + policy.update_delay(0).unwrap(); + assert_eq!(policy.delay(), Duration::from_secs(10)); + policy.update_delay(2).unwrap(); + assert_eq!(policy.delay(), Duration::from_secs(10)); + } + + #[test] + fn test_update_delay_with_zero_queue_size() { + let policy = TimeDelayPolicy::builder().build(); + let result = policy.update_delay(0); + assert!(result.is_ok()); + } + + #[test] + fn test_update_delay_with_zero_scaling_factor() { + let policy = TimeDelayPolicy::builder().build(); + let result = policy.update_delay(0); + assert!(result.is_ok()); + } + + #[test] + fn test_zero_initial_delay() { + let policy = TimeDelayPolicy::builder().initial_delay(0).build(); + assert_eq!(policy.delay(), Duration::from_secs(0)); + } + + #[test] + fn test_zero_maximum_delay() { + let policy = TimeDelayPolicy::builder().max_delay(0).build(); + assert_eq!(policy.delay(), Duration::from_secs(INITIAL_DELAY)); + + policy.update_delay(2).unwrap(); + assert_eq!(policy.delay(), Duration::from_secs(MIN_DELAY)); + } + + #[test] + fn test_zero_minimum_delay() { + let policy = TimeDelayPolicy::builder().min_delay(0).build(); + assert_eq!(policy.delay(), Duration::from_secs(INITIAL_DELAY)); + + policy.update_delay(2).unwrap(); + assert_eq!(policy.delay(), Duration::from_secs(2 * INITIAL_DELAY)); + } + + #[test] + fn should_dequeue_proposal_at_the_right_time() { + let _guard = setup_tracing(); + let policy = TimeDelayPolicy::builder() + .initial_delay(1) + .min_delay(0) + .max_delay(2) + .build(); + assert_eq!(policy.delay(), Duration::from_secs(1)); + + let queue = TestQueue::new(); + + let proposal = queue.dequeue(policy.clone()).unwrap(); + assert!(proposal.is_none(), "No Proposals to dequeue"); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + + let header = mock_proposal_header(r_id, 1); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal, policy.clone()).is_ok(), + "should accept proposal" + ); + // wait until the proposal is expected to be dequeued + std::thread::sleep(policy.delay()); + let proposal = queue.dequeue(policy).unwrap(); + assert!(proposal.is_some(), "should dequeue proposal"); + } + + #[test] + fn should_increase_delay_as_we_see_more_proposals() { + let _guard = setup_tracing(); + let policy = TimeDelayPolicy::builder() + .initial_delay(1) + .min_delay(1) + .max_delay(3) + .build(); + assert_eq!(policy.delay(), Duration::from_secs(1)); + + let queue = TestQueue::new(); + + let proposal = queue.dequeue(policy.clone()).unwrap(); + assert!(proposal.is_none(), "No Proposals to dequeue"); + + let target_system = mock_target_system(ethers::types::Address::zero()); + let target_chain = mock_typed_chain_id(1); + + let src_system = mock_target_system(ethers::types::Address::zero()); + let src_chain = mock_typed_chain_id(42); + + let r_id = mock_resourc_id(target_system, target_chain); + let src_r_id = mock_resourc_id(src_system, src_chain); + for i in 1..=10 { + let header = mock_proposal_header(r_id, i); + let proposal = mock_evm_anchor_update_proposal(header, src_r_id); + assert!( + queue.enqueue(proposal, policy.clone()).is_ok(), + "should accept proposal" + ); + } + // the delay should be larger than the initial delay + // but smaller than the max delay + assert!( + policy.delay() >= Duration::from_secs(policy.initial_delay) + && policy.delay() <= Duration::from_secs(policy.max_delay), + "delay should be larger than the initial delay but smaller than the max delay" + ); + // if we did try to dequeue, we should not get any proposals + // since the delay is not yet over. + let proposal = queue.dequeue(policy.clone()).unwrap(); + assert!(proposal.is_none(), "Cannot dequeue proposal yet"); + // wait for the delay to expire + std::thread::sleep(policy.delay()); + // we should be able to dequeue a proposal now + let proposal = queue.dequeue(policy).unwrap(); + assert!(proposal.is_some(), "should dequeue proposal"); + } +} diff --git a/crates/relayer-config/Cargo.toml b/crates/relayer-config/Cargo.toml index b4ee2e992..338a31bfb 100644 --- a/crates/relayer-config/Cargo.toml +++ b/crates/relayer-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-config" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -32,18 +32,14 @@ serde_path_to_error = { workspace = true } structopt = { version = "^0.3", features = ["paw"], optional = true } directories-next = { version = "^2.0", optional = true } -tracing-subscriber = { version = "0.3.16", features = ["parking_lot", "env-filter"], optional = true } +tracing-subscriber = { workspace = true, features = ["parking_lot", "env-filter"], optional = true } + +[dev-dependencies] +dotenv = { workspace = true } [features] default = ["evm-runtime", "substrate-runtime", "cli"] -cli = [ - "evm-runtime", - "substrate-runtime", - "anyhow", - "tracing-subscriber", - "structopt", - "directories-next" -] +cli = ["evm-runtime", "substrate-runtime", "anyhow", "tracing-subscriber", "structopt", "directories-next"] evm-runtime = ["webb/evm-runtime", "webb-proposals/evm"] substrate-runtime = ["webb/substrate-runtime", "webb-proposals/substrate"] integration-tests = ["tracing-subscriber/json"] diff --git a/crates/relayer-config/src/anchor.rs b/crates/relayer-config/src/anchor.rs index 14e56794f..72c9af459 100644 --- a/crates/relayer-config/src/anchor.rs +++ b/crates/relayer-config/src/anchor.rs @@ -8,7 +8,7 @@ use super::*; /// Linked anchor config for Evm based target system #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct RawResourceId { /// Raw resource Id pub resource_id: H256, diff --git a/crates/relayer-config/src/block_poller.rs b/crates/relayer-config/src/block_poller.rs index d4cd64715..4c9b2cc10 100644 --- a/crates/relayer-config/src/block_poller.rs +++ b/crates/relayer-config/src/block_poller.rs @@ -3,23 +3,22 @@ use webb_relayer_types::rpc_url::RpcUrl; /// Block poller configuration #[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct BlockPollerConfig { /// The starting block to listen at. #[serde(default)] pub start_block: Option, /// Polling interval in milliseconds - #[serde(rename(serialize = "pollingInterval"))] pub polling_interval: u64, /// The maximum blocks per step. /// /// default to 100 - #[serde(default = "max_blocks_per_step_default")] + #[serde(default = "defaults::max_blocks_per_step")] pub max_blocks_per_step: u64, /// The print progress interval. /// /// default to 7_000 - #[serde(default = "print_progress_interval_default")] + #[serde(default = "defaults::print_progress_interval")] pub print_progress_interval: u64, /// Light client RPC url #[serde(default)] @@ -31,8 +30,8 @@ impl Default for BlockPollerConfig { Self { start_block: None, polling_interval: 6000, - max_blocks_per_step: max_blocks_per_step_default(), - print_progress_interval: print_progress_interval_default(), + max_blocks_per_step: defaults::max_blocks_per_step(), + print_progress_interval: defaults::print_progress_interval(), light_client_rpc_url: None, } } diff --git a/crates/relayer-config/src/cli.rs b/crates/relayer-config/src/cli.rs index 1e8dba91f..eb7d81cd5 100644 --- a/crates/relayer-config/src/cli.rs +++ b/crates/relayer-config/src/cli.rs @@ -135,7 +135,7 @@ pub async fn create_store( let dirs = ProjectDirs::from(PACKAGE_ID[0], PACKAGE_ID[1], PACKAGE_ID[2]) .context("failed to get config")?; let p = match opts.config_dir.as_ref() { - Some(p) => p.to_path_buf(), + Some(p) => p.clone(), None => dirs.data_local_dir().to_path_buf(), }; let db_path = match opts.config_dir.as_ref().zip(p.parent()) { diff --git a/crates/relayer-config/src/defaults.rs b/crates/relayer-config/src/defaults.rs new file mode 100644 index 000000000..3eebc2442 --- /dev/null +++ b/crates/relayer-config/src/defaults.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; + +/// The default port the relayer will listen on. Defaults to 9955. +pub const fn relayer_port() -> u16 { + 9955 +} +/// Leaves watcher is set to `true` by default. +pub const fn enable_leaves_watcher() -> bool { + true +} +/// Data query access is set to `true` by default. +pub const fn enable_data_query() -> bool { + true +} +/// The maximum events per step is set to `100` by default. +pub const fn max_blocks_per_step() -> u64 { + 500 +} +/// The print progress interval is set to `7_000` by default. +pub const fn print_progress_interval() -> u64 { + 7_000 +} + +/// The default unlisted assets. +pub fn unlisted_assets() -> HashMap { + HashMap::from_iter([ + ( + String::from("tTNT"), + crate::UnlistedAssetConfig { + name: String::from("Test Tangle Network Token"), + decimals: 18, + price: 0.10, + }, + ), + ( + String::from("TNT"), + crate::UnlistedAssetConfig { + name: String::from("Tangle Network Token"), + decimals: 18, + price: 0.10, + }, + ), + ]) +} diff --git a/crates/relayer-config/src/event_watcher.rs b/crates/relayer-config/src/event_watcher.rs index 0bf00b65a..d6e73ea13 100644 --- a/crates/relayer-config/src/event_watcher.rs +++ b/crates/relayer-config/src/event_watcher.rs @@ -2,28 +2,24 @@ use super::*; /// EventsWatchConfig is the configuration for the events watch. #[derive(Debug, Clone, Serialize, Deserialize, Default, Copy)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct EventsWatcherConfig { /// A flag for enabling API endpoints for querying data from the relayer. - #[serde(default = "enable_data_query_default")] + #[serde(default = "defaults::enable_data_query")] pub enable_data_query: bool, - #[serde(default = "enable_leaves_watcher_default")] + #[serde(default = "defaults::enable_leaves_watcher")] /// if it is enabled for this chain or not. pub enabled: bool, /// Polling interval in milliseconds - #[serde(rename(serialize = "pollingInterval"))] pub polling_interval: u64, /// The maximum number of events to fetch in one request. - #[serde(skip_serializing, default = "max_blocks_per_step_default")] + #[serde(default = "defaults::max_blocks_per_step")] pub max_blocks_per_step: u64, /// print sync progress frequency in milliseconds /// if it is zero, means no progress will be printed. - #[serde( - rename(serialize = "printProgressInterval"), - default = "print_progress_interval_default" - )] + #[serde(default = "defaults::print_progress_interval")] pub print_progress_interval: u64, /// Sync blocks from - #[serde(rename(serialize = "syncBlocksFrom"))] + #[serde(skip_serializing_if = "Option::is_none")] pub sync_blocks_from: Option, } diff --git a/crates/relayer-config/src/evm/mod.rs b/crates/relayer-config/src/evm/mod.rs index 64ff35a65..7afa03763 100644 --- a/crates/relayer-config/src/evm/mod.rs +++ b/crates/relayer-config/src/evm/mod.rs @@ -1,4 +1,7 @@ +use core::fmt; + use ethereum_types::Address; +use url::Url; use webb_relayer_types::{private_key::PrivateKey, rpc_url::RpcUrl}; use crate::{ @@ -11,7 +14,7 @@ use super::*; /// EvmChainConfig is the configuration for the EVM based networks. #[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct EvmChainConfig { /// String that groups configuration for this chain on a human-readable name. pub name: String, @@ -20,13 +23,13 @@ pub struct EvmChainConfig { pub enabled: bool, /// Http(s) Endpoint for quick Req/Res #[serde(skip_serializing)] - pub http_endpoint: RpcUrl, + pub http_endpoint: HttpEndpoint, /// Websocket Endpoint for long living connections #[serde(skip_serializing)] pub ws_endpoint: RpcUrl, /// Block confirmations - #[serde(skip_serializing)] - pub block_confirmations: u32, + #[serde(skip_serializing, default)] + pub block_confirmations: u8, /// Block Explorer for this chain. /// /// Optional, and only used for printing a clickable links @@ -34,7 +37,6 @@ pub struct EvmChainConfig { #[serde(skip_serializing)] pub explorer: Option, /// chain specific id (output of chainId opcode on EVM networks) - #[serde(rename(serialize = "chainId"))] pub chain_id: u32, /// The Private Key of this account on this network /// the format is more dynamic here: @@ -61,6 +63,7 @@ pub struct EvmChainConfig { #[serde(skip_serializing)] pub private_key: Option, /// Optionally, a user can specify an account to receive rewards for relaying + #[serde(skip_serializing_if = "Option::is_none")] pub beneficiary: Option

, /// Supported contracts over this chain. #[serde(default)] @@ -69,12 +72,42 @@ pub struct EvmChainConfig { #[serde(skip_serializing, default)] pub tx_queue: TxQueueConfig, /// Block poller/listening configuration + #[serde(skip_serializing, default)] pub block_poller: Option, } +/// configuration for adding http endpoints. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum HttpEndpoint { + /// Single http endpoint + Single(RpcUrl), + /// Multiple http endpoints + Multiple(Vec), +} + +impl fmt::Display for HttpEndpoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HttpEndpoint::Single(url) => write!(f, "{}", url), + HttpEndpoint::Multiple(urls) => { + let urls: Vec = + urls.iter().map(ToString::to_string).collect(); + write!(f, "{}", urls.join(", ")) + } + } + } +} + +impl From for HttpEndpoint { + fn from(url: Url) -> Self { + HttpEndpoint::Single(url.into()) + } +} + /// Linked anchor config for Evm based target system #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct EvmLinkedAnchorConfig { /// The chain Id pub chain_id: u32, @@ -88,49 +121,83 @@ pub struct EvmLinkedAnchorConfig { pub enum Contract { /// The VAnchor contract configuration. VAnchor(VAnchorContractConfig), - /// The Open VAnchor contract configuration. - OpenVAnchor(VAnchorContractConfig), /// The Signature Bridge contract configuration. SignatureBridge(SignatureBridgeContractConfig), } /// CommonContractConfig represents the common configuration for contracts. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct CommonContractConfig { /// The address of this contract on this chain. pub address: Address, /// the block number where this contract got deployed at. - #[serde(rename(serialize = "deployedAt"))] pub deployed_at: u64, } +/// Smart Anchor Updates applies polices to the AnchorUpdate Proposals +/// which helps to reduce the number of updates, hence the number of +/// transactions and gas fees. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] +pub struct SmartAnchorUpdatesConfig { + /// Enables smart anchor updates + pub enabled: bool, + /// Minimum time delay for the time delay sliding window + #[serde(skip_serializing_if = "Option::is_none")] + pub min_time_delay: Option, + /// Maximum time delay for the time delay sliding window + #[serde(skip_serializing_if = "Option::is_none")] + pub max_time_delay: Option, + /// Initial time delay for the time delay sliding window + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_time_delay: Option, + /// Time delay sliding window size + #[serde(skip_serializing_if = "Option::is_none")] + pub time_delay_window_size: Option, +} + +impl Default for SmartAnchorUpdatesConfig { + fn default() -> Self { + Self { + // Disabled by default + // Experimental feature + enabled: false, + min_time_delay: Some(30), + max_time_delay: Some(300), + initial_time_delay: Some(10), + time_delay_window_size: Some(5), + } + } +} + /// VAnchorContractConfig represents the configuration for the VAnchor contract. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct VAnchorContractConfig { /// Common contract configuration. #[serde(flatten)] pub common: CommonContractConfig, /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, /// The type of the optional signing backend used for signing proposals. It can be None for pure Tx relayers - #[serde(rename(serialize = "proposalSigningBackend"))] + #[serde(skip_serializing_if = "Option::is_none")] pub proposal_signing_backend: Option, /// A List of linked Anchor Contracts (on other chains) to this contract. - #[serde(rename(serialize = "linkedAnchors"), default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub linked_anchors: Option>, + /// For configuring the smart anchor updates + #[serde(default)] + pub smart_anchor_updates: SmartAnchorUpdatesConfig, } /// Signature Bridge contract configuration. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct SignatureBridgeContractConfig { /// Common contract configuration. #[serde(flatten)] pub common: CommonContractConfig, /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, } diff --git a/crates/relayer-config/src/lib.rs b/crates/relayer-config/src/lib.rs index 89cf7606e..063e319db 100644 --- a/crates/relayer-config/src/lib.rs +++ b/crates/relayer-config/src/lib.rs @@ -34,6 +34,8 @@ pub mod block_poller; /// CLI configuration #[cfg(feature = "cli")] pub mod cli; +/// Module for all the default values. +pub mod defaults; /// Event watcher configuration pub mod event_watcher; /// EVM configuration @@ -49,42 +51,26 @@ use evm::EvmChainConfig; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use substrate::SubstrateConfig; - -/// The default port the relayer will listen on. Defaults to 9955. -const fn default_port() -> u16 { - 9955 -} -/// Leaves watcher is set to `true` by default. -const fn enable_leaves_watcher_default() -> bool { - true -} -/// Data query access is set to `true` by default. -const fn enable_data_query_default() -> bool { - true -} -/// The maximum events per step is set to `100` by default. -const fn max_blocks_per_step_default() -> u64 { - 100 -} -/// The print progress interval is set to `7_000` by default. -const fn print_progress_interval_default() -> u64 { - 7_000 -} +use webb::evm::ethers::types::Chain; +use webb_relayer_types::etherscan_api::EtherscanApiKey; /// WebbRelayerConfig is the configuration for the webb relayer. #[derive(Debug, Clone, Deserialize, Serialize, Default)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct WebbRelayerConfig { /// WebSocket Server Port number /// /// default to 9955 - #[serde(default = "default_port", skip_serializing)] + #[serde(default = "defaults::relayer_port", skip_serializing)] pub port: u16, /// EVM based networks and the configuration. /// /// a map between chain name and its configuration. #[serde(default)] pub evm: HashMap, + /// Etherscan API key configuration for evm based chains. + #[serde(default, skip_serializing)] + pub evm_etherscan: HashMap, /// ETH2 based networks and the configuration /// /// a map between chain name and its configuration @@ -96,9 +82,6 @@ pub struct WebbRelayerConfig { /// a map between chain name and its configuration. #[serde(default)] pub substrate: HashMap, - /// For Experimental Options - #[serde(default)] - pub experimental: ExperimentalConfig, /// Configuration for running relayer /// /// by deafult all features are enabled @@ -108,6 +91,11 @@ pub struct WebbRelayerConfig { /// 3. Private transaction relaying #[serde(default)] pub features: FeaturesConfig, + /// Configuration for the assets that are not listed on any exchange. + /// + /// it is a simple map between the asset symbol and its configuration. + #[serde(default = "defaults::unlisted_assets")] + pub assets: HashMap, } impl WebbRelayerConfig { @@ -141,21 +129,9 @@ impl WebbRelayerConfig { } } -/// ExperimentalConfig is the configuration for the Experimental Options. -#[derive(Debug, Clone, Copy, Deserialize, Serialize, Default)] -#[serde(rename_all = "kebab-case")] -pub struct ExperimentalConfig { - /// Enable the Smart Anchor Updates when it comes to signaling - /// the bridge to create the proposals. - pub smart_anchor_updates: bool, - /// The number of retries to check if an anchor is updated before sending our update - /// or not, before actually sending our update. - pub smart_anchor_updates_retries: u32, -} - /// FeaturesConfig is the configuration for running relayer with option. #[derive(Debug, Clone, Copy, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct FeaturesConfig { /// Enable data quering for leafs pub data_query: bool, @@ -164,6 +140,7 @@ pub struct FeaturesConfig { /// Enable private tx relaying pub private_tx_relay: bool, } + impl Default for FeaturesConfig { fn default() -> Self { Self { @@ -174,9 +151,26 @@ impl Default for FeaturesConfig { } } +/// Configuration to add etherscan API key +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] +pub struct EtherscanApiConfig { + /// Chain Id + pub chain_id: u32, + /// A wrapper type around the `String` to allow reading it from the env. + #[serde(skip_serializing)] + pub api_key: EtherscanApiKey, + /// An optional URL to use for the Etherscan API instead of the default. + /// + /// This is useful for testing against a local Etherscan API. + /// Or in case of testnets, the Etherscan GasOracle API is not available. + /// So we can use the mainnet API URL to get the gas price. + pub api_url: Option, +} + /// TxQueueConfig is the configuration for the TxQueue. #[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct TxQueueConfig { /// Maximum number of milliseconds to wait before dequeuing a transaction from /// the queue. @@ -190,3 +184,66 @@ impl Default for TxQueueConfig { } } } + +/// UnlistedAssetConfig is the configuration for the assets that are not listed on any exchange. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] +pub struct UnlistedAssetConfig { + /// The Price of the asset in USD. + pub price: f64, + /// The name of the asset. + pub name: String, + /// The decimals of the asset. + pub decimals: u8, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn all_config_files_are_correct() { + // This test is to make sure that all the config files are correct. + // This walks all the directories inside the root of the config directory + // and tries to parse the config file(s) inside it. + + let git_root = std::process::Command::new("git") + .args(["rev-parse", "--show-toplevel"]) + .output() + .expect("Failed to get git root") + .stdout; + let git_root = std::str::from_utf8(&git_root) + .expect("Failed to parse git root") + .trim(); + let config_dir = std::path::Path::new(git_root).join("config"); + let config_dirs = + glob::glob(config_dir.join("**").join("**").to_str().unwrap()) + .expect("Failed to read config directory") + .filter_map(Result::ok) + .filter(|p| p.is_dir()) + .collect::>(); + assert!( + !config_dirs.is_empty(), + "No config directories found in the config directory" + ); + let cwd = + std::env::current_dir().expect("Failed to get current directory"); + // For each config directory, we try to parse the config file(s) inside it. + for config_subdir in config_dirs { + std::env::set_current_dir(&config_subdir) + .expect("Failed to set current directory"); + // Load the example dot env file. + let _ = dotenv::from_filename(".env.example"); + if let Err(e) = utils::load(&config_subdir) { + panic!("Failed to parse config file in directory: {config_subdir:?} with error: {e}"); + } + + dotenv::vars().for_each(|(k, _)| { + std::env::remove_var(k); + }); + + std::env::set_current_dir(&cwd) + .expect("Failed to set current directory"); + } + } +} diff --git a/crates/relayer-config/src/signing_backend.rs b/crates/relayer-config/src/signing_backend.rs index a042443ee..5df876193 100644 --- a/crates/relayer-config/src/signing_backend.rs +++ b/crates/relayer-config/src/signing_backend.rs @@ -15,17 +15,17 @@ pub enum ProposalSigningBackendConfig { /// DKGNodeSigningBackendConfig represents the configuration for the DKGNode signing backend. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct DkgNodeProposalSigningBackendConfig { - /// The name of the DKG Node that this contract will use. + /// The chain id of the DKG Node that this contract will use. /// /// Must be defined in the config. - pub node: String, + pub chain_id: u32, } /// MockedSigningBackendConfig represents the configuration for the Mocked signing backend. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct MockedProposalSigningBackendConfig { /// The private key of the current Governor. #[serde(skip_serializing)] diff --git a/crates/relayer-config/src/substrate/mod.rs b/crates/relayer-config/src/substrate/mod.rs index 54114043d..971b9e755 100644 --- a/crates/relayer-config/src/substrate/mod.rs +++ b/crates/relayer-config/src/substrate/mod.rs @@ -9,7 +9,7 @@ use crate::{ /// SubstrateConfig is the relayer configuration for the Substrate based networks. #[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct SubstrateConfig { /// String that groups configuration for this chain on a human-readable name. pub name: String, @@ -29,7 +29,6 @@ pub struct SubstrateConfig { #[serde(skip_serializing)] pub explorer: Option, /// chain specific id (output of ChainIdentifier constant on LinkableTree Pallet) - #[serde(rename(serialize = "chainId"))] pub chain_id: u32, /// Interprets the string in order to generate a key Pair. in the /// case that the pair can be expressed as a direct derivation from a seed (some cases, such as Sr25519 derivations @@ -64,8 +63,6 @@ pub struct SubstrateConfig { pub suri: Option, /// Optionally, a user can specify an account to receive rewards for relaying pub beneficiary: Option, - /// Which Substrate Runtime to use? - pub runtime: SubstrateRuntime, /// Supported pallets over this substrate node. #[serde(default)] pub pallets: Vec, @@ -76,7 +73,7 @@ pub struct SubstrateConfig { /// Linked anchor config for Substrate based target system #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct SubstrateLinkedAnchorConfig { /// chain Id pub chain_id: u32, @@ -103,62 +100,46 @@ pub enum Pallet { VAnchorBn254(VAnchorBn254PalletConfig), } -/// Enumerates the supported Substrate runtimes. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum SubstrateRuntime { - /// The DKG runtime. (dkg-substrate) - #[serde(rename = "DKG")] - Dkg, - /// The Webb Protocol runtime. (protocol-substrate) - WebbProtocol, -} - /// DKGProposalsPalletConfig represents the configuration for the DKGProposals pallet. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct DKGProposalsPalletConfig { /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, } /// DKGPalletConfig represents the configuration for the DKG pallet (dkg-metadata). #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct DKGPalletConfig { /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, } /// DKGProposalHandlerPalletConfig represents the configuration for the DKGProposalHandler pallet. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct DKGProposalHandlerPalletConfig { /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, } /// SignatureBridgePalletConfig represents the configuration for the SignatureBridge pallet. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct SignatureBridgePalletConfig { /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, } /// VAnchorBn254PalletConfig represents the configuration for the VAnchorBn254 pallet. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] +#[serde(rename_all(serialize = "camelCase", deserialize = "kebab-case"))] pub struct VAnchorBn254PalletConfig { /// Controls the events watcher - #[serde(rename(serialize = "eventsWatcher"))] pub events_watcher: EventsWatcherConfig, /// The type of the optional signing backend used for signing proposals. It can be None for pure Tx relayers - #[serde(rename(serialize = "proposalSigningBackend"))] pub proposal_signing_backend: Option, /// A List of linked Anchor on this chain. - #[serde(rename(serialize = "linkedAnchors"), default)] + #[serde(default)] pub linked_anchors: Option>, } diff --git a/crates/relayer-config/src/utils.rs b/crates/relayer-config/src/utils.rs index 04850b4cd..499055506 100644 --- a/crates/relayer-config/src/utils.rs +++ b/crates/relayer-config/src/utils.rs @@ -150,7 +150,7 @@ pub fn postloading_process( let linked_anchors: Vec = linked_anchors .into_iter() - .map(|anchor| anchor.into_raw_resource_id()) + .map(LinkedAnchorConfig::into_raw_resource_id) .collect(); cfg.linked_anchors = Some(linked_anchors); } @@ -169,7 +169,7 @@ pub fn postloading_process( let linked_anchors: Vec = linked_anchors .into_iter() - .map(|anchor| anchor.into_raw_resource_id()) + .map(LinkedAnchorConfig::into_raw_resource_id) .collect(); cfg.linked_anchors = Some(linked_anchors); } diff --git a/crates/relayer-context/Cargo.toml b/crates/relayer-context/Cargo.toml index e6b7cb810..bf13468c8 100644 --- a/crates/relayer-context/Cargo.toml +++ b/crates/relayer-context/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-context" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -11,18 +11,21 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -webb-relayer-config = { path = "../relayer-config"} -webb-relayer-utils = { path = "../relayer-utils"} -webb-relayer-store = { path = "../relayer-store" } +webb-relayer-config = { workspace = true } +webb-relayer-utils = { workspace = true } +webb-relayer-store = { workspace = true } +webb-price-oracle-backends = { workspace = true, features = ["coingecko"] } +tracing = { workspace = true } tokio = { workspace = true } webb = { workspace = true } sp-core = { workspace = true } # Used by ethers (but we need it to be vendored with the lib). native-tls = { workspace = true } - -coingecko = "1.0.1" -ethers = {version = "1.0.2", features = ["rustls"] } +serde = { workspace = true } +serde_json = { workspace = true } +http = "0.2.9" +regex = { version = "1" } [features] default = ["std", "evm", "substrate"] diff --git a/crates/relayer-context/src/ethers_retry_policy.rs b/crates/relayer-context/src/ethers_retry_policy.rs new file mode 100644 index 000000000..c97d6d086 --- /dev/null +++ b/crates/relayer-context/src/ethers_retry_policy.rs @@ -0,0 +1,162 @@ +use std::time::Duration; + +use webb::evm::ethers::providers::ProviderError; +use webb::evm::ethers::providers::{JsonRpcError, RetryPolicy}; + +/// Implements [RetryPolicy] that will retry requests that errored with +/// status code 429 i.e. TOO_MANY_REQUESTS +/// +/// Infura often fails with a `"header not found"` rpc error which is apparently linked to load +/// balancing, which are retried as well. +/// +// Copied and modifed from: https://github.com/gakonst/ethers-rs/blob/b6c2d89c379ebc4573c269b5dc80c8a9fd2e9a49/ethers-providers/src/rpc/transports/retry.rs#L364 +#[derive(Debug)] +pub struct WebbHttpRetryPolicy { + err_regex: regex::Regex, +} + +impl WebbHttpRetryPolicy { + pub fn new() -> Self { + Self { + err_regex: regex::Regex::new( + r"(?mixU)\b(?:rate|limit|429|Too \s Many \s Requests)\b", + ) + .expect("Valid Regex"), + } + } + + pub fn boxed() -> Box { + Box::new(Self::new()) + } +} + +fn should_retry_json_rpc_error(err: &JsonRpcError) -> bool { + let JsonRpcError { code, message, .. } = err; + // alchemy throws it this way + if *code == 429 { + return true; + } + + // This is an infura error code for `exceeded project rate limit` + if *code == -32005 { + return true; + } + + // alternative alchemy error for specific IPs + if *code == -32016 && message.contains("rate limit") { + return true; + } + + match message.as_str() { + // this is commonly thrown by infura and is apparently a load balancer issue, see also + "header not found" => true, + // also thrown by infura if out of budget for the day and ratelimited + "daily request count exceeded, request rate limited" => true, + _ => false, + } +} + +// check json rpc error in serde error +fn should_retry_json_rpc_error_from_serde( + err: &serde_json::Error, + err_regex: regex::Regex, +) -> bool { + // some providers send invalid JSON RPC in the error case (no `id:u64`), but the + // text should be a `JsonRpcError` + #[derive(serde::Deserialize)] + struct Resp { + error: JsonRpcError, + } + + if let Ok(resp) = serde_json::from_str::(&err.to_string()) { + return should_retry_json_rpc_error(&resp.error); + } + + let err_text = err.to_string().to_lowercase(); + + // last resort, some providers send the error message in the text + // and the text itself is not a valid json response either. + // check if we have the word "rate", or "limit" in the error message + // and if so, we should retry + + let should_retry = err_regex.is_match(&err_text) + || matches!(err_text.as_str(), "expected value at line 1 column 1"); + + tracing::event!( + target: webb_relayer_utils::probe::TARGET, + tracing::Level::DEBUG, + kind = %webb_relayer_utils::probe::Kind::Retry, + should_retry = should_retry, + error = %err_text, + ); + should_retry +} + +impl RetryPolicy for WebbHttpRetryPolicy { + fn should_retry(&self, error: &ProviderError) -> bool { + tracing::debug!("should_retry: {:?}", error); + match error { + ProviderError::HTTPError(err) => { + err.status() == Some(http::StatusCode::TOO_MANY_REQUESTS) + } + ProviderError::JsonRpcClientError(err) => { + if let Some(e) = err.as_error_response() { + return should_retry_json_rpc_error(e); + }; + + tracing::debug!("Error source: {:?}", err.source()); + if let Some(e) = err.as_serde_error() { + // let new_err = SerdeJson::from(err).into(); + tracing::debug!("Serede error: {:?}", e); + return should_retry_json_rpc_error_from_serde( + e, + self.err_regex.clone(), + ); + } + + false + } + ProviderError::EnsError(_) => true, + ProviderError::EnsNotOwned(_) => true, + ProviderError::HexError(_) => false, + ProviderError::CustomError(_) => false, + ProviderError::UnsupportedRPC => false, + ProviderError::UnsupportedNodeClient => false, + ProviderError::SignerUnavailable => false, + + ProviderError::SerdeJson(err) => { + should_retry_json_rpc_error_from_serde( + err, + self.err_regex.clone(), + ) + } + } + } + + fn backoff_hint(&self, error: &ProviderError) -> Option { + const DEFAULT_BACKOFF: Duration = Duration::from_secs(5); + + if let ProviderError::JsonRpcClientError(err) = error { + let json_rpc_error = err.as_error_response(); + if let Some(json_rpc_error) = json_rpc_error { + if let Some(data) = &json_rpc_error.data { + // if daily rate limit exceeded, infura returns the requested backoff in the error + // response + let Some(backoff_seconds) = data.get("rate").and_then(|v| v.get("backoff_seconds")) else { + return Some(DEFAULT_BACKOFF); + }; + // infura rate limit error + if let Some(seconds) = backoff_seconds.as_u64() { + return Some(Duration::from_secs(seconds)); + } + if let Some(seconds) = backoff_seconds.as_f64() { + return Some(Duration::from_secs(seconds as u64 + 1)); + } + } + } + } + + // A default value of 5s + Some(DEFAULT_BACKOFF) + } +} diff --git a/crates/relayer-context/src/lib.rs b/crates/relayer-context/src/lib.rs index 233aa05ca..8eee6b4e1 100644 --- a/crates/relayer-context/src/lib.rs +++ b/crates/relayer-context/src/lib.rs @@ -16,12 +16,11 @@ //! # Relayer Context Module 🕸️ //! //! A module for managing the context of the relayer. -use std::convert::TryFrom; -use std::sync::Arc; use std::time::Duration; - +use std::{collections::HashMap, sync::Arc}; use tokio::sync::{broadcast, Mutex}; +use webb::evm::ethers; #[cfg(feature = "evm")] use webb::evm::ethers::core::k256::SecretKey; #[cfg(feature = "evm")] @@ -29,13 +28,25 @@ use webb::evm::ethers::prelude::*; #[cfg(feature = "substrate")] use sp_core::sr25519::Pair as Sr25519Pair; +use webb::evm::ethers::middleware::gas_oracle::{ + Cache as CachedGasOracle, Etherscan as EtherscanGasOracle, + Median as GasOracleMedian, ProviderOracle, +}; #[cfg(feature = "substrate")] use webb::substrate::subxt; -use coingecko::CoinGeckoClient; +use webb_price_oracle_backends::{ + CachedPriceBackend, CoinGeckoBackend, DummyPriceBackend, PriceOracleMerger, +}; use webb_relayer_store::SledStore; use webb_relayer_utils::metric::{self, Metrics}; +mod ethers_retry_policy; +use ethers_retry_policy::WebbHttpRetryPolicy; +use webb_relayer_utils::multi_provider::MultiProvider; + +type EthersClient = Provider>>; + /// RelayerContext contains Relayer's configuration and shutdown signal. #[derive(Clone)] pub struct RelayerContext { @@ -53,10 +64,13 @@ pub struct RelayerContext { /// Represents the metrics for the relayer pub metrics: Arc>, store: SledStore, - /// API client for https://www.coingecko.com/ - coin_gecko_client: Arc, - /// API client for https://etherscan.io/ - etherscan_client: Client, + /// Price backend for fetching prices. + price_oracle: Arc, + /// Hashmap of + etherscan_clients: Arc>, + + /// Evm Providers Cache. + evm_providers: Arc>>, } impl RelayerContext { @@ -64,19 +78,83 @@ impl RelayerContext { pub fn new( config: webb_relayer_config::WebbRelayerConfig, store: SledStore, - ) -> Self { + ) -> webb_relayer_utils::Result { let (notify_shutdown, _) = broadcast::channel(2); - let metrics = Arc::new(Mutex::new(Metrics::new())); - let coin_gecko_client = Arc::new(CoinGeckoClient::default()); - let etherscan_client = Client::new_from_env(Chain::Mainnet).unwrap(); - Self { + let metrics = Arc::new(Mutex::new(Metrics::new()?)); + + let dummy_backend = { + let price_map = config + .assets + .iter() + .map(|(token, details)| (token.clone(), details.price)) + .collect(); + DummyPriceBackend::new(price_map) + }; + // **chef's kiss** this is so beautiful + let cached_coingecko_backend = CachedPriceBackend::builder() + .backend(CoinGeckoBackend::builder().build()) + .store(store.clone()) + .use_cache_if_source_unavailable() + .even_if_expired() + .build(); + // merge all the price oracle backends + let price_oracle = PriceOracleMerger::builder() + .merge(Box::new(cached_coingecko_backend)) + .merge(Box::new(dummy_backend)) + .build(); + let price_oracle = Arc::new(price_oracle); + let mut etherscan_clients = HashMap::new(); + for (chain, etherscan_config) in &config.evm_etherscan { + let client_builder = ethers::etherscan::Client::builder() + .chain(*chain)? + .with_api_key(etherscan_config.api_key.as_str()); + // if the api url is set, override the default + let client = if let Some(ref api_url) = etherscan_config.api_url { + client_builder.with_api_url(api_url.as_str())?.build()? + } else { + client_builder.build()? + }; + etherscan_clients.insert(etherscan_config.chain_id.into(), client); + } + + // Create a Map for all EVM Chains + let mut evm_providers = HashMap::new(); + for (_, chain_config) in config.evm.iter() { + let mut providers = Vec::new(); + match chain_config.http_endpoint.clone() { + webb_relayer_config::evm::HttpEndpoint::Single(rpc_url) => { + let provider = Http::new(rpc_url); + providers.push(provider); + } + webb_relayer_config::evm::HttpEndpoint::Multiple(rpc_urls) => { + rpc_urls.iter().for_each(|rpc_url| { + let provider = Http::new(rpc_url.clone()); + providers.push(provider); + }); + } + } + + let multi_provider = MultiProvider::new(Arc::new(providers)); + // Wrap the provider with a retry client. + let retry_client = RetryClientBuilder::default() + .timeout_retries(u32::MAX) + .rate_limit_retries(u32::MAX) + .build(multi_provider, WebbHttpRetryPolicy::boxed()); + + let provider = Arc::new(Provider::new(retry_client)); + + evm_providers.insert(chain_config.chain_id.into(), provider); + } + + Ok(Self { config, notify_shutdown, metrics, store, - coin_gecko_client, - etherscan_client, - } + price_oracle, + etherscan_clients: Arc::new(etherscan_clients), + evm_providers: Arc::new(evm_providers), + }) } /// Returns a broadcast receiver handle for the shutdown signal. pub fn shutdown_signal(&self) -> Shutdown { @@ -92,18 +170,19 @@ impl RelayerContext { /// /// * `chain_id` - A string representing the chain id. #[cfg(feature = "evm")] - pub async fn evm_provider( + pub async fn evm_provider>( &self, - chain_id: &str, - ) -> webb_relayer_utils::Result> { - let chain_config = self.config.evm.get(chain_id).ok_or_else(|| { - webb_relayer_utils::Error::ChainNotFound { - chain_id: chain_id.to_string(), - } - })?; - let provider = Provider::try_from(chain_config.http_endpoint.as_str())? - .interval(Duration::from_millis(5u64)); - Ok(provider) + chain_id: I, + ) -> webb_relayer_utils::Result> { + let chain_id: types::U256 = chain_id.into(); + if let Some(provider) = self.evm_providers.get(&chain_id) { + Ok(provider.clone()) + } else { + let chain_id_string = chain_id.clone().to_string(); + Err(webb_relayer_utils::Error::ChainNotFound { + chain_id: chain_id_string, + }) + } } /// Sets up and returns an EVM wallet for the relayer. /// @@ -111,12 +190,14 @@ impl RelayerContext { /// /// * `chain_id` - A string representing the chain id. #[cfg(feature = "evm")] - pub async fn evm_wallet( + pub async fn evm_wallet>( &self, - chain_name: &str, + chain_id: I, ) -> webb_relayer_utils::Result { + let chain_id: types::U256 = chain_id.into(); + let chain_name = chain_id.to_string(); let chain_config = - self.config.evm.get(chain_name).ok_or_else(|| { + self.config.evm.get(&chain_name).ok_or_else(|| { webb_relayer_utils::Error::ChainNotFound { chain_id: chain_name.to_string(), } @@ -125,7 +206,7 @@ impl RelayerContext { .private_key .as_ref() .ok_or(webb_relayer_utils::Error::MissingSecrets)?; - let key = SecretKey::from_be_bytes(private_key.as_bytes())?; + let key = SecretKey::from_bytes(private_key.as_bytes().into())?; let chain_id = chain_config.chain_id; let wallet = LocalWallet::from(key).with_chain_id(chain_id); Ok(wallet) @@ -136,12 +217,14 @@ impl RelayerContext { /// /// * `chain_id` - A string representing the chain ID. #[cfg(feature = "substrate")] - pub async fn substrate_provider( + pub async fn substrate_provider>( &self, - chain_id: &str, + chain_id: I, ) -> webb_relayer_utils::Result> { + let chain_id: types::U256 = chain_id.into(); + let chain_name = chain_id.to_string(); let node_config = - self.config.substrate.get(chain_id).ok_or_else(|| { + self.config.substrate.get(&chain_name).ok_or_else(|| { webb_relayer_utils::Error::NodeNotFound { chain_id: chain_id.to_string(), } @@ -158,18 +241,18 @@ impl RelayerContext { /// /// * `chain_id` - A string representing the chain ID. #[cfg(feature = "substrate")] - pub async fn substrate_wallet( + pub async fn substrate_wallet>( &self, - chain_id: &str, + chain_id: I, ) -> webb_relayer_utils::Result { - let node_config = self - .config - .substrate - .get(chain_id) - .cloned() - .ok_or_else(|| webb_relayer_utils::Error::NodeNotFound { - chain_id: chain_id.to_string(), - })?; + let chain_id: types::U256 = chain_id.into(); + let chain_name = chain_id.to_string(); + let node_config = + self.config.substrate.get(&chain_name).cloned().ok_or_else( + || webb_relayer_utils::Error::NodeNotFound { + chain_id: chain_id.to_string(), + }, + )?; let suri_key = node_config .suri .ok_or(webb_relayer_utils::Error::MissingSecrets)?; @@ -181,14 +264,39 @@ impl RelayerContext { &self.store } - /// Returns API client for https://www.coingecko.com/ - pub fn coin_gecko_client(&self) -> &Arc { - &self.coin_gecko_client + /// Returns a price oracle for fetching token prices. + pub fn price_oracle(&self) -> Arc { + self.price_oracle.clone() } - /// Returns API client for https://etherscan.io/ - pub fn etherscan_client(&self) -> &Client { - &self.etherscan_client + /// Returns a gas oracle for the given chain. + pub async fn gas_oracle>( + &self, + chain_id: I, + ) -> webb_relayer_utils::Result { + let chain_id: types::U256 = chain_id.into(); + let chain_provider = self.evm_provider(chain_id).await?; + let provider_gas_oracle = ProviderOracle::new(chain_provider); + let mut gas_oracle = GasOracleMedian::new(); + // Give only 10% of the weight to the provider gas oracle + // since it is not very accurate. + gas_oracle.add_weighted(0.1, provider_gas_oracle); + // Check if we have etherscan client for this chain + if let Some(etherscan_client) = self.etherscan_clients.get(&chain_id) { + let etherscan_gas_oracle = + EtherscanGasOracle::new(etherscan_client.clone()); + let cached = CachedGasOracle::new( + // Cache for 5 minutes to avoid hitting etherscan rate limit + Duration::from_secs(5 * 60), + etherscan_gas_oracle, + ); + // Etherscan gas oracle is more accurate than the provider gas oracle + // so give it the remaining 90% of the weight. + gas_oracle.add_weighted(0.9, cached); + } + // TODO: Add more gas oracles + + Ok(gas_oracle) } } diff --git a/crates/relayer-handler-utils/Cargo.toml b/crates/relayer-handler-utils/Cargo.toml index 0ba3d1e59..d6fdda5d0 100644 --- a/crates/relayer-handler-utils/Cargo.toml +++ b/crates/relayer-handler-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-handler-utils" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -17,4 +17,4 @@ serde = { workspace = true } tokio = { workspace = true } webb = { workspace = true } # Used by ethers (but we need it to be vendored with the lib). -native-tls = { workspace = true } \ No newline at end of file +native-tls = { workspace = true } diff --git a/crates/relayer-handler-utils/src/lib.rs b/crates/relayer-handler-utils/src/lib.rs index 09b4f2b7a..483036e3d 100644 --- a/crates/relayer-handler-utils/src/lib.rs +++ b/crates/relayer-handler-utils/src/lib.rs @@ -18,14 +18,12 @@ use serde::{Deserialize, Deserializer, Serialize}; use tokio::sync::mpsc; use webb::evm::ethers::abi::Address; -use webb::evm::ethers::prelude::{ContractError, I256}; +use webb::evm::ethers::prelude::{ContractError, I256, U128}; use webb::evm::ethers::providers::Middleware; use webb::evm::ethers::types::Bytes; use webb::evm::ethers::types::{H256, U256}; use webb::substrate::subxt::utils::AccountId32; -use webb_relayer_tx_relay_utils::{ - MixerRelayTransaction, VAnchorRelayTransaction, -}; +use webb_relayer_tx_relay_utils::VAnchorRelayTransaction; /// Representation for IP address response #[derive(Debug, Serialize)] @@ -52,6 +50,24 @@ impl<'de> Deserialize<'de> for WebbI256 { Ok(WebbI256(i128_val)) } } +/// A wrapper type around [`i128`] that implements a correct way for [`Serialize`] and [`Deserialize`]. +/// +/// This supports the signed integer hex values that are not originally supported by the [`i128`] type. +#[derive(Debug, Clone, Serialize)] +#[serde(transparent)] +pub struct WebbI128(pub i128); + +impl<'de> Deserialize<'de> for WebbI128 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let i128_str = String::deserialize(deserializer)?; + let value = i128::from_str_radix(&i128_str, 16) + .map_err(serde::de::Error::custom)?; + Ok(WebbI128(value)) + } +} /// Type of Command to use #[derive(Debug, Clone, Deserialize)] @@ -77,8 +93,6 @@ pub enum EvmCommandType { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub enum SubstrateCommandType { - /// Webb Mixer. - Mixer(SubstrateMixerCommand), /// Webb Variable Anchors. VAnchor(SubstrateVAchorCommand), } @@ -170,12 +184,10 @@ type P = Vec; // Substrate raw proof bytes type R = Vec<[u8; 32]>; // Substrate roots format type E = [u8; 32]; // Substrate element type type I = AccountId32; // Substrate account identifier -type B = u128; // Substrate balance type -type A = i128; // Substrate signed amount type +type B = U128; // Substrate balance type +type A = WebbI128; // Substrate signed amount type type T = u32; // Substrate assetId -/// The command type for Substrate mixer txes -pub type SubstrateMixerCommand = MixerRelayTransaction; /// The command type for Substrate vanchor txes pub type SubstrateVAchorCommand = VAnchorRelayTransaction; diff --git a/crates/relayer-handlers/Cargo.toml b/crates/relayer-handlers/Cargo.toml index 08578d983..9c8a028f5 100644 --- a/crates/relayer-handlers/Cargo.toml +++ b/crates/relayer-handlers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-handlers" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/crates/relayer-handlers/src/lib.rs b/crates/relayer-handlers/src/lib.rs index 4d94fb426..059ee69de 100644 --- a/crates/relayer-handlers/src/lib.rs +++ b/crates/relayer-handlers/src/lib.rs @@ -17,7 +17,6 @@ #![allow(clippy::large_enum_variant)] #![warn(missing_docs)] use axum::extract::{Path, State, WebSocketUpgrade}; -use axum::http::StatusCode; use ethereum_types::{Address, U256}; use std::error::Error; use std::sync::Arc; @@ -25,6 +24,7 @@ use std::sync::Arc; use futures::prelude::*; use axum::extract::ws::{Message, WebSocket}; +use axum::http::StatusCode; use axum::response::Response; use axum::Json; use axum_client_ip::InsecureClientIp; @@ -37,12 +37,14 @@ use webb_relayer_handler_utils::{ Command, CommandResponse, CommandStream, EvmCommandType, IpInformationResponse, SubstrateCommandType, }; -use webb_relayer_tx_relay::evm::fees::{get_fee_info, FeeInfo}; +use webb_relayer_tx_relay::evm::fees::{get_evm_fee_info, EvmFeeInfo}; -use crate::routes::HandlerError; use webb_relayer_tx_relay::evm::vanchor::handle_vanchor_relay_tx; -use webb_relayer_tx_relay::substrate::mixer::handle_substrate_mixer_relay_tx; +use webb_relayer_tx_relay::substrate::fees::{ + get_substrate_fee_info, SubstrateFeeInfo, +}; use webb_relayer_tx_relay::substrate::vanchor::handle_substrate_vanchor_relay_tx; +use webb_relayer_utils::HandlerError; /// Module handles relayer API pub mod routes; @@ -178,9 +180,6 @@ pub async fn handle_cmd( SubstrateCommandType::VAnchor(vanchor) => { handle_substrate_vanchor_relay_tx(ctx, vanchor, stream).await } - SubstrateCommandType::Mixer(mixer) => { - handle_substrate_mixer_relay_tx(ctx, mixer, stream).await - } }, Command::Evm(evm) => match evm { EvmCommandType::VAnchor(vanchor) => { @@ -202,13 +201,30 @@ pub async fn handle_cmd( /// * `vanchor` - Address of the smart contract /// * `gas_amount` - How much gas the transaction needs. Don't use U256 here because it /// gets parsed incorrectly. -pub async fn handle_fee_info( +pub async fn handle_evm_fee_info( State(ctx): State>, - Path((chain_id, vanchor, gas_amount)): Path<(u64, Address, u64)>, -) -> Result, HandlerError> { - let chain_id = TypedChainId::from(chain_id); + Path((chain_id, vanchor, gas_amount)): Path<(u32, Address, u64)>, +) -> Result, HandlerError> { + let chain_id = TypedChainId::Evm(chain_id); let gas_amount = U256::from(gas_amount); - get_fee_info(chain_id, vanchor, gas_amount, ctx.as_ref()) + Ok( + get_evm_fee_info(chain_id, vanchor, gas_amount, ctx.as_ref()) + .await + .map(Json)?, + ) +} + +/// Handler for fee estimation +/// +/// # Arguments +/// * `chain_id` - ID of the blockchain +/// * `estimated_tx_fees` - Estimated transaction fees +/// * `ctx` - RelayContext reference that holds the configuration +pub async fn handle_substrate_fee_info( + State(ctx): State>, + Path((chain_id, estimated_tx_fees)): Path<(u64, u128)>, +) -> Result, HandlerError> { + get_substrate_fee_info(chain_id, estimated_tx_fees.into(), ctx.as_ref()) .await .map(Json) .map_err(|e| { diff --git a/crates/relayer-handlers/src/routes/encrypted_outputs.rs b/crates/relayer-handlers/src/routes/encrypted_outputs.rs index a37d7b4e5..c9716f2d6 100644 --- a/crates/relayer-handlers/src/routes/encrypted_outputs.rs +++ b/crates/relayer-handlers/src/routes/encrypted_outputs.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::routes::HandlerError; use axum::extract::{Path, Query, State}; use axum::http::StatusCode; use axum::Json; @@ -22,6 +21,7 @@ use std::{collections::HashMap, sync::Arc}; use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; use webb_relayer_context::RelayerContext; use webb_relayer_store::EncryptedOutputCacheStore; +use webb_relayer_utils::HandlerError; use super::OptionalRangeQuery; @@ -75,8 +75,7 @@ pub async fn handle_encrypted_outputs_cache_evm( .iter() .cloned() .filter_map(|c| match c { - webb_relayer_config::evm::Contract::VAnchor(c) - | webb_relayer_config::evm::Contract::OpenVAnchor(c) => { + webb_relayer_config::evm::Contract::VAnchor(c) => { Some((c.common.address, c.events_watcher)) } _ => None, @@ -112,14 +111,15 @@ pub async fn handle_encrypted_outputs_cache_evm( let src_typed_chain_id = TypedChainId::Evm(chain_id); let history_store_key = ResourceId::new(src_target_system, src_typed_chain_id); - let encrypted_output = ctx - .store() - .get_encrypted_output_with_range(history_store_key, query_range.into()) - .unwrap(); + let encrypted_output = ctx.store().get_encrypted_output_with_range( + history_store_key, + query_range.into(), + )?; let last_queried_block = ctx .store() - .get_last_deposit_block_number_for_encrypted_output(history_store_key) - .unwrap(); + .get_last_deposit_block_number_for_encrypted_output( + history_store_key, + )?; Ok(Json(EncryptedOutputsCacheResponse { encrypted_outputs: encrypted_output, diff --git a/crates/relayer-handlers/src/routes/info.rs b/crates/relayer-handlers/src/routes/info.rs index cfd2206d4..35dcee6ee 100644 --- a/crates/relayer-handlers/src/routes/info.rs +++ b/crates/relayer-handlers/src/routes/info.rs @@ -10,12 +10,34 @@ use webb::evm::ethers::{ }; use webb_relayer_context::RelayerContext; +/// Build info data +#[derive(Debug, Serialize)] +pub struct BuildInfo { + /// Version of the relayer + pub version: String, + /// Commit hash of the relayer + pub commit: String, + /// Build time of the relayer + pub timestamp: String, +} + /// Relayer config data #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] +pub struct RelayerConfig { + /// Relayer chain config + #[serde(flatten)] + pub config: webb_relayer_config::WebbRelayerConfig, + /// Relayer build info + pub build: BuildInfo, +} + +/// Relayer configuration response +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct RelayerInformationResponse { #[serde(flatten)] - config: webb_relayer_config::WebbRelayerConfig, + relayer_config: RelayerConfig, } /// Handles relayer configuration requests @@ -36,7 +58,7 @@ pub async fn handle_relayer_info( .private_key .as_ref() .ok_or(webb_relayer_utils::Error::MissingSecrets)?; - let key = SecretKey::from_be_bytes(key.as_bytes())?; + let key = SecretKey::from_bytes(key.as_bytes().into())?; let wallet = LocalWallet::from(key); v.beneficiary = Some(wallet.address()); webb_relayer_utils::Result::Ok(()) @@ -53,5 +75,17 @@ pub async fn handle_relayer_info( v.beneficiary = Some(suri.public()); webb_relayer_utils::Result::Ok(()) }); - Json(RelayerInformationResponse { config }) + + // Build info + let build_info = BuildInfo { + version: std::env::var("CARGO_PKG_VERSION").unwrap_or_default(), + commit: std::env::var("GIT_COMMIT").unwrap_or_default(), + timestamp: std::env::var("SOURCE_TIMESTAMP").unwrap_or_default(), + }; + let relayer_config = RelayerConfig { + config, + build: build_info, + }; + + Json(RelayerInformationResponse { relayer_config }) } diff --git a/crates/relayer-handlers/src/routes/leaves.rs b/crates/relayer-handlers/src/routes/leaves.rs index fd9aff72e..6585397cf 100644 --- a/crates/relayer-handlers/src/routes/leaves.rs +++ b/crates/relayer-handlers/src/routes/leaves.rs @@ -16,8 +16,8 @@ use axum::extract::{Path, Query, State}; use axum::http::StatusCode; use axum::Json; use std::{collections::HashMap, sync::Arc}; +use webb::evm::ethers::types; -use crate::routes::HandlerError; use ethereum_types::Address; use serde::Serialize; use webb_proposals::{ @@ -25,6 +25,7 @@ use webb_proposals::{ }; use webb_relayer_context::RelayerContext; use webb_relayer_store::LeafCacheStore; +use webb_relayer_utils::HandlerError; use super::OptionalRangeQuery; @@ -32,7 +33,7 @@ use super::OptionalRangeQuery; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct LeavesCacheResponse { - leaves: Vec>, + leaves: Vec, last_queried_block: u64, } @@ -77,8 +78,7 @@ pub async fn handle_leaves_cache_evm( .iter() .cloned() .filter_map(|c| match c { - webb_relayer_config::evm::Contract::VAnchor(c) - | webb_relayer_config::evm::Contract::OpenVAnchor(c) => { + webb_relayer_config::evm::Contract::VAnchor(c) => { Some((c.common.address, c.events_watcher)) } _ => None, @@ -117,11 +117,10 @@ pub async fn handle_leaves_cache_evm( let leaves = ctx .store() .get_leaves_with_range(history_store_key, query_range.into()) - .unwrap(); + .map(|tree| tree.into_values().collect::>())?; let last_queried_block = ctx .store() - .get_last_deposit_block_number(history_store_key) - .unwrap(); + .get_last_deposit_block_number(history_store_key)?; Ok(Json(LeavesCacheResponse { leaves, @@ -168,11 +167,11 @@ pub async fn handle_leaves_cache_substrate( let leaves = ctx .store() .get_leaves_with_range(history_store_key, query_range.into()) - .unwrap(); + .map(|tree| tree.into_values().collect::>())?; + let last_queried_block = ctx .store() - .get_last_deposit_block_number(history_store_key) - .unwrap(); + .get_last_deposit_block_number(history_store_key)?; Ok(Json(LeavesCacheResponse { leaves, diff --git a/crates/relayer-handlers/src/routes/metric.rs b/crates/relayer-handlers/src/routes/metric.rs index a400433b1..24267cf5a 100644 --- a/crates/relayer-handlers/src/routes/metric.rs +++ b/crates/relayer-handlers/src/routes/metric.rs @@ -1,4 +1,5 @@ use axum::extract::{Path, State}; +use axum::http::StatusCode; use axum::Json; use ethereum_types::Address; use serde::Serialize; @@ -8,13 +9,7 @@ use webb_proposals::{ }; use webb_relayer_context::RelayerContext; use webb_relayer_utils::metric::Metrics; - -/// Response with metrics message -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct RelayerMetricResponse { - metrics: String, -} +use webb_relayer_utils::HandlerError; /// Response with resource metrics data #[derive(Debug, Serialize)] @@ -31,11 +26,11 @@ pub struct ResourceMetricResponse { /// Handles relayer metric requests /// /// Returns a Result with the `MetricResponse` on success -pub async fn handle_metric_info() -> Json { - let metric_gathered = Metrics::gather_metrics(); - Json(RelayerMetricResponse { - metrics: metric_gathered, - }) +pub async fn handle_metric_info() -> Result { + let metric_gathered = Metrics::gather_metrics().map_err(|e| { + HandlerError(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()) + })?; + Ok(metric_gathered) } /// Handles relayer metric requests for evm based resource @@ -52,15 +47,16 @@ pub async fn handle_evm_metric_info( let typed_chain_id = TypedChainId::Evm(chain_id); let resource_id = ResourceId::new(target_system, typed_chain_id); // fetch metric for given resource_id - let resource_metric = metrics - .resource_metric_map - .entry(resource_id) - .or_insert_with(|| Metrics::register_resource_id_counters(resource_id)); + let account_balance = metrics + .account_balance_entry(typed_chain_id) + .get() + .to_string(); + let resource_metric = metrics.resource_metric_entry(resource_id); Json(ResourceMetricResponse { total_gas_spent: resource_metric.total_gas_spent.get().to_string(), total_fee_earned: resource_metric.total_fee_earned.get().to_string(), - account_balance: resource_metric.account_balance.get().to_string(), + account_balance, }) } @@ -82,14 +78,15 @@ pub async fn handle_substrate_metric_info( let resource_id = ResourceId::new(target_system, typed_chain_id); // fetch metric for given resource_id - let resource_metric = metrics - .resource_metric_map - .entry(resource_id) - .or_insert_with(|| Metrics::register_resource_id_counters(resource_id)); + let account_balance = metrics + .account_balance_entry(typed_chain_id) + .get() + .to_string(); + let resource_metric = metrics.resource_metric_entry(resource_id); Json(ResourceMetricResponse { total_gas_spent: resource_metric.total_gas_spent.get().to_string(), total_fee_earned: resource_metric.total_fee_earned.get().to_string(), - account_balance: resource_metric.account_balance.get().to_string(), + account_balance, }) } diff --git a/crates/relayer-handlers/src/routes/mod.rs b/crates/relayer-handlers/src/routes/mod.rs index c28802da0..960212c6d 100644 --- a/crates/relayer-handlers/src/routes/mod.rs +++ b/crates/relayer-handlers/src/routes/mod.rs @@ -1,6 +1,3 @@ -use axum::http::StatusCode; -use axum::response::{IntoResponse, Response}; -use axum::Json; use serde::{Deserialize, Serialize}; /// Module for handling encrypted commitment leaves API @@ -15,13 +12,6 @@ pub mod metric; /// Module for handling relayer info API pub mod info; -// Unsupported feature response -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct UnsupportedFeature { - message: String, -} - /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end`). /// @@ -69,17 +59,3 @@ const fn default_zero() -> Option { const fn default_u32_max() -> Option { Some(u32::MAX) } - -/// Error type for HTTP handlers -pub struct HandlerError( - /// HTTP status code for response - pub(crate) StatusCode, - /// Response message - pub(crate) String, -); - -impl IntoResponse for HandlerError { - fn into_response(self) -> Response { - (self.0, Json(UnsupportedFeature { message: self.1 })).into_response() - } -} diff --git a/crates/relayer-store/Cargo.toml b/crates/relayer-store/Cargo.toml index 991b708fa..71a726fd9 100644 --- a/crates/relayer-store/Cargo.toml +++ b/crates/relayer-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-store" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -14,7 +14,7 @@ repository = { workspace = true } webb-relayer-utils = { workspace = true } tracing = { workspace = true } -sled = { workspace = true } +sled = { workspace = true, optional = true } serde = { workspace = true } serde_json = { workspace = true } hex = { workspace = true } @@ -24,4 +24,9 @@ native-tls = { workspace = true, optional = true } webb-proposals = { workspace = true } tempfile = { workspace = true } -parking_lot = "^0.12" +parking_lot = { workspace = true } + +[features] +default = ["std", "sled"] +std = [] +sled = ["dep:sled"] diff --git a/crates/relayer-store/src/lib.rs b/crates/relayer-store/src/lib.rs index 531d9cc65..ea38e104c 100644 --- a/crates/relayer-store/src/lib.rs +++ b/crates/relayer-store/src/lib.rs @@ -32,9 +32,11 @@ use webb_relayer_utils::Result; /// A module for managing in-memory storage of the relayer. pub mod mem; /// A module for setting up and managing a [Sled](https://sled.rs)-based database. +#[cfg(feature = "sled")] pub mod sled; /// A store that uses [`sled`](https://sled.rs) as the backend. +#[cfg(feature = "sled")] pub use self::sled::SledStore; /// A store that uses in memory data structures as the backend. pub use mem::InMemoryStore; @@ -216,6 +218,29 @@ pub trait HistoryStore: Clone + Send + Sync { ) -> crate::Result { self.get_last_block_number(key, 1u64) } + + /// Sets the Target Block number (Usually the latest block number of the target chain) + /// This used to be able to check if we are fully synced with the target chain or not. + fn set_target_block_number + Debug>( + &self, + key: K, + block_number: u64, + ) -> crate::Result; + /// Get the target block number. + /// if not found, returns the `default_block_number`. + fn get_target_block_number + Debug>( + &self, + key: K, + default_block_number: u64, + ) -> crate::Result; + + /// an easy way to call the `get_target_block_number`. + fn get_target_block_number_or_default + Debug>( + &self, + key: K, + ) -> crate::Result { + self.get_target_block_number(key, 1u64) + } } /// A Simple Event Store, that does not store the events, instead it store the hash of the event as the key @@ -240,7 +265,7 @@ pub trait EventHashStore: Send + Sync + Clone { /// getting the leaves and insert them with a simple API. pub trait LeafCacheStore: HistoryStore { /// The Output type which is the leaf. - type Output: IntoIterator>; + type Output: IntoIterator; /// Clear leaf cache on relayer fn clear_leaves_cache + Debug>( @@ -260,27 +285,23 @@ pub trait LeafCacheStore: HistoryStore { range: core::ops::Range, ) -> crate::Result; - /// Insert the leaves for the given key. - fn insert_leaves + Debug>( - &self, - key: K, - leaves: &[(u32, Vec)], - ) -> crate::Result<()>; - /// The last deposit info is sent to the client on leaf request /// So they can verify when the last transaction was sent to maintain - /// their own state of mixers. + /// their own state of vanchors. fn get_last_deposit_block_number + Debug>( &self, key: K, ) -> crate::Result; - /// Set the last deposit block number for the given key. - fn insert_last_deposit_block_number + Debug>( + /// Insert leaves and last deposit block number for the given key. + fn insert_leaves_and_last_deposit_block_number< + K: Into + Debug + Clone, + >( &self, key: K, + leaves: &[(u32, Vec)], block_number: u64, - ) -> crate::Result; + ) -> crate::Result<()>; } /// An Encrypted Output Cache Store is a simple trait that would help in @@ -302,16 +323,9 @@ pub trait EncryptedOutputCacheStore: HistoryStore { range: core::ops::Range, ) -> crate::Result; - /// Insert the encrypted output for the given key. - fn insert_encrypted_output + Debug>( - &self, - key: K, - encrypted_output: &[(u32, Vec)], - ) -> crate::Result<()>; - /// The last deposit info is sent to the client on encrypted_output request /// So they can verify when the last transaction was sent to maintain - /// their own state of mixers. + /// their own state of vanchors. fn get_last_deposit_block_number_for_encrypted_output< K: Into + Debug, >( @@ -319,14 +333,15 @@ pub trait EncryptedOutputCacheStore: HistoryStore { key: K, ) -> crate::Result; - /// Set the last deposit block number for the given key. - fn insert_last_deposit_block_number_for_encrypted_output< - K: Into + Debug, + /// Insert encrypted output and last deposit block number for the given key. + fn insert_encrypted_output_and_last_deposit_block_number< + K: Into + Debug + Clone, >( &self, key: K, + encrypted_output: &[(u32, Vec)], block_number: u64, - ) -> crate::Result; + ) -> crate::Result<()>; } /// A Command sent to the Bridge to execute different actions. @@ -413,15 +428,24 @@ where S::remove_item(self, key) } } -/// ProposalStore is a simple trait for inserting and removing proposals. -pub trait ProposalStore { - /// The type of the Proposal. - type Proposal: Serialize + DeserializeOwned; - /// Insert a proposal into the store. - fn insert_proposal(&self, proposal: Self::Proposal) -> crate::Result<()>; - /// Remove a proposal from the store. - fn remove_proposal( + +/// A trait for Cached Token Price. +pub trait TokenPriceCacheStore +where + CachedTokenPrice: Serialize + DeserializeOwned, +{ + /// Get the cached token price for the given token key. + /// If the token is not found, it will return `None`. + fn get_price( &self, - data_hash: &[u8], - ) -> crate::Result>; + token_key: &str, + ) -> crate::Result>; + /// Insert the cached token price for the given token key. + /// + /// **Note**: this will override the previous value. + fn insert_price( + &self, + token_key: &str, + value: CachedTokenPrice, + ) -> crate::Result<()>; } diff --git a/crates/relayer-store/src/mem.rs b/crates/relayer-store/src/mem.rs index 991b8d9d9..713aa848b 100644 --- a/crates/relayer-store/src/mem.rs +++ b/crates/relayer-store/src/mem.rs @@ -12,25 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Debug; use std::sync::Arc; use parking_lot::RwLock; use webb::evm::ethers::types; +use crate::TokenPriceCacheStore; + use super::{ EncryptedOutputCacheStore, HistoryStore, HistoryStoreKey, LeafCacheStore, }; type MemStore = HashMap>; type MemStoreForVec = HashMap>>; +type MemStoreForMap = HashMap>; /// InMemoryStore is a store that stores the history of events in memory. #[derive(Clone, Default)] pub struct InMemoryStore { _store: Arc>, - store_for_vec: Arc>, + leaf_store: Arc>, + encrypted_output_store: Arc>, last_block_numbers: Arc>>, + target_block_numbers: Arc>>, + last_deposit_block_numbers: Arc>>, + encrypted_output_last_deposit_block_numbers: + Arc>>, + token_prices_cache: Arc>>>, } impl std::fmt::Debug for InMemoryStore { @@ -73,17 +82,44 @@ impl HistoryStore for InMemoryStore { ) -> crate::Result { self.get_last_block_number(key, 1u64) } + + #[tracing::instrument(skip(self))] + fn set_target_block_number + Debug>( + &self, + key: K, + block_number: u64, + ) -> crate::Result { + let mut guard = self.target_block_numbers.write(); + let val = guard.entry(key.into()).or_insert(block_number); + let old = *val; + *val = block_number; + Ok(old) + } + + #[tracing::instrument(skip(self))] + fn get_target_block_number + Debug>( + &self, + key: K, + default_block_number: u64, + ) -> crate::Result { + let guard = self.target_block_numbers.read(); + let val = guard + .get(&key.into()) + .cloned() + .unwrap_or(default_block_number); + Ok(val) + } } impl LeafCacheStore for InMemoryStore { - type Output = Vec>; + type Output = BTreeMap; #[tracing::instrument(skip(self))] fn clear_leaves_cache + Debug>( &self, key: K, ) -> crate::Result<()> { - let mut guard = self.store_for_vec.write(); + let mut guard = self.leaf_store.write(); guard.clear(); Ok(()) } @@ -93,7 +129,7 @@ impl LeafCacheStore for InMemoryStore { &self, key: K, ) -> crate::Result { - let guard = self.store_for_vec.read(); + let guard = self.leaf_store.read(); let val = guard.get(&key.into()).cloned().unwrap_or_default(); Ok(val) } @@ -103,7 +139,7 @@ impl LeafCacheStore for InMemoryStore { key: K, range: core::ops::Range, ) -> crate::Result { - let guard = self.store_for_vec.read(); + let guard = self.leaf_store.read(); let val = guard.get(&key.into()).cloned().unwrap_or_default(); let iter = val .into_iter() @@ -112,39 +148,54 @@ impl LeafCacheStore for InMemoryStore { Ok(iter.collect()) } - #[tracing::instrument(skip(self))] - fn insert_leaves + Debug>( - &self, - key: K, - leaves: &[(u32, Vec)], - ) -> crate::Result<()> { - let mut guard = self.store_for_vec.write(); - guard - .entry(key.into()) - .and_modify(|v| { - for (index, leaf) in leaves { - v.insert(*index as usize, leaf.clone()); - } - }) - .or_insert_with(|| leaves.iter().map(|v| v.1.clone()).collect()); - Ok(()) - } - #[tracing::instrument(skip(self))] fn get_last_deposit_block_number + Debug>( &self, key: K, ) -> crate::Result { - Ok(0u64) + let guard = self.last_deposit_block_numbers.read(); + let default_block_number = 0u64; + let val = guard + .get(&key.into()) + .cloned() + .unwrap_or(default_block_number); + Ok(val) } #[tracing::instrument(skip(self))] - fn insert_last_deposit_block_number + Debug>( + fn insert_leaves_and_last_deposit_block_number< + K: Into + Debug + Clone, + >( &self, key: K, + leaves: &[(u32, Vec)], block_number: u64, - ) -> crate::Result { - Ok(0u64) + ) -> crate::Result<()> { + let mut guard1 = self.leaf_store.write(); + let mut guard2 = self.last_deposit_block_numbers.write(); + let mut guard3 = self.last_block_numbers.write(); + { + // 1. Insert leaves + guard1 + .entry(key.clone().into()) + .and_modify(|v| { + for (index, leaf) in leaves { + v.insert(*index, types::H256::from_slice(leaf)); + } + }) + .or_insert_with(|| { + let mut map = BTreeMap::new(); + for (index, leaf) in leaves { + map.insert(*index, types::H256::from_slice(leaf)); + } + map + }); + // 2. Insert last deposit block number + guard2.insert(key.clone().into(), block_number); + // 3. Insert last block number + guard3.entry(key.into()).or_insert(block_number); + } + Ok(()) } } @@ -156,7 +207,7 @@ impl EncryptedOutputCacheStore for InMemoryStore { &self, key: K, ) -> crate::Result { - let guard = self.store_for_vec.read(); + let guard = self.encrypted_output_store.read(); let val = guard.get(&key.into()).cloned().unwrap_or_default(); Ok(val) } @@ -167,7 +218,7 @@ impl EncryptedOutputCacheStore for InMemoryStore { key: K, range: core::ops::Range, ) -> crate::Result { - let guard = self.store_for_vec.read(); + let guard = self.encrypted_output_store.read(); let val = guard.get(&key.into()).cloned().unwrap_or_default(); let iter = val .into_iter() @@ -176,26 +227,6 @@ impl EncryptedOutputCacheStore for InMemoryStore { Ok(iter.collect()) } - #[tracing::instrument(skip(self))] - fn insert_encrypted_output + Debug>( - &self, - key: K, - encrypted_outputs: &[(u32, Vec)], - ) -> crate::Result<()> { - let mut guard = self.store_for_vec.write(); - guard - .entry(key.into()) - .and_modify(|v| { - for (index, encrypted_output) in encrypted_outputs { - v.insert(*index as usize, encrypted_output.clone()); - } - }) - .or_insert_with(|| { - encrypted_outputs.iter().map(|v| v.1.clone()).collect() - }); - Ok(()) - } - #[tracing::instrument(skip(self))] fn get_last_deposit_block_number_for_encrypted_output< K: Into + Debug, @@ -203,18 +234,60 @@ impl EncryptedOutputCacheStore for InMemoryStore { &self, key: K, ) -> crate::Result { - Ok(0u64) + let guard = self.encrypted_output_last_deposit_block_numbers.read(); + let default_block_number = 0u64; + let val = guard + .get(&key.into()) + .cloned() + .unwrap_or(default_block_number); + Ok(val) } #[tracing::instrument(skip(self))] - fn insert_last_deposit_block_number_for_encrypted_output< - K: Into + Debug, + fn insert_encrypted_output_and_last_deposit_block_number< + K: Into + Debug + Clone, >( &self, key: K, + encrypted_outputs: &[(u32, Vec)], block_number: u64, - ) -> crate::Result { - Ok(0u64) + ) -> crate::Result<()> { + let mut guard1 = self.encrypted_output_store.write(); + let mut guard2 = self.last_deposit_block_numbers.write(); + { + guard1 + .entry(key.clone().into()) + .and_modify(|v| { + for (index, encrypted_output) in encrypted_outputs { + v.insert(*index as usize, encrypted_output.clone()); + } + }) + .or_insert_with(|| { + encrypted_outputs.iter().map(|v| v.1.clone()).collect() + }); + guard2.insert(key.into(), block_number); + } + Ok(()) + } +} + +impl TokenPriceCacheStore for InMemoryStore +where + T: serde::Serialize + serde::de::DeserializeOwned + Clone + Debug, +{ + fn get_price(&self, token: &str) -> crate::Result> { + self.token_prices_cache + .read() + .get(token) + .map(|v| serde_json::from_slice(v)) + .transpose() + .map_err(Into::into) + } + + fn insert_price(&self, token: &str, value: T) -> crate::Result<()> { + let v = serde_json::to_vec(&value)?; + self.token_prices_cache.write().insert(token.to_string(), v); + Ok(()) } } @@ -230,10 +303,20 @@ mod tests { .map(|i| (i, types::H256::random().to_fixed_bytes().to_vec())) .collect::>(); let key = HistoryStoreKey::from(1u32); - store.insert_leaves(key, &generated_leaves).unwrap(); + let block_number = 20u64; + store + .insert_leaves_and_last_deposit_block_number( + key, + &generated_leaves, + block_number, + ) + .unwrap(); let leaves = store.get_leaves(key).unwrap(); assert_eq!( - leaves, + leaves + .into_iter() + .map(|v| v.1.to_fixed_bytes().to_vec()) + .collect::>(), generated_leaves .iter() .map(|v| v.1.clone()) @@ -249,10 +332,20 @@ mod tests { .map(|i| (i, types::H256::random().to_fixed_bytes().to_vec())) .collect::>(); let key = HistoryStoreKey::from(1u32); - store.insert_leaves(key, &generated_leaves).unwrap(); + let block_number = 20u64; + store + .insert_leaves_and_last_deposit_block_number( + key, + &generated_leaves, + block_number, + ) + .unwrap(); let leaves = store.get_leaves_with_range(key, 5..10).unwrap(); assert_eq!( - leaves, + leaves + .into_iter() + .map(|v| v.1.to_fixed_bytes().to_vec()) + .collect::>(), generated_leaves .iter() .skip(5) @@ -261,4 +354,36 @@ mod tests { .collect::>() ); } + + #[test] + fn insert_leaves_and_last_deposit_block_number() { + // a simple test to show that we can get all the leaves that we inserted. + let store = InMemoryStore::default(); + let generated_leaves = (0..20u32) + .map(|i| (i, types::H256::random().to_fixed_bytes().to_vec())) + .collect::>(); + let key = HistoryStoreKey::from(1u32); + let block_number = 20u64; + store + .insert_leaves_and_last_deposit_block_number( + key, + &generated_leaves, + block_number, + ) + .unwrap(); + let last_deposit_block_number = + store.get_last_deposit_block_number(key).unwrap(); + assert_eq!(last_deposit_block_number, block_number); + let leaves = store.get_leaves(key).unwrap(); + assert!(leaves + .into_iter() + .map(|v| v.1.to_fixed_bytes().to_vec()) + .collect::>() + .iter() + .eq(generated_leaves + .iter() + .map(|v| v.1.clone()) + .collect::>() + .iter())); + } } diff --git a/crates/relayer-store/src/sled.rs b/crates/relayer-store/src/sled.rs index 9af416b69..24642ff3c 100644 --- a/crates/relayer-store/src/sled.rs +++ b/crates/relayer-store/src/sled.rs @@ -12,19 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::HistoryStoreKey; +use super::{ + EncryptedOutputCacheStore, EventHashStore, HistoryStore, LeafCacheStore, + QueueStore, TokenPriceCacheStore, +}; use crate::{BridgeKey, QueueKey}; use core::fmt; use serde::de::DeserializeOwned; use serde::Serialize; +use sled::Transactional; +use std::collections::BTreeMap; use std::fmt::Debug; use std::path::Path; -use webb::evm::ethers; - -use super::HistoryStoreKey; -use super::{ - EncryptedOutputCacheStore, EventHashStore, HistoryStore, LeafCacheStore, - ProposalStore, QueueStore, -}; +use webb::evm::ethers::{self, types}; /// SledStore is a store that stores the history of events in a [Sled](https://sled.rs)-based database. #[derive(Clone)] pub struct SledStore { @@ -98,10 +99,49 @@ impl HistoryStore for SledStore { None => Ok(default_block_number), } } + + #[tracing::instrument(skip(self))] + fn set_target_block_number + Debug>( + &self, + key: K, + block_number: u64, + ) -> crate::Result { + let tree = self.db.open_tree("target_block_numbers")?; + let bytes = block_number.to_le_bytes(); + let key: HistoryStoreKey = key.into(); + let old = tree.insert(key.to_bytes(), &bytes)?; + match old { + Some(v) => { + let mut output = [0u8; 8]; + output.copy_from_slice(&v); + Ok(u64::from_be_bytes(output)) + } + None => Ok(block_number), + } + } + + #[tracing::instrument(skip(self))] + fn get_target_block_number + Debug>( + &self, + key: K, + default_block_number: u64, + ) -> crate::Result { + let tree = self.db.open_tree("target_block_numbers")?; + let key: HistoryStoreKey = key.into(); + let val = tree.get(key.to_bytes())?; + match val { + Some(v) => { + let mut output = [0u8; 8]; + output.copy_from_slice(&v); + Ok(u64::from_le_bytes(output)) + } + None => Ok(default_block_number), + } + } } impl LeafCacheStore for SledStore { - type Output = Vec>; + type Output = BTreeMap; #[tracing::instrument(skip(self))] fn clear_leaves_cache + Debug>( @@ -128,9 +168,20 @@ impl LeafCacheStore for SledStore { key.chain_id(), key.address() ))?; - let leaves = - tree.iter().values().flatten().map(|v| v.to_vec()).collect(); - Ok(leaves) + let leaves_map: BTreeMap<_, _> = tree + .iter() + .flatten() + .map(|(k, v)| { + let leaf_index_bytes = k.get(0..4).expect("leaf index bytes"); + let leaf_index_bytes = leaf_index_bytes + .try_into() + .expect("leaf index bytes is u32 bytes"); + let leaf_index = u32::from_le_bytes(leaf_index_bytes); + let leaf = types::H256::from_slice(&v); + (leaf_index, leaf) + }) + .collect(); + Ok(leaves_map) } #[tracing::instrument(skip(self))] @@ -149,29 +200,19 @@ impl LeafCacheStore for SledStore { let range_end = range.end.to_le_bytes(); let leaves = tree .range(range_start..range_end) - .values() .flatten() - .map(|v| v.to_vec()) + .map(|(k, v)| { + let leaf_index_bytes = k.get(0..4).expect("leaf index bytes"); + let leaf_index_bytes = leaf_index_bytes + .try_into() + .expect("leaf index bytes is u32 bytes"); + let leaf_index = u32::from_le_bytes(leaf_index_bytes); + let leaf = types::H256::from_slice(&v); + (leaf_index, leaf) + }) .collect(); Ok(leaves) } - #[tracing::instrument(skip(self))] - fn insert_leaves + Debug>( - &self, - key: K, - leaves: &[(u32, Vec)], - ) -> crate::Result<()> { - let key: HistoryStoreKey = key.into(); - let tree = self.db.open_tree(format!( - "leaves/{}/{}", - key.chain_id(), - key.address() - ))?; - for (k, v) in leaves { - tree.insert(k.to_le_bytes(), v.as_slice())?; - } - Ok(()) - } #[tracing::instrument(skip(self))] fn get_last_deposit_block_number + Debug>( @@ -192,23 +233,38 @@ impl LeafCacheStore for SledStore { } #[tracing::instrument(skip(self))] - fn insert_last_deposit_block_number + Debug>( + fn insert_leaves_and_last_deposit_block_number< + K: Into + Debug, + >( &self, key: K, + leaves: &[(u32, Vec)], block_number: u64, - ) -> crate::Result { - let tree = self.db.open_tree("last_deposit_block_number")?; - let bytes = block_number.to_le_bytes(); + ) -> crate::Result<()> { let key: HistoryStoreKey = key.into(); - let old = tree.insert(key.to_bytes(), &bytes)?; - match old { - Some(v) => { - let mut output = [0u8; 8]; - output.copy_from_slice(&v); - Ok(u64::from_le_bytes(output)) - } - None => Ok(block_number), - } + + let leaf_tree = self.db.open_tree(format!( + "leaves/{}/{}", + key.chain_id(), + key.address() + ))?; + // This is last deposit event block number + let set_block_tree1 = self.db.open_tree("last_deposit_block_number")?; + // This will be used by event watcher to track the block number has been processed + let set_block_tree2 = self.db.open_tree("last_block_numbers")?; + let block_number_bytes = block_number.to_le_bytes(); + + (&leaf_tree, &set_block_tree1, &set_block_tree2).transaction( + |(leaf_tree, set_block_tree1, set_block_tree2)| { + for (k, v) in leaves { + leaf_tree.insert(&k.to_le_bytes(), v.as_slice())?; + } + set_block_tree1.insert(key.to_bytes(), &block_number_bytes)?; + set_block_tree2.insert(key.to_bytes(), &block_number_bytes)?; + Ok(()) + }, + )?; + Ok(()) } } @@ -254,32 +310,15 @@ impl EncryptedOutputCacheStore for SledStore { Ok(encrypted_outputs) } - #[tracing::instrument(skip(self))] - fn insert_encrypted_output + Debug>( - &self, - key: K, - encrypted_output: &[(u32, Vec)], - ) -> crate::Result<()> { - let key: HistoryStoreKey = key.into(); - - let tree = self.db.open_tree(format!( - "encrypted_outputs/{}/{}", - key.chain_id(), - key.address() - ))?; - for (k, v) in encrypted_output { - tree.insert(k.to_le_bytes(), &*v.clone())?; - } - Ok(()) - } - fn get_last_deposit_block_number_for_encrypted_output< K: Into + Debug, >( &self, key: K, ) -> crate::Result { - let tree = self.db.open_tree("last_deposit_block_number")?; + let tree = self + .db + .open_tree("encrypted_output_last_deposit_block_number")?; let key: HistoryStoreKey = key.into(); let val = tree.get(key.to_bytes())?; match val { @@ -292,25 +331,37 @@ impl EncryptedOutputCacheStore for SledStore { } } - fn insert_last_deposit_block_number_for_encrypted_output< - K: Into + Debug, + #[tracing::instrument(skip(self))] + fn insert_encrypted_output_and_last_deposit_block_number< + K: Into + Debug + Clone, >( &self, key: K, + encrypted_output: &[(u32, Vec)], block_number: u64, - ) -> crate::Result { - let tree = self.db.open_tree("last_deposit_block_number")?; - let bytes = block_number.to_le_bytes(); + ) -> crate::Result<()> { let key: HistoryStoreKey = key.into(); - let old = tree.insert(key.to_bytes(), &bytes)?; - match old { - Some(v) => { - let mut output = [0u8; 8]; - output.copy_from_slice(&v); - Ok(u64::from_be_bytes(output)) - } - None => Ok(block_number), - } + + let encrypted_output_tree = self.db.open_tree(format!( + "encrypted_outputs/{}/{}", + key.chain_id(), + key.address() + ))?; + let set_block_tree = self + .db + .open_tree("encrypted_output_last_deposit_block_number")?; + let block_number_bytes = block_number.to_le_bytes(); + (&encrypted_output_tree, &set_block_tree).transaction( + |(encrypted_output_tree, set_block_tree)| { + for (k, v) in encrypted_output { + encrypted_output_tree + .insert(&k.to_le_bytes(), v.as_slice())?; + } + set_block_tree.insert(key.to_bytes(), &block_number_bytes)?; + Ok(()) + }, + )?; + Ok(()) } } @@ -575,37 +626,25 @@ where } } -impl ProposalStore for SledStore { - type Proposal = (); - - #[tracing::instrument(skip_all)] - fn insert_proposal(&self, proposal: Self::Proposal) -> crate::Result<()> { - let tree = self.db.open_tree("proposal_store")?; - tree.insert("TODO", serde_json::to_vec(&proposal)?.as_slice())?; - Ok(()) - } - - #[tracing::instrument( - skip_all, - fields(data_hash = %hex::encode(data_hash)) - )] - fn remove_proposal( - &self, - data_hash: &[u8], - ) -> crate::Result> { - let tree = self.db.open_tree("proposal_store")?; - match tree.get(data_hash)? { +impl TokenPriceCacheStore for SledStore +where + T: Serialize + DeserializeOwned, +{ + fn get_price(&self, token: &str) -> crate::Result> { + let tree = self.db.open_tree("token_prices")?; + match tree.get(token)? { Some(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)), - None => { - tracing::warn!( - "Proposal not seen yet; not found in the proposal storage." - ); - Ok(None) - } + None => Ok(None), } } -} + fn insert_price(&self, token: &str, value: T) -> crate::Result<()> { + let v = serde_json::to_vec(&value)?; + let tree = self.db.open_tree("token_prices")?; + tree.insert(token, v.as_slice())?; + Ok(()) + } +} #[cfg(test)] mod tests { use super::*; @@ -667,6 +706,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let store = SledStore::open(tmp.path()).unwrap(); let chain_id = 1u32; + let block_number = 20u64; let contract = types::H160::from_slice("11111111111111111111".as_bytes()); let history_store_key = ( @@ -677,14 +717,21 @@ mod tests { .map(|i| (i, types::H256::random().to_fixed_bytes().to_vec())) .collect::>(); store - .insert_leaves(history_store_key, &generated_leaves) + .insert_leaves_and_last_deposit_block_number( + history_store_key, + &generated_leaves, + block_number, + ) .unwrap(); let leaves = store .get_leaves_with_range(history_store_key, 5..10) .unwrap(); assert_eq!(leaves.len(), 5); assert_eq!( - leaves, + leaves + .values() + .map(|v| v.to_fixed_bytes().to_vec()) + .collect::>(), generated_leaves .into_iter() .skip(5) @@ -812,4 +859,55 @@ mod tests { store.delete_event(&event_bytes).unwrap(); } } + + #[test] + fn insert_leaves_and_last_deposit_block_number_should_work() { + let tmp = tempfile::tempdir().unwrap(); + let store = SledStore::open(tmp.path()).unwrap(); + let chain_id = 1u32; + let contract = + types::H160::from_slice("11111111111111111111".as_bytes()); + let history_store_key = ( + TypedChainId::Evm(chain_id), + TargetSystem::new_contract_address(contract.to_fixed_bytes()), + ); + let generated_leaves = (0..2u32) + .map(|i| (i, types::H256::random().to_fixed_bytes().to_vec())) + .collect::>(); + let block_number = 20u64; + let default_block_number = 1u64; + store + .insert_leaves_and_last_deposit_block_number( + history_store_key, + &generated_leaves, + block_number, + ) + .unwrap(); + let leaves = store + .get_leaves_with_range(history_store_key, 0..2) + .unwrap(); + assert_eq!(leaves.len(), 2); + assert_eq!( + leaves + .values() + .map(|v| v.to_fixed_bytes().to_vec()) + .collect::>(), + generated_leaves + .into_iter() + .map(|(_, v)| v) + .collect::>() + ); + assert_eq!( + store + .get_last_deposit_block_number(history_store_key) + .unwrap(), + block_number + ); + assert_eq!( + store + .get_last_block_number(history_store_key, default_block_number) + .unwrap(), + block_number + ); + } } diff --git a/crates/relayer-types/Cargo.toml b/crates/relayer-types/Cargo.toml index 11cc517c7..32a65d845 100644 --- a/crates/relayer-types/Cargo.toml +++ b/crates/relayer-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-types" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/crates/relayer-types/src/dynamic_payload.rs b/crates/relayer-types/src/dynamic_payload.rs deleted file mode 100644 index f7b0f65a5..000000000 --- a/crates/relayer-types/src/dynamic_payload.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2022 Webb Technologies Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use serde::{Deserialize, Serialize}; -use std::borrow::Cow; -use webb::substrate::subxt::dynamic::Value; - -/// This represents a intermediary type to store `DynamicTxPayload`. -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct WebbDynamicTxPayload<'a> { - /// Pallet name - pub pallet_name: Cow<'a, str>, - /// Extrinsic name - pub call_name: Cow<'a, str>, - /// Extrinsic params - pub fields: Vec>, -} diff --git a/crates/relayer-types/src/etherscan_api.rs b/crates/relayer-types/src/etherscan_api.rs new file mode 100644 index 000000000..2f2973248 --- /dev/null +++ b/crates/relayer-types/src/etherscan_api.rs @@ -0,0 +1,66 @@ +use serde::{Deserialize, Serialize}; +#[derive(Clone, Serialize)] +pub struct EtherscanApiKey(String); + +impl std::fmt::Debug for EtherscanApiKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("EtherscanApiKey").finish() + } +} + +impl From for EtherscanApiKey { + fn from(api_key: String) -> Self { + EtherscanApiKey(api_key) + } +} + +impl std::ops::Deref for EtherscanApiKey { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'de> Deserialize<'de> for EtherscanApiKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct EtherscanApiKeyVisitor; + impl<'de> serde::de::Visitor<'de> for EtherscanApiKeyVisitor { + type Value = String; + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + formatter.write_str( + "Etherscan api key or an env var containing a etherscan api key in it", + ) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if value.starts_with('$') { + // env + let var = value.strip_prefix('$').unwrap_or(value); + tracing::trace!("Reading {} from env", var); + let val = std::env::var(var).map_err(|e| { + serde::de::Error::custom(format!( + "error while loading this env {var}: {e}", + )) + })?; + return Ok(val); + } + Ok(value.to_string()) + } + } + + let etherscan_api_key = + deserializer.deserialize_str(EtherscanApiKeyVisitor)?; + Ok(Self(etherscan_api_key)) + } +} diff --git a/crates/relayer-types/src/lib.rs b/crates/relayer-types/src/lib.rs index 7f85bee5c..d91751840 100644 --- a/crates/relayer-types/src/lib.rs +++ b/crates/relayer-types/src/lib.rs @@ -1,4 +1,4 @@ -pub mod dynamic_payload; +pub mod etherscan_api; pub mod mnemonic; pub mod private_key; pub mod rpc_url; diff --git a/crates/relayer-types/src/mod.rs b/crates/relayer-types/src/mod.rs deleted file mode 100644 index 76f85f186..000000000 --- a/crates/relayer-types/src/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// A custom type to support `CWChainId` deserialization from environment variables. -pub mod cw_chain_id; -/// A custom type to support `DynamicTxPayload` -pub mod dynamic_payload; -/// A custom type to support `Mnemonic` deserialization from environment variables. -pub mod mnemonic; -/// A custom type to support `PrivateKey` deserialization from environment variables. -pub mod private_key; -/// A custom type to support `RpcUrl` deserialization from environment variables. -pub mod rpc_url; -/// A custom type to support `Suri` deserialization from environment variables. -pub mod suri; diff --git a/crates/relayer-types/src/suri.rs b/crates/relayer-types/src/suri.rs index 435113cc6..c3a0ff74d 100644 --- a/crates/relayer-types/src/suri.rs +++ b/crates/relayer-types/src/suri.rs @@ -4,7 +4,7 @@ use sp_core::Pair; /// [`Substrate Uri`](https://polkadot.js.org/docs/keyring/start/suri/) #[derive(Clone)] -pub struct Suri(Sr25519Pair); +pub struct Suri(pub Sr25519Pair); impl std::fmt::Debug for Suri { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/relayer-utils/Cargo.toml b/crates/relayer-utils/Cargo.toml index 3e4bf3111..99e9a0e3b 100644 --- a/crates/relayer-utils/Cargo.toml +++ b/crates/relayer-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-utils" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -10,12 +10,20 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dev-dependencies] +tokio = { workspace = true } + [dependencies] +async-trait = { workspace = true } +futures = { workspace = true } +thiserror = { workspace = true } hex = { workspace = true } backoff = { workspace = true } serde_path_to_error = { workspace = true } webb-proposals = { workspace = true } webb = { workspace = true } +serde = { workspace = true } +serde_bytes = { workspace = true } # Used by ethers (but we need it to be vendored with the lib). native-tls = { workspace = true, optional = true } glob = { workspace = true } @@ -25,11 +33,10 @@ url = { workspace = true } serde_json = { workspace = true } axum = { workspace = true } config = { workspace = true } +reqwest = { workspace = true } ark-std = { version = "^0.3.0", default-features = false } derive_more = { version = "0.99", default-features = false, features = ["display"] } prometheus = "0.13.3" -thiserror = "^1" -reqwest = "0.11" hyper = "0.14.24" [features] diff --git a/crates/relayer-utils/src/lib.rs b/crates/relayer-utils/src/lib.rs index be50f9d46..5dd89bbc7 100644 --- a/crates/relayer-utils/src/lib.rs +++ b/crates/relayer-utils/src/lib.rs @@ -12,17 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::sync::Arc; + +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use multi_provider::MultiProvider; use webb::{evm::ethers, substrate::subxt}; +use webb_proposals::ResourceId; pub mod clickable_link; /// Metrics functionality pub mod metric; +/// Multi provider for ethers. +pub mod multi_provider; /// A module used for debugging relayer lifecycle, sync state, or other relayer state. pub mod probe; /// Retry functionality pub mod retry; +/// type-erased StaticTxPayload for Substrate Transaction queue. +pub mod static_tx_payload; +type RetryClientProvider = ethers::providers::Provider< + ethers::providers::RetryClient>, +>; /// An enum of all possible errors that could be encountered during the execution of the Webb /// Relayer. #[derive(Debug, thiserror::Error)] @@ -68,7 +81,7 @@ pub enum Error { EthersProvider(#[from] ethers::providers::ProviderError), /// Smart contract error. #[error(transparent)] - EthersContract( + EthersContractCall( #[from] ethers::contract::ContractError< ethers::providers::Provider, @@ -76,7 +89,7 @@ pub enum Error { ), /// Smart contract error. #[error(transparent)] - EthersContract2( + EthersContractCallWithSigner( #[from] ethers::contract::ContractError< ethers::middleware::SignerMiddleware< @@ -85,6 +98,50 @@ pub enum Error { >, >, ), + + /// Smart contract error. + #[error(transparent)] + EthersContractCallWithRetry( + #[from] ethers::contract::ContractError, + ), + /// Smart contract error. + #[error(transparent)] + EthersContractCallWithRetryCloneable( + #[from] ethers::contract::ContractError>, + ), + /// Ethers Timelag provider error. + #[error(transparent)] + EthersTimelagRetryClientError( + #[from] ethers::middleware::timelag::TimeLagError, + ), + + /// Ethers Timelag provider error. + #[error(transparent)] + EthersTimelagRetryClientClonableError( + #[from] + ethers::middleware::timelag::TimeLagError< + std::sync::Arc, + >, + ), + + /// Ether contract error for TimeLag provider. + #[error(transparent)] + EthersContractCallWithTimeLagRetryClient( + #[from] + ethers::contract::ContractError< + ethers::middleware::timelag::TimeLag, + >, + ), + + /// Ether contract error for TimeLag provider. + #[error(transparent)] + EthersContractCallWithTimeLagRetryClientCloneable( + #[from] + ethers::contract::ContractError< + ethers::middleware::timelag::TimeLag>, + >, + ), + /// SCALE Codec error. #[error(transparent)] ScaleCodec(#[from] webb::substrate::scale::Error), @@ -102,12 +159,19 @@ pub enum Error { /// Etherscan API error #[error(transparent)] Etherscan(#[from] ethers::etherscan::errors::EtherscanError), + #[error(transparent)] + GasOracle(#[from] ethers::middleware::gas_oracle::GasOracleError), + /// Ether wallet errors. + #[error(transparent)] + EtherWalletError(#[from] ethers::signers::WalletError), /// Ethers currency conversion error #[error(transparent)] Conversion(#[from] ethers::utils::ConversionError), /// Failed to convert string to float #[error(transparent)] ParseFloatError(#[from] std::num::ParseFloatError), + #[error(transparent)] + PrometheusError(#[from] prometheus::Error), /// Generic error. #[error("{}", _0)] Generic(&'static str), @@ -150,6 +214,31 @@ pub enum Error { /// Arkworks Errors. #[error("{}", _0)] ArkworksError(String), + /// Etherscan api configuration not found. + #[error("Etherscan api configuration not found for chain : {}", chain_id)] + EtherscanConfigNotFound { + /// The chain id of the node. + chain_id: String, + }, + #[error("No bridge registered with DKG for resource id {:?}", _0)] + BridgeNotRegistered(ResourceId), + #[error("Failed to fetch token price for token: {token}")] + FetchTokenPriceError { token: String }, + #[error("Failed to read a value from substrate storage")] + ReadSubstrateStorageError, + #[error("Cannot convert default leaf scalar into bytes")] + ConvertLeafScalarError, + /// Invalid Merkle root + #[error("Invalid Merkle root at index {}", _0)] + InvalidMerkleRootError(u32), + /// Missing Static Transaction Validation Details + /// This error is raised when the static transaction validation details + /// are missing. + #[error("Missing Substrate Static Transaction Validation Details")] + MissingValidationDetails, + /// Provider not found error. + #[error("Provider not found for index {0}")] + ProviderNotFound(usize), } /// A type alias for the result for webb relayer, that uses the `Error` enum. @@ -160,3 +249,23 @@ impl From> for Error { Error::ArkworksError(format!("{}", error)) } } + +impl From for HandlerError { + fn from(value: Error) -> Self { + HandlerError(StatusCode::INTERNAL_SERVER_ERROR, value.to_string()) + } +} + +/// Error type for HTTP handlers +pub struct HandlerError( + /// HTTP status code for response + pub StatusCode, + /// Response message + pub String, +); + +impl IntoResponse for HandlerError { + fn into_response(self) -> Response { + (self.0, self.1).into_response() + } +} diff --git a/crates/relayer-utils/src/metric.rs b/crates/relayer-utils/src/metric.rs index f7b21f37d..9a82dcafd 100644 --- a/crates/relayer-utils/src/metric.rs +++ b/crates/relayer-utils/src/metric.rs @@ -15,18 +15,18 @@ use std::collections::HashMap; use prometheus::core::{AtomicF64, GenericCounter, GenericGauge}; +use prometheus::labels; +use prometheus::opts; use prometheus::{register_counter, register_gauge, Encoder, TextEncoder}; -use webb_proposals::ResourceId; +use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; /// A struct for collecting metrics for particular resource. #[derive(Debug, Clone)] pub struct ResourceMetric { - /// Total gas spent on Resource. + /// Total gas spent (in gwei) on Resource. pub total_gas_spent: GenericCounter, /// Total fees earned on Resource. pub total_fee_earned: GenericCounter, - /// Account Balance - pub account_balance: GenericGauge, } /// A struct definition for collecting metrics in the relayer. @@ -34,8 +34,6 @@ pub struct ResourceMetric { pub struct Metrics { /// Bridge watcher back off metric pub bridge_watcher_back_off: GenericCounter, - /// Total active Relayer metric - pub total_active_relayer: GenericCounter, /// Total transaction made Relayer metric pub total_transaction_made: GenericCounter, /// Anchor update proposals proposed by relayer @@ -61,156 +59,208 @@ pub struct Metrics { /// Total amount of data stored metric pub total_amount_of_data_stored: GenericGauge, /// Resource metric - pub resource_metric_map: HashMap, + resource_metric_map: HashMap, + /// Metric for account balance (in gwei) on specific chain + account_balance: HashMap>, } impl Metrics { /// Instantiates the various metrics and their counters, also creates a registry for the counters and /// registers the counters - pub fn new() -> Self { - let bridge_watcher_back_off_counter = register_counter!( + pub fn new() -> Result { + let bridge_watcher_back_off = register_counter!( "bridge_watcher_back_off", "specifies how many times the bridge watcher backed off" - ); - - let total_active_relayer_counter = register_counter!( - "total_active_relayer", - "The total number of active relayers", - ); + )?; - let total_transaction_made_counter = register_counter!( + let total_transaction_made = register_counter!( "total_transaction_made", "The total number of transaction made", - ); + )?; - let anchor_update_proposals_counter = register_counter!( + let anchor_update_proposals = register_counter!( "anchor_update_proposals", "The total number of anchor update proposal proposed by relayer", - ); + )?; - let proposals_signed_counter = register_counter!( + let proposals_signed = register_counter!( "proposals_signed", "The total number of proposal signed by dkg/mocked backend", - ); + )?; - let proposals_processed_tx_queue_counter = register_counter!( + let proposals_processed_tx_queue = register_counter!( "proposals_processed_tx_queue", "Total number of signed proposals processed by transaction queue", - ); + )?; - let proposals_processed_substrate_tx_queue_counter = register_counter!( + let proposals_processed_substrate_tx_queue = register_counter!( "proposals_processed_substrate_tx_queue", "Total number of signed proposals processed by substrate transaction queue", - ); + )?; - let proposals_processed_evm_tx_queue_counter = register_counter!( + let proposals_processed_evm_tx_queue = register_counter!( "proposals_processed_evm_tx_queue", "Total number of signed proposals processed by evm transaction queue", - ); + )?; - let transaction_queue_back_off_counter = register_counter!( + let transaction_queue_back_off = register_counter!( "transaction_queue_back_off", "How many times the transaction queue backed off", - ); + )?; - let substrate_transaction_queue_back_off_counter = register_counter!( + let substrate_transaction_queue_back_off = register_counter!( "substrate_transaction_queue_back_off", "How many times the substrate transaction queue backed off", - ); + )?; - let evm_transaction_queue_back_off_counter = register_counter!( + let evm_transaction_queue_back_off = register_counter!( "evm_transaction_queue_back_off", "How many times the evm transaction queue backed off", - ); + )?; - let total_fee_earned_counter = register_counter!( + let total_fee_earned = register_counter!( "total_fee_earned", "The total number of fees earned", - ); + )?; - let gas_spent_counter = - register_counter!("gas_spent", "The total number of gas spent"); + let gas_spent = + register_counter!("gas_spent", "The total number of gas spent")?; - let total_amount_of_data_stored_counter = register_gauge!( + let total_amount_of_data_stored = register_gauge!( "total_amount_of_data_stored", "The Total number of data stored", - ); + )?; - let resource_metric_map = HashMap::new(); - - Self { - bridge_watcher_back_off: bridge_watcher_back_off_counter.unwrap(), - total_active_relayer: total_active_relayer_counter.unwrap(), - total_transaction_made: total_transaction_made_counter.unwrap(), - anchor_update_proposals: anchor_update_proposals_counter.unwrap(), - proposals_signed: proposals_signed_counter.unwrap(), - proposals_processed_tx_queue: proposals_processed_tx_queue_counter - .unwrap(), - proposals_processed_substrate_tx_queue: - proposals_processed_substrate_tx_queue_counter.unwrap(), - proposals_processed_evm_tx_queue: - proposals_processed_evm_tx_queue_counter.unwrap(), - transaction_queue_back_off: transaction_queue_back_off_counter - .unwrap(), - substrate_transaction_queue_back_off: - substrate_transaction_queue_back_off_counter.unwrap(), - evm_transaction_queue_back_off: - evm_transaction_queue_back_off_counter.unwrap(), - total_fee_earned: total_fee_earned_counter.unwrap(), - gas_spent: gas_spent_counter.unwrap(), - total_amount_of_data_stored: total_amount_of_data_stored_counter - .unwrap(), - resource_metric_map, - } + Ok(Self { + bridge_watcher_back_off, + total_transaction_made, + anchor_update_proposals, + proposals_signed, + proposals_processed_tx_queue, + proposals_processed_substrate_tx_queue, + proposals_processed_evm_tx_queue, + transaction_queue_back_off, + substrate_transaction_queue_back_off, + evm_transaction_queue_back_off, + total_fee_earned, + gas_spent, + total_amount_of_data_stored, + resource_metric_map: Default::default(), + account_balance: Default::default(), + }) } /// Gathers the whole relayer metrics - pub fn gather_metrics() -> String { + pub fn gather_metrics() -> Result { let mut buffer = Vec::new(); let encoder = TextEncoder::new(); // Gather the metrics. let metric_families = prometheus::gather(); // Encode them to send. - encoder.encode(&metric_families, &mut buffer).unwrap(); + encoder.encode(&metric_families, &mut buffer)?; - String::from_utf8(buffer.clone()).unwrap() + Ok(String::from_utf8(buffer.clone())?) + } + + // TODO: move this to webb-proposals + fn chain_name(chain: TypedChainId) -> &'static str { + match chain { + TypedChainId::None => "None", + TypedChainId::Evm(_) => "Evm", + TypedChainId::Substrate(_) => "Substrate", + TypedChainId::PolkadotParachain(_) => "PolkadotParachain", + TypedChainId::KusamaParachain(_) => "KusamaParachain", + TypedChainId::RococoParachain(_) => "RococoParachain", + TypedChainId::Cosmos(_) => "Cosmos", + TypedChainId::Solana(_) => "Solana", + TypedChainId::Ink(_) => "Ink", + } + } + + pub fn resource_metric_entry( + &mut self, + resource_id: ResourceId, + ) -> &mut ResourceMetric { + self.resource_metric_map + .entry(resource_id) + .or_insert_with(|| { + Metrics::register_resource_id_counters(resource_id) + }) + } + + pub fn account_balance_entry( + &mut self, + chain: TypedChainId, + ) -> &mut GenericGauge { + self.account_balance.entry(chain).or_insert_with(|| { + let chain_id = chain.underlying_chain_id().to_string(); + register_gauge!(opts!( + "chain_account_balance", + "Total account balance on chain", + labels!( + "chain_type" => Self::chain_name(chain), + "chain_id" => &chain_id, + ) + )) + .expect("create gauge for account balance") + }) } /// Registers new counters to track metric for individual resources. - pub fn register_resource_id_counters( + fn register_resource_id_counters( resource_id: ResourceId, ) -> ResourceMetric { - let resource_hex = hex::encode(resource_id.to_bytes().as_ref()); - // Total gas fee spent on particular resource. - let total_gas_spent_counter = register_counter!( - format!("resource_{resource_hex}_total_gas_spent"), - format!( - "The total number of gas spent on resource : {resource_hex}" - ) + let chain_id = resource_id + .typed_chain_id() + .underlying_chain_id() + .to_string(); + let (target_system_type, target_system_value) = + match resource_id.target_system() { + TargetSystem::ContractAddress(address) => { + ("contract", hex::encode(address)) + } + TargetSystem::Substrate(system) => ( + "tree_id", + format!( + "{}, pallet_index: {}", + system.tree_id, system.pallet_index + ), + ), + }; + let labels = labels!( + "chain_type" => Self::chain_name(resource_id.typed_chain_id()), + "chain_id" => &chain_id, + "target_system_type" => target_system_type, + "target_system_value" => &target_system_value ); + + // Total gas fee spent on particular resource. + let total_gas_spent = register_counter!(opts!( + "resource_total_gas_spent", + "Total number of gas spent on resource", + labels + )) + .expect("create counter for gas spent"); + // Total fee earned on particular resource. - let total_fee_earned_counter = register_counter!( - format!("resource_{resource_hex}_total_fees_earned"), - format!( - "The total number of fees earned on resource : {resource_hex}" - ) - ); - // Account Balance - let account_balance_counter = register_gauge!( - format!("resource_{resource_hex}_account_balance"), - format!("Total account balance : {resource_hex}") - ); + let total_fee_earned = register_counter!(opts!( + "resource_total_fees_earned", + "Total number of fees earned on resource", + labels + )) + .expect("create counter for fees earned"); ResourceMetric { - total_gas_spent: total_gas_spent_counter.unwrap(), - total_fee_earned: total_fee_earned_counter.unwrap(), - account_balance: account_balance_counter.unwrap(), + total_gas_spent, + total_fee_earned, } } } -impl Default for Metrics { - fn default() -> Self { - Self::new() - } +#[derive(Debug, thiserror::Error)] +pub enum GatherMetricsError { + #[error(transparent)] + PrometheusError(#[from] prometheus::Error), + #[error(transparent)] + FromUtf8Error(#[from] std::string::FromUtf8Error), } diff --git a/crates/relayer-utils/src/multi_provider.rs b/crates/relayer-utils/src/multi_provider.rs new file mode 100644 index 000000000..f5119125b --- /dev/null +++ b/crates/relayer-utils/src/multi_provider.rs @@ -0,0 +1,83 @@ +use crate::Error as WebbRelayerError; +use core::fmt::Debug; +use futures::prelude::*; +use serde::{de::DeserializeOwned, Serialize}; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; +use webb::evm::ethers::providers::{JsonRpcClient, ProviderError}; +/// MultiProvider is a JsonRpcClient that will round-robin requests to the underlying providers. +#[derive(Debug, Clone)] +pub struct MultiProvider

{ + providers: Arc>, + last_used: Arc, +} + +impl

MultiProvider

{ + pub fn new(providers: Arc>) -> Self { + Self { + providers, + last_used: Default::default(), + } + } +} + +#[async_trait::async_trait] +impl JsonRpcClient for MultiProvider

+where + P::Error: Into, +{ + type Error = ProviderError; + + async fn request< + T: Debug + Serialize + Send + Sync, + R: DeserializeOwned + Send, + >( + &self, + method: &str, + params: T, + ) -> Result { + // Fetch the next provider index to use + // incrementing it by 1 and wrapping around if it exceeds the number of providers + let next_provider_idx = self + .last_used + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |last_used| { + Some(last_used.saturating_add(1) % self.providers.len()) + }) + .unwrap_or_default(); + + if let Some(provider) = self.providers.get(next_provider_idx) { + provider + .request(method, params) + .map_err(P::Error::into) + .await + } else { + Err(ProviderError::CustomError( + WebbRelayerError::ProviderNotFound(next_provider_idx) + .to_string(), + )) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + use webb::evm::ethers::providers::{self, Http, Middleware}; + + #[tokio::test] + async fn should_process_request() { + let p1 = Http::from_str("https://eth.llamarpc.com").unwrap(); + let p2 = Http::from_str("https://1rpc.io/eth").unwrap(); + + let multi_provider = MultiProvider::new(vec![p1, p2].into()); + assert_eq!(multi_provider.providers.len(), 2); + assert_eq!(multi_provider.last_used.load(Ordering::SeqCst), 0); + let provider = providers::Provider::new(multi_provider.clone()); + provider.get_block_number().await.expect("should work"); + assert_eq!(multi_provider.last_used.load(Ordering::SeqCst), 1); + provider.get_block_number().await.expect("should work"); + } +} diff --git a/crates/relayer-utils/src/probe.rs b/crates/relayer-utils/src/probe.rs index bb34c3f48..b2191c835 100644 --- a/crates/relayer-utils/src/probe.rs +++ b/crates/relayer-utils/src/probe.rs @@ -46,4 +46,7 @@ pub enum Kind { /// Relayer Encrypted Output Store state on a specific chain/node. #[display(fmt = "encrypted_outputs_store")] EncryptedOutputStore, + /// When the relayer will retry to do something. + #[display(fmt = "retry")] + Retry, } diff --git a/crates/relayer-utils/src/retry.rs b/crates/relayer-utils/src/retry.rs index afc8579e7..1d539e942 100644 --- a/crates/relayer-utils/src/retry.rs +++ b/crates/relayer-utils/src/retry.rs @@ -42,12 +42,10 @@ impl ConstantWithMaxRetryCount { impl Backoff for ConstantWithMaxRetryCount { fn next_backoff(&mut self) -> Option { - if self.count < self.max_retry_count { + (self.count < self.max_retry_count).then(|| { self.count += 1; - Some(self.interval) - } else { - None - } + self.interval + }) } fn reset(&mut self) { diff --git a/crates/relayer-utils/src/static_tx_payload.rs b/crates/relayer-utils/src/static_tx_payload.rs new file mode 100644 index 000000000..31aa86f0e --- /dev/null +++ b/crates/relayer-utils/src/static_tx_payload.rs @@ -0,0 +1,72 @@ +use core::fmt; + +use webb::substrate::{ + scale::Encode, + subxt::tx::{StaticTxPayload, TxPayload}, +}; + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +pub struct TypeErasedStaticTxPayload { + pub pallet_name: String, + pub call_name: String, + #[serde(with = "serde_bytes")] + pub call_data: Vec, +} + +impl std::fmt::Debug for TypeErasedStaticTxPayload { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TypeErasedStaticTxPayload") + .field("pallet_name", &self.pallet_name) + .field("call_name", &self.call_name) + .field("call_data", &hex::encode(&self.call_data)) + .finish() + } +} + +impl fmt::Display for TypeErasedStaticTxPayload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}.{}({})", + self.pallet_name, + self.call_name, + hex::encode(&self.call_data) + ) + } +} + +impl TryFrom> + for TypeErasedStaticTxPayload +{ + type Error = super::Error; + fn try_from( + payload: StaticTxPayload, + ) -> Result { + let details = payload + .validation_details() + .ok_or_else(|| Self::Error::MissingValidationDetails)?; + let call_data = payload.call_data().encode(); + Ok(Self { + pallet_name: details.pallet_name.to_owned(), + call_name: details.call_name.to_owned(), + call_data, + }) + } +} + +impl TxPayload for TypeErasedStaticTxPayload { + fn encode_call_data_to( + &self, + metadata: &webb::substrate::subxt::Metadata, + out: &mut Vec, + ) -> Result<(), webb::substrate::subxt::Error> { + let pallet = metadata.pallet(&self.pallet_name)?; + let pallet_index = pallet.index(); + let call_index = pallet.call_index(&self.call_name)?; + + pallet_index.encode_to(out); + call_index.encode_to(out); + out.extend_from_slice(&self.call_data); + Ok(()) + } +} diff --git a/crates/tx-queue/Cargo.toml b/crates/tx-queue/Cargo.toml index dfcda13fc..fe65ee84b 100644 --- a/crates/tx-queue/Cargo.toml +++ b/crates/tx-queue/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-tx-queue" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -22,21 +22,22 @@ futures = { workspace = true } backoff = { workspace = true } tokio = { workspace = true } webb = { workspace = true } -sp-core = { workspace = true } -sp-runtime = { workspace = true } +sp-core = { workspace = true, optional = true } +sp-runtime = { workspace = true, optional = true } # Used by ethers (but we need it to be vendored with the lib). native-tls = { workspace = true, optional = true } ethereum-types = { workspace = true } -rand = { version = "0.8", default-features = false, features = ["getrandom"] } +rand = { workspace = true, default-features = false, features = ["getrandom"] } [features] default = ["std", "evm", "substrate"] std = [] -evm = [ - "webb-relayer-context/evm", -] -substrate = [ - "webb-relayer-context/substrate", -] +evm = ["webb-relayer-context/evm"] +substrate = ["webb-relayer-context/substrate", "sp-core", "sp-runtime"] + +[dev-dependencies] +webb-relayer-config = { workspace = true } +url = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/crates/tx-queue/src/evm/evm_tx_queue.rs b/crates/tx-queue/src/evm/evm_tx_queue.rs index cda5347f9..7d1fbac40 100644 --- a/crates/tx-queue/src/evm/evm_tx_queue.rs +++ b/crates/tx-queue/src/evm/evm_tx_queue.rs @@ -20,8 +20,10 @@ use futures::TryFutureExt; use rand::Rng; use webb::evm::ethers::core::types::transaction::eip2718::TypedTransaction; use webb::evm::ethers::middleware::SignerMiddleware; +use webb::evm::ethers::prelude::TimeLag; use webb::evm::ethers::providers::Middleware; +use webb::evm::ethers::types; use webb_relayer_context::RelayerContext; use webb_relayer_store::sled::SledQueueKey; use webb_relayer_store::QueueStore; @@ -34,7 +36,7 @@ use webb_relayer_utils::clickable_link::ClickableLink; #[derive(Clone)] pub struct TxQueue> { ctx: RelayerContext, - chain_id: String, + chain_id: types::U256, store: Arc, } @@ -51,7 +53,11 @@ where /// * `ctx` - RelayContext reference that holds the configuration /// * `chain_id` - The chainId that this queue is for /// * `store` - [Sled](https://sled.rs)-based database store - pub fn new(ctx: RelayerContext, chain_id: String, store: Arc) -> Self { + pub fn new( + ctx: RelayerContext, + chain_id: types::U256, + store: Arc, + ) -> Self { Self { ctx, chain_id, @@ -64,14 +70,21 @@ where #[tracing::instrument(skip_all, fields(chain = %self.chain_id))] pub async fn run(self) -> webb_relayer_utils::Result<()> { let provider = self.ctx.evm_provider(&self.chain_id).await?; - let wallet = self.ctx.evm_wallet(&self.chain_id).await?; - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let chain_config = - self.ctx.config.evm.get(&self.chain_id).ok_or_else(|| { - webb_relayer_utils::Error::ChainNotFound { - chain_id: self.chain_id.clone(), - } + let wallet = self.ctx.evm_wallet(self.chain_id).await?; + let signer_client = SignerMiddleware::new(provider, wallet); + + let chain_config = self + .ctx + .config + .evm + .get(&self.chain_id.as_u64().to_string()) + .ok_or_else(|| webb_relayer_utils::Error::ChainNotFound { + chain_id: self.chain_id.to_string(), })?; + + // TimeLag client + let client = + TimeLag::new(signer_client, chain_config.block_confirmations); let chain_id = client .get_chainid() .map_err(|_| { @@ -98,7 +111,6 @@ where let metrics_clone = self.ctx.metrics.clone(); let task = || async { loop { - // tracing::trace!("Checking for any txs in the queue ..."); let maybe_tx = store .dequeue_item(SledQueueKey::from_evm_chain_id(chain_id))?; let maybe_explorer = &chain_config.explorer; @@ -108,6 +120,7 @@ where raw_tx.set_chain_id(U64::from(chain_id)).clone(); let my_tx_hash = raw_tx.sighash(); tx_hash = my_tx_hash; + tracing::debug!(?tx_hash, tx = ?raw_tx, "Found tx in queue"); // dry run test let dry_run_outcome = client.call(&raw_tx.clone(), None).await; diff --git a/crates/tx-queue/src/substrate/mod.rs b/crates/tx-queue/src/substrate/mod.rs index bc9b4eb5e..1e2c7d1c9 100644 --- a/crates/tx-queue/src/substrate/mod.rs +++ b/crates/tx-queue/src/substrate/mod.rs @@ -15,3 +15,88 @@ mod substrate_tx_queue; #[doc(hidden)] pub use substrate_tx_queue::*; + +#[cfg(test)] +mod tests { + use std::{collections::HashMap, sync::Arc}; + + use sp_core::{sr25519::Pair as Sr25519Pair, Pair}; + use webb::substrate::subxt::PolkadotConfig; + use webb::substrate::tangle_runtime::api as RuntimeApi; + use webb_relayer_store::sled::SledQueueKey; + use webb_relayer_store::QueueStore; + use webb_relayer_types::suri::Suri; + use webb_relayer_utils::static_tx_payload::TypeErasedStaticTxPayload; + + use super::*; + + pub fn setup_tracing() -> tracing::subscriber::DefaultGuard { + // Setup tracing for tests + let env_filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive( + tracing_subscriber::filter::LevelFilter::DEBUG.into(), + ) + .from_env_lossy(); + let s = tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_test_writer() + .without_time() + .with_target(false) + .compact() + .finish(); + tracing::subscriber::set_default(s) + } + + #[tokio::test] + #[ignore = "needs substrate node"] + async fn should_handle_many_txs() -> webb_relayer_utils::Result<()> { + let _guard = setup_tracing(); + let chain_id = 1081u32; + let config = webb_relayer_config::WebbRelayerConfig { + substrate: HashMap::from([( + chain_id.to_string(), + webb_relayer_config::substrate::SubstrateConfig { + name: String::from("tangle"), + enabled: true, + http_endpoint: "http://localhost:9933" + .parse::() + .unwrap() + .into(), + ws_endpoint: "ws://localhost:9944" + .parse::() + .unwrap() + .into(), + explorer: None, + chain_id, + suri: Some(Suri( + Sr25519Pair::from_string_with_seed("//Alice", None) + .unwrap() + .0, + )), + beneficiary: None, + pallets: Default::default(), + tx_queue: Default::default(), + }, + )]), + ..Default::default() + }; + let store = webb_relayer_store::SledStore::temporary()?; + let context = + webb_relayer_context::RelayerContext::new(config, store.clone())?; + let store = Arc::new(store); + let tx_queue = SubstrateTxQueue::new(context, chain_id, store.clone()); + let _handle = tokio::spawn(tx_queue.run::()); + let tx_count = 5; + let tx_api = RuntimeApi::tx().system(); + for i in 0..tx_count { + let tx = tx_api + .remark_with_event(format!("tx {}", i).as_bytes().to_vec()); + let tx = TypeErasedStaticTxPayload::try_from(tx)?; + let tx_key = SledQueueKey::from_substrate_chain_id(chain_id); + QueueStore::enqueue_item(&store, tx_key, tx)?; + } + // Wait for txs to be processed. + tokio::time::sleep(tokio::time::Duration::from_secs(120)).await; + Ok(()) + } +} diff --git a/crates/tx-queue/src/substrate/substrate_tx_queue.rs b/crates/tx-queue/src/substrate/substrate_tx_queue.rs index ef4f977d9..e0f77b3c9 100644 --- a/crates/tx-queue/src/substrate/substrate_tx_queue.rs +++ b/crates/tx-queue/src/substrate/substrate_tx_queue.rs @@ -15,37 +15,37 @@ use futures::StreamExt; use futures::TryFutureExt; use rand::Rng; +use webb::substrate::subxt; use webb::substrate::subxt::config::ExtrinsicParams; +use webb::substrate::subxt::PolkadotConfig; use webb_relayer_context::RelayerContext; use webb_relayer_store::sled::SledQueueKey; use webb_relayer_store::QueueStore; +use webb_relayer_utils::static_tx_payload::TypeErasedStaticTxPayload; use std::sync::Arc; use std::time::Duration; use sp_core::sr25519; -use std::marker::PhantomData; -use webb::substrate::subxt; -use webb::substrate::subxt::tx::{PairSigner, TxStatus as TransactionStatus}; -use webb_relayer_types::dynamic_payload::WebbDynamicTxPayload; +use webb::substrate::subxt::tx::TxStatus as TransactionStatus; /// The SubstrateTxQueue stores transaction call params in bytes so the relayer can process them later. /// This prevents issues such as creating transactions with the same nonce. /// Randomized sleep intervals are used to prevent relayers from submitting /// the same transaction. #[derive(Clone)] -pub struct SubstrateTxQueue<'a, S> +pub struct SubstrateTxQueue where - S: QueueStore, Key = SledQueueKey>, + S: QueueStore, { ctx: RelayerContext, chain_id: u32, store: Arc, - _marker: PhantomData<&'a ()>, } -impl<'a, S> SubstrateTxQueue<'a, S> + +impl SubstrateTxQueue where - S: QueueStore, Key = SledQueueKey>, + S: QueueStore, { /// Creates a new SubstrateTxQueue instance. /// @@ -61,7 +61,6 @@ where ctx, chain_id, store, - _marker: PhantomData {}, } } /// Starts the SubstrateTxQueue service. @@ -90,15 +89,6 @@ where max_elapsed_time: None, ..Default::default() }; - // protocol-substrate client - let client = self - .ctx - .substrate_provider::(&chain_id.to_string()) - .await?; - - // get pair - let pair = self.ctx.substrate_wallet(&chain_id.to_string()).await?; - let signer = PairSigner::new(pair); tracing::event!( target: webb_relayer_utils::probe::TARGET, @@ -111,25 +101,32 @@ where let metrics_clone = self.ctx.metrics.clone(); let task = || async { + // Tangle node connection + let maybe_client = self + .ctx + .substrate_provider::(chain_id) + .await; + let client = match maybe_client { + Ok(client) => client, + Err(err) => { + tracing::error!( + "Failed to connect with substrate client for chain_id: {}, retrying...!", + chain_id + ); + return Err(backoff::Error::transient(err)); + } + }; + let pair = self.ctx.substrate_wallet(chain_id).await?; + let signer = subxt::tx::PairSigner::::new(pair); loop { - tracing::trace!("Checking for any txs in the queue ..."); - // dequeue transaction call data. This are call params stored as bytes - let maybe_call_data = store.dequeue_item( + // dequeue signed transaction + let tx_call_data = store.dequeue_item( SledQueueKey::from_substrate_chain_id(chain_id), )?; - if let Some(payload) = maybe_call_data { - let dynamic_tx_payload = subxt::dynamic::tx( - payload.pallet_name, - payload.call_name, - payload.fields, - ); + if let Some(payload) = tx_call_data { let signed_extrinsic = client .tx() - .create_signed( - &dynamic_tx_payload, - &signer, - Default::default(), - ) + .create_signed(&payload, &signer, Default::default()) .map_err(Into::into) .map_err(backoff::Error::transient) .await?; @@ -143,6 +140,7 @@ where kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", chain_id = %chain_id, + tx = %payload, dry_run = "passed" ); } @@ -153,6 +151,7 @@ where kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", chain_id = %chain_id, + tx = %payload, errored = true, error = %err, dry_run = "failed" @@ -161,12 +160,21 @@ where } } // watch_extrinsic submits and returns transaction subscription - let mut progress = client - .tx() - .sign_and_submit_then_watch_default( - &dynamic_tx_payload, - &signer, - ) + let mut progress = signed_extrinsic + .submit_and_watch() + .inspect_err(|e| { + tracing::event!( + target: webb_relayer_utils::probe::TARGET, + tracing::Level::DEBUG, + kind = %webb_relayer_utils::probe::Kind::TxQueue, + ty = "SUBSTRATE", + chain_id = %chain_id, + tx = %payload, + errored = true, + error = %e, + progress = "failed", + ); + }) .map_err(Into::into) .map_err(backoff::Error::transient) .await?; @@ -181,6 +189,7 @@ where kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", chain_id = %chain_id, + tx = %payload, errored = true, error = %err, ); @@ -195,6 +204,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Future", ); @@ -205,6 +215,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Ready", ); @@ -215,17 +226,20 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Broadcast", ); } - TransactionStatus::InBlock(_) => { + TransactionStatus::InBlock(data) => { tracing::event!( target: webb_relayer_utils::probe::TARGET, tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, + block_hash = ?data.block_hash(), status = "InBlock", ); } @@ -234,6 +248,7 @@ where target: webb_relayer_utils::probe::TARGET, tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, + tx = %payload, ty = "SUBSTRATE", chain_id = %chain_id, status = "Retracted", @@ -244,6 +259,7 @@ where target: webb_relayer_utils::probe::TARGET, tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, + tx = %payload, ty = "SUBSTRATE", chain_id = %chain_id, status = "FinalityTimeout", @@ -255,6 +271,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Finalized", finalized = true, @@ -273,6 +290,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Usurped", ); @@ -283,6 +301,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Dropped", ); @@ -293,6 +312,7 @@ where tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::TxQueue, ty = "SUBSTRATE", + tx = %payload, chain_id = %chain_id, status = "Invalid", ); diff --git a/crates/tx-relay-utils/Cargo.toml b/crates/tx-relay-utils/Cargo.toml index bc0c6e6f4..8176ee288 100644 --- a/crates/tx-relay-utils/Cargo.toml +++ b/crates/tx-relay-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-tx-relay-utils" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -13,4 +13,4 @@ repository = { workspace = true } [dependencies] serde = { workspace = true } # Used by ethers (but we need it to be vendored with the lib). -native-tls = { workspace = true, optional = true } \ No newline at end of file +native-tls = { workspace = true, optional = true } diff --git a/crates/tx-relay-utils/src/lib.rs b/crates/tx-relay-utils/src/lib.rs index 689170cf0..dbc9b7cac 100644 --- a/crates/tx-relay-utils/src/lib.rs +++ b/crates/tx-relay-utils/src/lib.rs @@ -14,37 +14,13 @@ use serde::{Deserialize, Serialize}; -/// Contains data that is relayed to the Mixers -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MixerRelayTransaction { - /// one of the supported chains of this relayer - pub chain_id: u64, - /// The tree id of the mixer's underlying tree - pub id: Id, - /// The zero-knowledge proof bytes - pub proof: P, - /// The target merkle root for the proof - pub root: E, - /// The nullifier_hash for the proof - pub nullifier_hash: E, - /// The recipient of the transaction - pub recipient: I, - /// The relayer of the transaction - pub relayer: I, - /// The relayer's fee for the transaction - pub fee: B, - /// The refund for the transaction in native tokens - pub refund: B, -} - /// Contains data that is relayed to the Anchors #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AnchorRelayTransaction { /// one of the supported chains of this relayer pub chain_id: u64, - /// The tree id of the mixer's underlying tree + /// The tree id of the vanchor's underlying tree pub id: Id, /// The zero-knowledge proof bytes pub proof: P, @@ -114,7 +90,7 @@ pub struct ExtData { pub struct VAnchorRelayTransaction { /// one of the supported chains of this relayer pub chain_id: u64, - /// The tree id of the mixer's underlying tree + /// The tree id of the vanchor's underlying tree pub id: Id, /// The zero-knowledge proof data structure for VAnchor transactions pub proof_data: ProofData, diff --git a/crates/tx-relay/Cargo.toml b/crates/tx-relay/Cargo.toml index 8e1e88db2..200605ed5 100644 --- a/crates/tx-relay/Cargo.toml +++ b/crates/tx-relay/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-relayer-tx-relay" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -15,6 +15,8 @@ webb-relayer-handler-utils = { workspace = true } webb-relayer-config = { workspace = true } webb-relayer-context = { workspace = true } webb-relayer-utils = { workspace = true } +webb-price-oracle-backends = { workspace = true } +webb-chains-info = { workspace = true } tracing = { workspace = true } futures = { workspace = true } @@ -25,16 +27,13 @@ native-tls = { workspace = true, optional = true } webb-proposals = { workspace = true } ethereum-types = { workspace = true } serde = { workspace = true } +sp-core = { workspace = true } once_cell = "1.17.0" -chrono = {version = "0.4.23", features = ["serde"] } +chrono = { version = "0.4.23", features = ["serde"] } [features] default = ["std", "evm", "substrate"] std = [] -evm = [ - "webb-relayer-context/evm", -] -substrate = [ - "webb-relayer-context/substrate", -] +evm = ["webb-relayer-context/evm"] +substrate = ["webb-relayer-context/substrate"] diff --git a/crates/tx-relay/src/evm/fees.rs b/crates/tx-relay/src/evm/fees.rs index eb3420140..738f7a8f5 100644 --- a/crates/tx-relay/src/evm/fees.rs +++ b/crates/tx-relay/src/evm/fees.rs @@ -1,38 +1,41 @@ +use crate::{MAX_REFUND_USD, TRANSACTION_PROFIT_USD}; use chrono::DateTime; use chrono::Duration; use chrono::Utc; use once_cell::sync::Lazy; use serde::Serialize; +use std::cmp::min; use std::collections::HashMap; use std::ops::Add; use std::sync::{Arc, Mutex}; use webb::evm::contract::protocol_solidity::{ - FungibleTokenWrapperContract, OpenVAnchorContract, + FungibleTokenWrapperContract, VAnchorContract, }; -use webb::evm::ethers::middleware::SignerMiddleware; +use webb::evm::ethers::middleware::gas_oracle::GasOracle; use webb::evm::ethers::prelude::U256; +use webb::evm::ethers::providers::Middleware; +use webb::evm::ethers::signers::Signer; use webb::evm::ethers::types::Address; use webb::evm::ethers::utils::{format_units, parse_units}; +use webb_chains_info::chain_info_by_chain_id; +use webb_price_oracle_backends::PriceBackend; use webb_proposals::TypedChainId; use webb_relayer_context::RelayerContext; use webb_relayer_utils::Result; -/// Maximum refund amount per relay transaction in USD. -const MAX_REFUND_USD: f64 = 5.; /// Amount of time for which a `FeeInfo` is valid after creation static FEE_CACHE_TIME: Lazy = Lazy::new(|| Duration::minutes(1)); -/// Amount of profit that the relay should make with each transaction (in USD). -const TRANSACTION_PROFIT_USD: f64 = 5.; /// Cache for previously generated fee info. Key consists of the VAnchor address and chain id. /// Entries are valid as long as `timestamp` is no older than `FEE_CACHE_TIME`. -static FEE_INFO_CACHED: Lazy>> = - Lazy::new(|| Mutex::new(HashMap::new())); +static FEE_INFO_CACHED: Lazy< + Mutex>, +> = Lazy::new(|| Mutex::new(HashMap::new())); /// Return value of fee_info API call. Contains information about relay transaction fee and refunds. #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] -pub struct FeeInfo { +pub struct EvmFeeInfo { /// Estimated fee for an average relay transaction, in `wrappedToken`. This is only for /// display to the user pub estimated_fee: U256, @@ -47,6 +50,9 @@ pub struct FeeInfo { /// Price of the native token in USD, internally cached to recalculate estimated fee #[serde(skip)] native_token_price: f64, + /// Number of decimals of the native token, internally cached to recalculate max refund + #[serde(skip)] + native_token_decimals: u8, /// Price of the wrapped token in USD, internally cached to recalculate estimated fee #[serde(skip)] wrapped_token_price: f64, @@ -59,15 +65,16 @@ pub struct FeeInfo { /// /// If fee info was recently requested, the cached value is used. Otherwise it is regenerated /// based on the current exchange rate and estimated gas price. -pub async fn get_fee_info( +pub async fn get_evm_fee_info( chain_id: TypedChainId, vanchor: Address, gas_amount: U256, ctx: &RelayerContext, -) -> Result { +) -> Result { // Retrieve cached fee info item let fee_info_cached = { - let mut lock = FEE_INFO_CACHED.lock().unwrap(); + let mut lock = + FEE_INFO_CACHED.lock().expect("lock fee info cache mutex"); // Remove all items from cache which are older than `FEE_CACHE_TIME` lock.retain(|_, v| { let fee_info_valid_time = v.timestamp.add(*FEE_CACHE_TIME); @@ -76,33 +83,35 @@ pub async fn get_fee_info( lock.get(&(vanchor, chain_id)).cloned() }; - match fee_info_cached { - // There is a cached fee info, use it - Some(mut fee_info) => { - // Need to recalculate estimated fee with the gas amount that was passed in. We use - // cached exchange rate so that this matches calculation on the client. - fee_info.estimated_fee = calculate_transaction_fee( - fee_info.gas_price, - gas_amount, - fee_info.native_token_price, - fee_info.wrapped_token_price, - fee_info.wrapped_token_decimals, - ) - .await?; - Ok(fee_info) - } - // No cached fee info, generate new one - None => { - let fee_info = - generate_fee_info(chain_id, vanchor, gas_amount, ctx).await?; + if let Some(mut fee_info) = fee_info_cached { + // Need to recalculate estimated fee with the gas amount that was passed in. We use + // cached exchange rate so that this matches calculation on the client. + fee_info.estimated_fee = calculate_transaction_fee( + fee_info.gas_price, + gas_amount, + fee_info.native_token_price, + fee_info.wrapped_token_price, + fee_info.wrapped_token_decimals, + )?; + // Recalculate max refund in case relayer balance changed. + fee_info.max_refund = max_refund( + chain_id, + fee_info.native_token_price, + fee_info.native_token_decimals, + ctx, + ) + .await?; + Ok(fee_info) + } else { + let fee_info = + generate_fee_info(chain_id, vanchor, gas_amount, ctx).await?; - // Insert newly generated fee info into cache. - FEE_INFO_CACHED - .lock() - .unwrap() - .insert((vanchor, chain_id), fee_info.clone()); - Ok(fee_info) - } + // Insert newly generated fee info into cache. + FEE_INFO_CACHED + .lock() + .expect("lock fee info cache mutex") + .insert((vanchor, chain_id), fee_info.clone()); + Ok(fee_info) } } @@ -112,68 +121,102 @@ async fn generate_fee_info( vanchor: Address, gas_amount: U256, ctx: &RelayerContext, -) -> Result { +) -> Result { // Get token names - let native_token = get_native_token_name_and_decimals(chain_id)?; - let wrapped_token = + let (native_token, native_token_decimals) = + get_native_token_name_and_decimals(chain_id)?; + let (wrapped_token, wrapped_token_decimals) = get_wrapped_token_name_and_decimals(chain_id, vanchor, ctx).await?; - // Fetch USD prices for tokens from coingecko API (eg value of 1 ETH in USD). + // Fetch USD prices for tokens from the price oracle backend (eg value of 1 ETH in USD). let prices = ctx - .coin_gecko_client() - .price( - &[native_token.0, &wrapped_token.0], - &["usd"], - false, - false, - false, - false, - ) + .price_oracle() + .get_prices(&[native_token, &wrapped_token]) .await?; - let native_token_price = prices[native_token.0].usd.unwrap(); - let wrapped_token_price = prices[&wrapped_token.0].usd.unwrap(); - // Fetch native gas price estimate from etherscan.io, using "average" value - let gas_oracle = ctx.etherscan_client().gas_oracle().await?; - let gas_price_gwei = U256::from(gas_oracle.propose_gas_price); - let gas_price = parse_units(gas_price_gwei, "gwei")?.into(); + let native_token_price = match prices.get(native_token) { + Some(price) => *price, + None => { + return Err(webb_relayer_utils::Error::FetchTokenPriceError { + token: native_token.into(), + }) + } + }; + + let wrapped_token_price = match prices.get(&wrapped_token) { + Some(price) => *price, + None => { + return Err(webb_relayer_utils::Error::FetchTokenPriceError { + token: wrapped_token.clone(), + }) + } + }; + + // Fetch native gas price estimate from gas oracle, using "average" value + let gas_price = ctx + .gas_oracle(chain_id.underlying_chain_id()) + .await? + .fetch() + .await?; let estimated_fee = calculate_transaction_fee( gas_price, gas_amount, native_token_price, wrapped_token_price, - wrapped_token.1, - ) - .await?; + wrapped_token_decimals, + )?; // Calculate the exchange rate from wrapped token to native token which is used for the refund. - let refund_exchange_rate = - parse_units(native_token_price / wrapped_token_price, wrapped_token.1)? - .into(); + let refund_exchange_rate = parse_units( + native_token_price / wrapped_token_price, + wrapped_token_decimals, + )? + .into(); - // Calculate the maximum refund amount per relay transaction in `nativeToken`. - let max_refund = - parse_units(MAX_REFUND_USD / native_token_price, native_token.1)? - .into(); - - Ok(FeeInfo { + Ok(EvmFeeInfo { estimated_fee, gas_price, refund_exchange_rate, - max_refund, + max_refund: max_refund( + chain_id, + native_token_price, + native_token_decimals, + ctx, + ) + .await?, timestamp: Utc::now(), native_token_price, + native_token_decimals, wrapped_token_price, - wrapped_token_decimals: wrapped_token.1, + wrapped_token_decimals, }) } +async fn max_refund( + chain_id: TypedChainId, + native_token_price: f64, + native_token_decimals: u8, + ctx: &RelayerContext, +) -> Result { + let wallet = ctx.evm_wallet(chain_id.underlying_chain_id()).await?; + let provider = ctx.evm_provider(chain_id.underlying_chain_id()).await?; + let relayer_balance = provider.get_balance(wallet.address(), None).await?; + // Calculate the maximum refund amount per relay transaction in `nativeToken`. + // Ensuring that refund <= relayer balance + let max_refund = parse_units( + MAX_REFUND_USD / native_token_price, + u32::from(native_token_decimals), + )? + .into(); + Ok(min(relayer_balance, max_refund)) +} + /// Pull USD prices of base token from coingecko.com, and use this to calculate the transaction /// fee in `wrappedToken` wei. This fee includes a profit for the relay of `TRANSACTION_PROFIT_USD`. /// /// The algorithm is explained at https://www.notion.so/hicommonwealth/Private-Tx-Relay-Support-v1-f5522b04d6a349aab1bbdb0dd83a7fb4#6bb2b4920e3f42d69988688c6fa54e6e -async fn calculate_transaction_fee( +fn calculate_transaction_fee( gas_price: U256, gas_amount: U256, native_token_price: f64, @@ -202,28 +245,27 @@ async fn calculate_transaction_fee( Ok(fee_with_profit) } -/// Retrieves the token name of a given anchor contract. Wrapper prefixes are stripped in order -/// to get a token name which coingecko understands. +/// Returns the name and decimals of the wrapped token for the given chain. +/// then converts it to the underlying token token name to be used in the price oracle. async fn get_wrapped_token_name_and_decimals( chain_id: TypedChainId, vanchor: Address, ctx: &RelayerContext, ) -> Result<(String, u32)> { - let chain_name = chain_id.underlying_chain_id().to_string(); - let wallet = ctx.evm_wallet(&chain_name).await?; - let provider = ctx.evm_provider(&chain_name).await?; - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = ctx.evm_provider(chain_id.underlying_chain_id()).await?; + let client = Arc::new(provider); - let anchor_contract = OpenVAnchorContract::new(vanchor, client.clone()); + let anchor_contract = VAnchorContract::new(vanchor, client.clone()); let token_address = anchor_contract.token().call().await?; let token_contract = FungibleTokenWrapperContract::new(token_address, client.clone()); let token_symbol = token_contract.symbol().call().await?; // TODO: add all supported tokens let name = match token_symbol.replace("webb", "").as_str() { - "WETH" => "ethereum", + "Alpha" | "Standalone" | "WETH" => "ETH", + "tTNT-standalone" => "tTNT", // only used in tests - "WEBB" if cfg!(debug_assertions) => "ethereum", + "WEBB" if cfg!(debug_assertions) => "ETH", x => x, } .to_string(); @@ -231,44 +273,42 @@ async fn get_wrapped_token_name_and_decimals( Ok((name, decimals.into())) } -/// Hardcodede mapping from chain id to base token name. Testnets use the mainnet name because -/// otherwise there is no exchange rate available. -/// -/// https://github.com/DefiLlama/chainlist/blob/main/constants/chainIds.json +/// Returns the native token symbol and the decimals +/// of the given chain identifier fn get_native_token_name_and_decimals( chain_id: TypedChainId, -) -> Result<(&'static str, i32)> { +) -> Result<(&'static str, u8)> { use TypedChainId::*; - let name = match chain_id { - Evm(id) => { - match id { - 1 | // ethereum mainnet - 5 | // goerli testnet - 5001 | // hermes testnet - 5002 | // athena testnet - 5003 | // demeter testnet - 11155111 // sepolia testnet - => "ethereum", - // optimism mainnet and testnet - 10 | 420 => "optimism", - // polygon mainnet and testnet - 127 | 80001 => "polygon", - // moonbeam mainnet and testnet - 1284 | 1287 => "moonbeam", - _ => { - // Typescript tests use randomly generated chain id, so we always return - // "ethereum" in debug mode to make them work. - if cfg!(debug_assertions) { - "ethereum" - } else { - let chain_id = chain_id.chain_id().to_string(); - return Err(webb_relayer_utils::Error::ChainNotFound { chain_id }); - } + match chain_id { + Evm(id) => chain_info_by_chain_id(u64::from(id)).map_or_else( + || { + // Typescript tests use randomly generated chain id, so we always return + // "ethereum" in debug mode to make them work. + if cfg!(debug_assertions) { + Ok(("ETH", 18)) + } else { + let chain_id = chain_id.chain_id().to_string(); + Err(webb_relayer_utils::Error::ChainNotFound { chain_id }) + } + }, + |info| { + Ok((info.native_currency.symbol, info.native_currency.decimals)) + }, + ), + Substrate(id) => match id { + 1081 => Ok(("tTNT", 18)), + _ => { + // During testing, we will use the tTNT token for all substrate chains. + if cfg!(debug_assertions) { + Ok(("tTNT", 18)) + } else { + let chain_id = chain_id.chain_id().to_string(); + Err(webb_relayer_utils::Error::ChainNotFound { chain_id }) } } - } - _ => unimplemented!(), - }; - let decimals = 18; - Ok((name, decimals)) + }, + unknown => Err(webb_relayer_utils::Error::ChainNotFound { + chain_id: unknown.chain_id().to_string(), + }), + } } diff --git a/crates/tx-relay/src/evm/mod.rs b/crates/tx-relay/src/evm/mod.rs index 6f4c1ff12..b97309f96 100644 --- a/crates/tx-relay/src/evm/mod.rs +++ b/crates/tx-relay/src/evm/mod.rs @@ -1,7 +1,9 @@ +use ethereum_types::U256; use std::{sync::Arc, time::Duration}; use tokio::sync::Mutex; use webb::evm::ethers::{ + self, abi::Detokenize, prelude::{builders::ContractCall, Middleware}, }; @@ -9,7 +11,7 @@ use webb_proposals::ResourceId; use webb_relayer_handler_utils::{ into_withdraw_error, CommandResponse, CommandStream, WithdrawStatus, }; -use webb_relayer_utils::metric::{self, Metrics}; +use webb_relayer_utils::metric::{self}; pub mod fees; /// Variable Anchor transaction relayer. @@ -95,22 +97,27 @@ where finalized = true, tx_hash = %receipt.transaction_hash, ); - // gas spent by relayer on particular resource. - let gas_price = receipt.gas_used.unwrap_or_default(); - let mut metrics = metrics.lock().await; - let resource_metric = metrics - .resource_metric_map - .entry(resource_id) - .or_insert_with(|| Metrics::register_resource_id_counters(resource_id)); - - resource_metric - .total_gas_spent - .inc_by(gas_price.as_u64() as f64); - drop(metrics); let _ = stream .send(Withdraw(WithdrawStatus::Finalized { tx_hash: receipt.transaction_hash, })) .await; + // gas spent by relayer on particular resource. + let gas_used = receipt.gas_used.unwrap_or_default(); + let mut metrics = metrics.lock().await; + metrics + .resource_metric_entry(resource_id) + .total_gas_spent + .inc_by(wei_to_gwei(gas_used)); Ok(()) } + +fn wei_to_gwei(wei: U256) -> f64 { + ethers::utils::format_units(wei, "gwei") + .and_then(|gas| { + gas.parse::() + // TODO: this error is pointless as it is silently dropped + .map_err(|_| ethers::utils::ConversionError::ParseOverflow) + }) + .unwrap_or_default() +} diff --git a/crates/tx-relay/src/evm/vanchor.rs b/crates/tx-relay/src/evm/vanchor.rs index 11d4128f5..4c83ed739 100644 --- a/crates/tx-relay/src/evm/vanchor.rs +++ b/crates/tx-relay/src/evm/vanchor.rs @@ -1,7 +1,8 @@ use super::*; -use crate::evm::fees::{get_fee_info, FeeInfo}; +use crate::evm::fees::{get_evm_fee_info, EvmFeeInfo}; use crate::evm::handle_evm_tx; use ethereum_types::U256; +use futures::TryFutureExt; use std::{collections::HashMap, sync::Arc}; use webb::evm::ethers::utils::{format_units, parse_ether}; use webb::evm::{ @@ -15,7 +16,6 @@ use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; use webb_relayer_context::RelayerContext; use webb_relayer_handler_utils::EvmVanchorCommand; use webb_relayer_handler_utils::{CommandStream, NetworkStatus}; -use webb_relayer_utils::metric::Metrics; /// Handler for VAnchor commands /// @@ -52,12 +52,9 @@ pub async fn handle_vanchor_relay_tx<'a>( .get(&cmd.id) .ok_or(Network(NetworkStatus::UnsupportedContract))?; - let wallet = - ctx.evm_wallet(&cmd.chain_id.to_string()) - .await - .map_err(|e| { - Error(format!("Misconfigured Network: {:?}, {e}", cmd.chain_id)) - })?; + let wallet = ctx.evm_wallet(cmd.chain_id).await.map_err(|e| { + Error(format!("Misconfigured Network: {:?}, {e}", cmd.chain_id)) + })?; // validate the relayer address first before trying // send the transaction. let reward_address = chain.beneficiary.unwrap_or(wallet.address()); @@ -78,29 +75,13 @@ pub async fn handle_vanchor_relay_tx<'a>( chain.http_endpoint ); let _ = stream.send(Network(NetworkStatus::Connecting)).await; - let provider = - ctx.evm_provider(&cmd.chain_id.to_string()) - .await - .map_err(|e| { - Network(NetworkStatus::Failed { - reason: e.to_string(), - }) - })?; + let provider = ctx.evm_provider(cmd.chain_id).await.map_err(|e| { + Network(NetworkStatus::Failed { + reason: e.to_string(), + }) + })?; let _ = stream.send(Network(NetworkStatus::Connected)).await; - // ensure that relayer has enough balance for refund - let relayer_balance = provider - .get_balance(wallet.address(), None) - .await - .map_err(|e| { - Error(format!("Failed to retrieve relayer balance: {e}")) - })?; - if cmd.ext_data.refund > relayer_balance { - return Err(Error( - "Requested refund is higher than relayer balance".to_string(), - )); - } - let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = VAnchorContract::new(cmd.id, client.clone()); @@ -121,10 +102,14 @@ pub async fn handle_vanchor_relay_tx<'a>( .iter() .map(|v| v.to_fixed_bytes().into()) .collect(), - output_commitments: [ - cmd.proof_data.output_commitments[0].to_fixed_bytes().into(), - cmd.proof_data.output_commitments[1].to_fixed_bytes().into(), - ], + output_commitments: cmd + .proof_data + .output_commitments + .into_iter() + .map(|c| U256::from(c.to_fixed_bytes())) + .collect::>() + .try_into() + .unwrap_or_default(), public_amount: U256::from_big_endian( &cmd.proof_data.public_amount.to_fixed_bytes(), ), @@ -145,6 +130,7 @@ pub async fn handle_vanchor_relay_tx<'a>( public_inputs, encryptions, ); + if !cmd.ext_data.refund.is_zero() { call = call.value(cmd.ext_data.refund); } @@ -156,7 +142,7 @@ pub async fn handle_vanchor_relay_tx<'a>( }) })?; let typed_chain_id = TypedChainId::Evm(chain.chain_id); - let fee_info = get_fee_info( + let fee_info = get_evm_fee_info( typed_chain_id, contract_config.common.address, gas_amount, @@ -189,7 +175,8 @@ pub async fn handle_vanchor_relay_tx<'a>( if cmd.ext_data.fee < adjusted_fee + wrapped_amount { let msg = format!( "User sent a fee that is too low {} but expected {}", - cmd.ext_data.fee, fee_info.estimated_fee + cmd.ext_data.fee, + adjusted_fee + wrapped_amount ); return Err(Error(msg)); } @@ -207,24 +194,30 @@ pub async fn handle_vanchor_relay_tx<'a>( let metrics_clone = ctx.metrics.clone(); let mut metrics = metrics_clone.lock().await; // update metric for total fee earned by relayer on particular resource - let resource_metric = metrics - .resource_metric_map - .entry(resource_id) - .or_insert_with(|| Metrics::register_resource_id_counters(resource_id)); - resource_metric + metrics + .resource_metric_entry(resource_id) .total_fee_earned - .inc_by(cmd.ext_data.fee.as_u64() as f64); + .inc_by(cmd.ext_data.fee.as_u128() as f64); // update metric for total fee earned by relayer metrics .total_fee_earned - .inc_by(cmd.ext_data.fee.as_u64() as f64); + .inc_by(cmd.ext_data.fee.as_u128() as f64); + + let relayer_balance = client + .get_balance(client.signer().address(), None) + .unwrap_or_else(|_| U256::zero()) + .await; + + metrics + .account_balance_entry(typed_chain_id) + .set(wei_to_gwei(relayer_balance)); Ok(()) } fn calculate_wrapped_refund_amount( refund: U256, - fee_info: &FeeInfo, + fee_info: &EvmFeeInfo, ) -> webb_relayer_utils::Result { let refund_exchange_rate: f32 = format_units(fee_info.refund_exchange_rate, "ether")?.parse()?; diff --git a/crates/tx-relay/src/lib.rs b/crates/tx-relay/src/lib.rs index ca4bd99a0..62c11da01 100644 --- a/crates/tx-relay/src/lib.rs +++ b/crates/tx-relay/src/lib.rs @@ -4,3 +4,8 @@ pub mod evm; /// Substrate Transactional Relayer. #[cfg(feature = "substrate")] pub mod substrate; + +/// Maximum refund amount per relay transaction in USD. +const MAX_REFUND_USD: f64 = 5.; +/// Amount of profit that the relay should make with each transaction (in USD). +const TRANSACTION_PROFIT_USD: f64 = 5.; diff --git a/crates/tx-relay/src/substrate/fees.rs b/crates/tx-relay/src/substrate/fees.rs new file mode 100644 index 000000000..5b760a490 --- /dev/null +++ b/crates/tx-relay/src/substrate/fees.rs @@ -0,0 +1,71 @@ +use crate::substrate::balance; +use crate::{MAX_REFUND_USD, TRANSACTION_PROFIT_USD}; +use chrono::{DateTime, Utc}; +use serde::Serialize; +use sp_core::U256; +use std::cmp::min; +use webb::evm::ethers::utils::__serde_json::Value; +use webb::substrate::subxt::tx::PairSigner; +use webb::substrate::subxt::PolkadotConfig; +use webb_relayer_context::RelayerContext; +use webb_relayer_utils::Error; + +const TOKEN_PRICE_USD: f64 = 0.1; + +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SubstrateFeeInfo { + /// Estimated fee for an average relay transaction, in `wrappedToken`. Includes network fees + /// and relay fee. + pub estimated_fee: U256, + /// Exchange rate for refund from `wrappedToken` to `nativeToken` + pub refund_exchange_rate: U256, + /// Maximum amount of `nativeToken` which can be exchanged to `wrappedToken` by relay + pub max_refund: U256, + /// Time when this FeeInfo was generated + timestamp: DateTime, +} + +pub async fn get_substrate_fee_info( + chain_id: u64, + estimated_tx_fees: U256, + ctx: &RelayerContext, +) -> webb_relayer_utils::Result { + let client = ctx + .substrate_provider::(chain_id) + .await?; + let decimals: i32 = client + .rpc() + .system_properties() + .await? + .get("tokenDecimals") + .and_then(Value::as_i64) + .ok_or(Error::ReadSubstrateStorageError)? + as i32; + let estimated_fee = estimated_tx_fees + + native_token_to_unit( + TRANSACTION_PROFIT_USD / TOKEN_PRICE_USD, + decimals, + ); + let refund_exchange_rate = native_token_to_unit(1., decimals); + let max_refund = + native_token_to_unit(MAX_REFUND_USD / TOKEN_PRICE_USD, decimals); + let pair = ctx.substrate_wallet(chain_id).await?; + let signer = PairSigner::new(pair.clone()); + + let max_refund = + min(max_refund, U256::from(balance(client, signer).await?)); + Ok(SubstrateFeeInfo { + estimated_fee, + refund_exchange_rate, + max_refund, + timestamp: Utc::now(), + }) +} + +/// Convert from full wrapped token amount to smallest unit amount. +/// +/// It looks like subxt has no built-in functionality for this. +fn native_token_to_unit(matic: f64, token_decimals: i32) -> U256 { + U256::from((matic * 10_f64.powi(token_decimals)) as u128) +} diff --git a/crates/tx-relay/src/substrate/mixer.rs b/crates/tx-relay/src/substrate/mixer.rs deleted file mode 100644 index a69d6d900..000000000 --- a/crates/tx-relay/src/substrate/mixer.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::*; -use crate::substrate::handle_substrate_tx; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; -use webb::substrate::{ - protocol_substrate_runtime::api::runtime_types::webb_primitives::runtime::Element, - subxt::{tx::PairSigner, SubstrateConfig}, -}; -use webb_relayer_context::RelayerContext; -use webb_relayer_handler_utils::SubstrateMixerCommand; - -/// Handler for Substrate Mixer commands -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `cmd` - The command to execute -/// * `stream` - The stream to write the response to -pub async fn handle_substrate_mixer_relay_tx<'a>( - ctx: RelayerContext, - cmd: SubstrateMixerCommand, - stream: CommandStream, -) -> Result<(), CommandResponse> { - use CommandResponse::*; - - let root_element = Element(cmd.root); - let nullifier_hash_element = Element(cmd.nullifier_hash); - - let requested_chain = cmd.chain_id; - let client = ctx - .substrate_provider::(&requested_chain.to_string()) - .await - .map_err(|e| { - Error(format!("Error while getting Substrate client: {e}")) - })?; - - let pair = ctx - .substrate_wallet(&cmd.chain_id.to_string()) - .await - .map_err(|e| { - Error(format!("Misconfigured Network {:?}: {e}", cmd.chain_id)) - })?; - - let signer = PairSigner::new(pair); - - let withdraw_tx = RuntimeApi::tx().mixer_bn254().withdraw( - cmd.id, - cmd.proof, - root_element, - nullifier_hash_element, - cmd.recipient, - cmd.relayer, - cmd.fee, - cmd.refund, - ); - - let withdraw_tx_hash = client - .tx() - .sign_and_submit_then_watch_default(&withdraw_tx, &signer) - .await; - - let event_stream = withdraw_tx_hash - .map_err(|e| Error(format!("Error while sending Tx: {e}")))?; - - handle_substrate_tx(event_stream, stream, cmd.chain_id).await?; - Ok(()) -} diff --git a/crates/tx-relay/src/substrate/mod.rs b/crates/tx-relay/src/substrate/mod.rs index df5c62017..c6c44c2a2 100644 --- a/crates/tx-relay/src/substrate/mod.rs +++ b/crates/tx-relay/src/substrate/mod.rs @@ -1,15 +1,17 @@ use ethereum_types::H256; use futures::TryStreamExt; +use sp_core::sr25519::Pair; +use webb::substrate::subxt::tx::PairSigner; use webb::substrate::subxt::{ tx::TxProgress, tx::TxStatus as TransactionStatus, OnlineClient, - SubstrateConfig, + PolkadotConfig, }; +use webb::substrate::tangle_runtime::api; use webb_relayer_handler_utils::{ CommandResponse, CommandStream, WithdrawStatus, }; -/// Substrate Mixer Transactional Relayer. -pub mod mixer; +pub mod fees; /// Substrate Variable Anchor Transactional Relayer. pub mod vanchor; @@ -19,10 +21,7 @@ pub mod vanchor; /// is intended to be used in a variety of places for all kinds of submitted Substrate /// transactions. pub async fn handle_substrate_tx( - mut event_stream: TxProgress< - SubstrateConfig, - OnlineClient, - >, + mut event_stream: TxProgress>, stream: CommandStream, chain_id: u64, ) -> Result<(), CommandResponse> { @@ -114,3 +113,22 @@ pub async fn handle_substrate_tx( } Ok(()) } + +fn wei_to_gwei(wei: u128) -> f64 { + (wei / (10 ^ 9)) as f64 +} + +async fn balance( + client: OnlineClient, + signer: PairSigner, +) -> webb_relayer_utils::Result { + let account = api::storage().system().account(signer.account_id()); + let balance = client + .storage() + .at(None) + .await? + .fetch(&account) + .await? + .ok_or(webb_relayer_utils::Error::ReadSubstrateStorageError)?; + Ok(balance.data.free) +} diff --git a/crates/tx-relay/src/substrate/vanchor.rs b/crates/tx-relay/src/substrate/vanchor.rs index 2a52dfa2f..d3a0d3231 100644 --- a/crates/tx-relay/src/substrate/vanchor.rs +++ b/crates/tx-relay/src/substrate/vanchor.rs @@ -1,19 +1,20 @@ use super::*; +use crate::substrate::fees::get_substrate_fee_info; use crate::substrate::handle_substrate_tx; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api as RuntimeApi; use webb::substrate::subxt::utils::AccountId32; +use webb::substrate::tangle_runtime::api::runtime_types::tangle_standalone_runtime::protocol_substrate_config::Element; use webb::substrate::{ - protocol_substrate_runtime::api::runtime_types::{ - webb_primitives::runtime::Element, webb_primitives::types::vanchor, - }, - subxt::{tx::PairSigner, SubstrateConfig}, -}; + subxt::{PolkadotConfig, tx::PairSigner}, + tangle_runtime::api::runtime_types::webb_primitives::types::vanchor, +};use ethereum_types::U256; +use sp_core::{Decode, Encode}; +use webb::substrate::scale::Compact; use webb_proposals::{ ResourceId, SubstrateTargetSystem, TargetSystem, TypedChainId, }; use webb_relayer_context::RelayerContext; use webb_relayer_handler_utils::SubstrateVAchorCommand; -use webb_relayer_utils::metric::Metrics; /// Handler for Substrate Anchor commands /// @@ -51,40 +52,91 @@ pub async fn handle_substrate_vanchor_relay_tx<'a>( vanchor::ExtData { recipient: cmd.ext_data.recipient, relayer: cmd.ext_data.relayer, - fee: cmd.ext_data.fee, - ext_amount: cmd.ext_data.ext_amount, - encrypted_output1: cmd.ext_data.encrypted_output1.to_vec(), - encrypted_output2: cmd.ext_data.encrypted_output2.to_vec(), - refund: cmd.ext_data.refund, + fee: cmd.ext_data.fee.as_u128(), + ext_amount: cmd.ext_data.ext_amount.0, + encrypted_output1: cmd.ext_data.encrypted_output1.clone(), + encrypted_output2: cmd.ext_data.encrypted_output2.clone(), + refund: cmd.ext_data.refund.as_u128(), token: cmd.ext_data.token, }; let requested_chain = cmd.chain_id; let maybe_client = ctx - .substrate_provider::(&requested_chain.to_string()) + .substrate_provider::(requested_chain) .await; let client = maybe_client.map_err(|e| { Error(format!("Error while getting Substrate client: {e}")) })?; - let pair = ctx - .substrate_wallet(&cmd.chain_id.to_string()) - .await - .map_err(|e| { - Error(format!("Misconfigured Network {:?}: {e}", cmd.chain_id)) - })?; + let pair = ctx.substrate_wallet(requested_chain).await.map_err(|e| { + Error(format!("Misconfigured Network {:?}: {e}", cmd.chain_id)) + })?; - let signer = PairSigner::new(pair); + let signer = PairSigner::new(pair.clone()); let transact_tx = RuntimeApi::tx().v_anchor_bn254().transact( cmd.id, proof_elements, ext_data_elements, ); - let transact_tx_hash = client + + // TODO: Taken from subxt PR. Replace with new method state_call_decoded() after upgrading subxt. + // https://github.com/paritytech/subxt/pull/910 + let signed = client .tx() - .sign_and_submit_then_watch_default(&transact_tx, &signer) - .await; + .create_signed(&transact_tx, &signer, Default::default()) + .await + .map_err(|e| Error(format!("Failed to sign transaction: {e}")))?; + let mut params = signed.encoded().to_vec(); + (signed.encoded().len() as u32).encode_to(&mut params); + let bytes = client + .rpc() + .state_call("TransactionPaymentApi_query_info", Some(¶ms), None) + .await + .map_err(|e| { + Error(format!( + "RPC call TransactionPaymentApi_query_info failed: {e}" + )) + })?; + let cursor = &mut &bytes[..]; + let payment_info: (Compact, Compact, u8, u128) = + Decode::decode(cursor).map_err(|e| { + Error(format!("Failed to decode payment info: {e}")) + })?; + let fee_info = get_substrate_fee_info( + requested_chain, + U256::from(payment_info.3), + &ctx, + ) + .await + .map_err(|e| Error(format!("Get substrate fee info failed: {e}")))?; + + // validate refund amount + if U256::from(cmd.ext_data.refund) > fee_info.max_refund { + // TODO: use error enum for these messages so they dont have to be duplicated between + // evm/substrate + let msg = format!( + "User requested a refund which is higher than the maximum of {}", + fee_info.max_refund + ); + return Err(Error(msg)); + } + + // Check that transaction fee is enough to cover network fee and relayer fee + // TODO: refund needs to be converted from wrapped token to native token once there + // is an exchange rate + if U256::from(cmd.ext_data.fee) + < fee_info.estimated_fee + cmd.ext_data.refund + { + let msg = format!( + "User sent a fee that is too low ({}) but expected {}", + cmd.ext_data.fee, + fee_info.estimated_fee + cmd.ext_data.refund + ); + return Err(Error(msg)); + } + + let transact_tx_hash = signed.submit_and_watch().await; let event_stream = transact_tx_hash .map_err(|e| Error(format!("Error while sending Tx: {e}")))?; @@ -110,15 +162,48 @@ pub async fn handle_substrate_vanchor_relay_tx<'a>( let metrics_clone = ctx.metrics.clone(); let mut metrics = metrics_clone.lock().await; // update metric for total fee earned by relayer on particular resource - let resource_metric = metrics - .resource_metric_map - .entry(resource_id) - .or_insert_with(|| Metrics::register_resource_id_counters(resource_id)); - - resource_metric + metrics + .resource_metric_entry(resource_id) .total_fee_earned - .inc_by(cmd.ext_data.fee as f64); + .inc_by(cmd.ext_data.fee.as_u128() as f64); // update metric for total fee earned by relayer - metrics.total_fee_earned.inc_by(cmd.ext_data.fee as f64); + metrics + .total_fee_earned + .inc_by(wei_to_gwei(cmd.ext_data.fee.as_u128())); + + let balance = balance(client, signer) + .await + .map_err(|e| Error(format!("Failed to read substrate balance: {e}")))?; + metrics + .account_balance_entry(typed_chain_id) + .set(wei_to_gwei(balance)); Ok(()) } + +#[cfg(tests)] +mod test { + use webb::substrate::subxt::runtime_api::RuntimeApiClient; + use webb::substrate::subxt::tx::PairSigner; + use webb::substrate::subxt::utils::AccountId32; + use webb::substrate::subxt::SubstrateConfig; + + #[tokio::test] + async fn test_account_balance() { + // TODO: use alice account id from + // https://docs.rs/sp-keyring/latest/sp_keyring/ed25519/enum.Keyring.html + let account_id = AccountId32::from([0u8; 32]); + let account = RuntimeApi::storage().balances().account(account_id); + let client = subxt::OnlineClient::::default().await?; + let balance = client + .storage() + .at(None) + .await + .unwrap() + .fetch(&account) + .await + .unwrap(); + dbg!(balance); + assert_eq!(balance, None); + fail!(); + } +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d55cfb418..3717d436a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -7,18 +7,37 @@ services: env_file: - ./nginx-certbot.env ports: - - 80:80 - - 443:443 + - "80:80" + - "443:443" volumes: - nginx_secrets:/etc/letsencrypt - ./user_conf.d:/etc/nginx/user_conf.d + relayer: image: ghcr.io/webb-tools/relayer:edge restart: unless-stopped depends_on: [nginx] ports: - - 9955:9955 + - "127.0.0.1:9955:9955" volumes: - ./config:/config + + grafana: + image: grafana/grafana:9.4.7 + ports: + - "127.0.0.1:3000:3000" + depends_on: [prometheus] + volumes: + - grafana-data:/var/lib/grafana + - ./provisioning:/etc/grafana/provisioning + + prometheus: + image: prom/prometheus:v2.43.0 + ports: + - "127.0.0.1:9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + volumes: nginx_secrets: + grafana-data: diff --git a/docker/prometheus.yml b/docker/prometheus.yml new file mode 100644 index 000000000..77d72aa45 --- /dev/null +++ b/docker/prometheus.yml @@ -0,0 +1,10 @@ +global: + scrape_interval: 15s + evaluation_interval: 30s + +scrape_configs: + - job_name: 'webb-relayer' + metrics_path: '/api/v1/metrics' + + static_configs: + - targets: ['relayer:9955'] \ No newline at end of file diff --git a/docker/provisioning/alerting/alerting.yaml b/docker/provisioning/alerting/alerting.yaml new file mode 100644 index 000000000..2823b6087 --- /dev/null +++ b/docker/provisioning/alerting/alerting.yaml @@ -0,0 +1,101 @@ +apiVersion: 1 +groups: + - orgId: 1 + name: relayer + folder: webb-relayer + interval: 1m + rules: + - uid: mZQdReE4k + title: account-balance + condition: C + data: + - refId: A + relativeTimeRange: + from: 600 + to: 0 + datasourceUid: PBFA97CFB590B2093 + model: + editorMode: builder + expr: chain_account_balance / 1000000000 + hide: false + intervalMs: 1000 + legendFormat: __auto + maxDataPoints: 43200 + range: true + refId: A + - refId: B + relativeTimeRange: + from: 600 + to: 0 + datasourceUid: __expr__ + model: + conditions: + - evaluator: + params: [] + type: gt + operator: + type: and + query: + params: + - B + reducer: + params: [] + type: last + type: query + datasource: + type: __expr__ + uid: __expr__ + expression: A + hide: false + intervalMs: 1000 + maxDataPoints: 43200 + reducer: last + refId: B + type: reduce + - refId: C + relativeTimeRange: + from: 600 + to: 0 + datasourceUid: __expr__ + model: + conditions: + - evaluator: + params: + - 1 + type: lt + operator: + type: and + query: + params: + - C + reducer: + params: [] + type: last + type: query + datasource: + type: __expr__ + uid: __expr__ + expression: B + hide: false + intervalMs: 1000 + maxDataPoints: 43200 + refId: C + type: threshold + noDataState: NoData + execErrState: Error + for: 5m + isPaused: false + +contactPoints: + - org_id: 1 + name: slack + receivers: + - uid: slack1 + type: slack + settings: + # Replace this with Slack Incoming Webhook URL to enable notifications + url: 'slack-placeholder' + +policies: + - orgId: 1 + receiver: slack \ No newline at end of file diff --git a/docker/provisioning/dashboards/dashboard.json b/docker/provisioning/dashboards/dashboard.json new file mode 100644 index 000000000..57db62464 --- /dev/null +++ b/docker/provisioning/dashboards/dashboard.json @@ -0,0 +1,1000 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "total_fee_earned / 10^18", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total fee earned", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "gas_spent / 10^18", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Gas spent", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "chain_account_balance / 10 ^ 9", + "legendFormat": "{{chain_type}}({{chain_id}})", + "range": true, + "refId": "A" + } + ], + "title": "Chain account balance", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "anchor_update_proposals", + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A" + } + ], + "title": "Anchor update proposals", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "resource_total_fees_earned / 10 ^ 18", + "legendFormat": "{{chain_type}}({{chain_id}}) {{target_system_type}}({{target_system_value}})", + "range": true, + "refId": "A" + } + ], + "title": "Resource total fees earned", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "evm_transaction_queue_back_off", + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "substrate_transaction_queue_back_off", + "hide": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "transaction_queue_back_off", + "hide": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "C" + } + ], + "title": "Queue backoff", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "resource_total_gas_spent / 10^9", + "legendFormat": "{{chain_type}}({{chain_id}}) {{target_system_type}}({{target_system_value}})", + "range": true, + "refId": "A" + } + ], + "title": "Resource total gas spent", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bridge_watcher_back_off", + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "hide": false, + "refId": "B" + } + ], + "title": "Bridge Watcher Backoff", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 20, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "total_amount_of_data_stored", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total amount of data stored", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 30 + }, + "id": 22, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "proposals_signed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Proposals signed", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 32 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "proposals_processed_evm_tx_queue", + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "proposals_processed_substrate_tx_queue", + "hide": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "proposals_processed_tx_queue", + "hide": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "C" + } + ], + "title": "Proposals processed", + "type": "timeseries" + } + ], + "refresh": "5s", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Relayer", + "uid": "MGkfbJEVk", + "version": 3, + "weekStart": "" +} diff --git a/docker/provisioning/dashboards/default.yaml b/docker/provisioning/dashboards/default.yaml new file mode 100644 index 000000000..472ea7986 --- /dev/null +++ b/docker/provisioning/dashboards/default.yaml @@ -0,0 +1,9 @@ +apiVersion: 1 + +providers: + - name: Default + folder: webb-relayer + type: file + options: + path: + /etc/grafana/provisioning/dashboards/dashboard.json diff --git a/docker/provisioning/datasources/default.yaml b/docker/provisioning/datasources/default.yaml new file mode 100644 index 000000000..e5e23d012 --- /dev/null +++ b/docker/provisioning/datasources/default.yaml @@ -0,0 +1,6 @@ +apiVersion: 1 +datasources: + - name: Prometheus + type: prometheus + url: http://localhost:9090 + uid: PBFA97CFB590B2093 diff --git a/event-watchers/dkg/Cargo.toml b/event-watchers/dkg/Cargo.toml index 8f612aeb5..e77f2ac9c 100644 --- a/event-watchers/dkg/Cargo.toml +++ b/event-watchers/dkg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-ew-dkg" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/event-watchers/dkg/src/lib.rs b/event-watchers/dkg/src/lib.rs index 124a83c30..08635e3a5 100644 --- a/event-watchers/dkg/src/lib.rs +++ b/event-watchers/dkg/src/lib.rs @@ -20,12 +20,12 @@ pub use proposal_signed_handler::*; mod public_key_changed_handler; #[doc(hidden)] pub use public_key_changed_handler::*; -use webb::substrate::dkg_runtime::api::{ +use webb::substrate::subxt::events::StaticEvent; +use webb::substrate::subxt::PolkadotConfig; +use webb::substrate::tangle_runtime::api::{ dkg::events::PublicKeySignatureChanged, dkg_proposal_handler::events::ProposalSigned, }; -use webb::substrate::subxt::events::StaticEvent; -use webb::substrate::subxt::{self, PolkadotConfig}; use webb_event_watcher_traits::SubstrateEventWatcher; /// The DKGMetadataWatcher watches for the events from Dkg Pallet. @@ -38,8 +38,6 @@ impl SubstrateEventWatcher for DKGMetadataWatcher { const PALLET_NAME: &'static str = PublicKeySignatureChanged::PALLET; - type Client = subxt::OnlineClient; - type Store = webb_relayer_store::SledStore; } @@ -53,7 +51,5 @@ impl SubstrateEventWatcher for DKGProposalHandlerWatcher { const PALLET_NAME: &'static str = ProposalSigned::PALLET; - type Client = subxt::OnlineClient; - type Store = webb_relayer_store::SledStore; } diff --git a/event-watchers/dkg/src/proposal_signed_handler.rs b/event-watchers/dkg/src/proposal_signed_handler.rs index e70423253..387bd55e6 100644 --- a/event-watchers/dkg/src/proposal_signed_handler.rs +++ b/event-watchers/dkg/src/proposal_signed_handler.rs @@ -16,8 +16,8 @@ use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::dkg_runtime::api::dkg_proposal_handler; -use webb::substrate::dkg_runtime::api::runtime_types::webb_proposals::header::TypedChainId; +use webb::substrate::tangle_runtime::api::dkg_proposal_handler; +use webb::substrate::tangle_runtime::api::runtime_types::webb_proposals::header::TypedChainId; use webb::substrate::subxt::{self, OnlineClient, PolkadotConfig}; use webb_relayer_store::sled::{SledQueueKey, SledStore}; diff --git a/event-watchers/dkg/src/public_key_changed_handler.rs b/event-watchers/dkg/src/public_key_changed_handler.rs index f2bc1fe19..3dc7bee94 100644 --- a/event-watchers/dkg/src/public_key_changed_handler.rs +++ b/event-watchers/dkg/src/public_key_changed_handler.rs @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ethereum_types::U256; use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::dkg_runtime::api::dkg; use webb::substrate::subxt::{self, OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api::dkg; use webb_relayer_store::sled::{SledQueueKey, SledStore}; use webb_relayer_store::{BridgeCommand, BridgeKey, QueueStore}; @@ -75,11 +74,22 @@ impl EventHandler for DKGPublicKeyChangedHandler { %block_number, "DKG Public Key Changed", ); - let bridge_keys = self - .webb_config - .evm - .values() - .map(|c| BridgeKey::new(U256::from(c.chain_id))); + let mut bridge_keys = Vec::new(); + // get evm bridges + for (_, config) in self.webb_config.evm.iter() { + let typed_chain_id = + webb_proposals::TypedChainId::Evm(config.chain_id); + let bridge_key = BridgeKey::new(typed_chain_id); + bridge_keys.push(bridge_key); + } + // get substrate bridges + for (_, config) in self.webb_config.substrate.iter() { + let typed_chain_id = + webb_proposals::TypedChainId::Substrate(config.chain_id); + let bridge_key = BridgeKey::new(typed_chain_id); + bridge_keys.push(bridge_key); + } + // now we just signal every signature bridge to transfer the ownership. for bridge_key in bridge_keys { tracing::debug!( diff --git a/event-watchers/evm/Cargo.toml b/event-watchers/evm/Cargo.toml index 189679588..c7e0108d1 100644 --- a/event-watchers/evm/Cargo.toml +++ b/event-watchers/evm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-ew-evm" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -12,6 +12,7 @@ repository = { workspace = true } [dependencies] webb-proposal-signing-backends = { workspace = true } +webb-bridge-registry-backends = { workspace = true } webb-event-watcher-traits = { workspace = true } webb-relayer-store = { workspace = true } webb-relayer-config = { workspace = true } @@ -37,3 +38,4 @@ webb = { workspace = true } native-tls = { workspace = true } webb-proposals ={ workspace = true } ethereum-types = { workspace = true } +typed-builder = { workspace = true } diff --git a/event-watchers/evm/src/lib.rs b/event-watchers/evm/src/lib.rs index 168e227ce..9261bb12d 100644 --- a/event-watchers/evm/src/lib.rs +++ b/event-watchers/evm/src/lib.rs @@ -16,18 +16,14 @@ use std::ops; use std::sync::Arc; use std::time::Duration; use webb::evm::contract::protocol_solidity::{ - OpenVAnchorContract, OpenVAnchorContractEvents, VAnchorContract, - VAnchorContractEvents, + VAnchorContract, VAnchorContractEvents, }; use webb::evm::ethers::contract::Contract; use webb::evm::ethers::prelude::Middleware; -use webb::evm::ethers::{providers, types}; +use webb::evm::ethers::types; pub mod signature_bridge_watcher; -/// A module for listening on open vanchor events. -#[doc(hidden)] -pub mod open_vanchor; /// A module for listening on vanchor events. #[doc(hidden)] pub mod vanchor; @@ -36,6 +32,7 @@ pub mod vanchor; mod tests; use webb_event_watcher_traits::evm::{EventWatcher, WatchableContract}; +use webb_event_watcher_traits::EthersTimeLagClient; use webb_relayer_store::SledStore; // VAnchorContractWrapper contains VAnchorContract contract along with configurations for Anchor contract, and Relayer. @@ -101,8 +98,6 @@ where } } -type HttpProvider = providers::Provider; - /// An Anchor Contract Watcher that watches for the Anchor contract events and calls the event /// handlers. #[derive(Copy, Clone, Debug, Default)] @@ -117,88 +112,9 @@ pub struct VAnchorContractWatcher; impl EventWatcher for VAnchorContractWatcher { const TAG: &'static str = "VAnchor Contract Watcher"; - type Contract = VAnchorContractWrapper; + type Contract = VAnchorContractWrapper; type Events = VAnchorContractEvents; type Store = SledStore; } - -// OpenVAnchorContractWrapper contains VAnchorContract contract along with configurations for Anchor contract, and Relayer. -#[derive(Clone, Debug)] -pub struct OpenVAnchorContractWrapper -where - M: Middleware, -{ - pub config: webb_relayer_config::evm::VAnchorContractConfig, - pub webb_config: webb_relayer_config::WebbRelayerConfig, - pub contract: OpenVAnchorContract, -} - -impl OpenVAnchorContractWrapper -where - M: Middleware, -{ - /// Creates a new OpenVAnchorContractOverDKGWrapper. - pub fn new( - config: webb_relayer_config::evm::VAnchorContractConfig, - webb_config: webb_relayer_config::WebbRelayerConfig, - client: Arc, - ) -> Self { - Self { - contract: OpenVAnchorContract::new(config.common.address, client), - config, - webb_config, - } - } -} - -impl ops::Deref for OpenVAnchorContractWrapper -where - M: Middleware, -{ - type Target = Contract; - - fn deref(&self) -> &Self::Target { - &self.contract - } -} - -impl WatchableContract for OpenVAnchorContractWrapper -where - M: Middleware, -{ - fn deployed_at(&self) -> types::U64 { - self.config.common.deployed_at.into() - } - - fn polling_interval(&self) -> Duration { - Duration::from_millis(self.config.events_watcher.polling_interval) - } - - fn max_blocks_per_step(&self) -> types::U64 { - self.config.events_watcher.max_blocks_per_step.into() - } - - fn print_progress_interval(&self) -> Duration { - Duration::from_millis( - self.config.events_watcher.print_progress_interval, - ) - } -} - -/// An Open VAnchor Contract Watcher that watches for the Anchor contract events and calls the event -/// handlers. -#[derive(Copy, Clone, Debug, Default)] -pub struct OpenVAnchorContractWatcher; - -#[async_trait::async_trait] -impl EventWatcher for OpenVAnchorContractWatcher { - const TAG: &'static str = "Open VAnchor Contract Watcher"; - - type Contract = OpenVAnchorContractWrapper; - - type Events = OpenVAnchorContractEvents; - - type Store = SledStore; -} diff --git a/event-watchers/evm/src/open_vanchor/mod.rs b/event-watchers/evm/src/open_vanchor/mod.rs deleted file mode 100644 index 474181fe9..000000000 --- a/event-watchers/evm/src/open_vanchor/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::*; -pub mod open_vanchor_deposit_handler; -pub mod open_vanchor_leaves_handler; - -#[doc(hidden)] -pub use open_vanchor_deposit_handler::*; -#[doc(hidden)] -pub use open_vanchor_leaves_handler::*; diff --git a/event-watchers/evm/src/open_vanchor/open_vanchor_deposit_handler.rs b/event-watchers/evm/src/open_vanchor/open_vanchor_deposit_handler.rs deleted file mode 100644 index 49ddc34ee..000000000 --- a/event-watchers/evm/src/open_vanchor/open_vanchor_deposit_handler.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2022 Webb Technologies Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{HttpProvider, OpenVAnchorContractWrapper}; -use ethereum_types::H256; -use std::sync::Arc; -use tokio::sync::Mutex; -use webb::evm::contract::protocol_solidity::OpenVAnchorContractEvents; -use webb::evm::ethers::prelude::{LogMeta, Middleware}; -use webb_event_watcher_traits::evm::EventHandler; -use webb_proposal_signing_backends::{ - proposal_handler, ProposalSigningBackend, -}; -use webb_relayer_config::anchor::LinkedAnchorConfig; -use webb_relayer_store::EventHashStore; -use webb_relayer_store::SledStore; -use webb_relayer_utils::metric; - -/// Represents an VAnchor Contract Watcher which will use a configured signing backend for signing proposals. -pub struct OpenVAnchorDepositHandler { - proposal_signing_backend: B, -} - -impl OpenVAnchorDepositHandler -where - B: ProposalSigningBackend, -{ - pub fn new(proposal_signing_backend: B) -> Self { - Self { - proposal_signing_backend, - } - } -} - -#[async_trait::async_trait] -impl EventHandler for OpenVAnchorDepositHandler -where - B: ProposalSigningBackend + Send + Sync, -{ - type Contract = OpenVAnchorContractWrapper; - - type Events = OpenVAnchorContractEvents; - - type Store = SledStore; - - async fn can_handle_events( - &self, - events: Self::Events, - _wrapper: &Self::Contract, - ) -> webb_relayer_utils::Result { - use OpenVAnchorContractEvents::*; - let has_event = matches!(events, NewCommitmentFilter(_)); - Ok(has_event) - } - - #[tracing::instrument(skip_all)] - async fn handle_event( - &self, - store: Arc, - wrapper: &Self::Contract, - (event, log): (Self::Events, LogMeta), - metrics: Arc>, - ) -> webb_relayer_utils::Result<()> { - use OpenVAnchorContractEvents::*; - let event_data = match event { - NewCommitmentFilter(data) => { - let chain_id = wrapper.contract.client().get_chainid().await?; - let commitment: [u8; 32] = data.commitment.into(); - let info = (data.leaf_index.as_u32(), H256::from(commitment)); - tracing::event!( - target: webb_relayer_utils::probe::TARGET, - tracing::Level::DEBUG, - kind = %webb_relayer_utils::probe::Kind::MerkleTreeInsertion, - leaf_index = %info.0, - leaf = %info.1, - chain_id = %chain_id, - block_number = %log.block_number - ); - data - } - _ => return Ok(()), - }; - - let client = wrapper.contract.client(); - let chain_id = client.get_chainid().await?; - let root: [u8; 32] = - wrapper.contract.get_last_root().call().await?.into(); - let leaf_index = event_data.leaf_index.as_u32(); - let src_chain_id = webb_proposals::TypedChainId::Evm(chain_id.as_u32()); - let src_target_system = - webb_proposals::TargetSystem::new_contract_address( - wrapper.contract.address().to_fixed_bytes(), - ); - let src_resource_id = - webb_proposals::ResourceId::new(src_target_system, src_chain_id); - - let linked_anchors = match &wrapper.config.linked_anchors { - Some(anchors) => anchors, - None => { - tracing::error!( - "Linked anchors not configured for : ({})", - chain_id - ); - return Ok(()); - } - }; - - for linked_anchor in linked_anchors { - let target_resource_id = match linked_anchor { - LinkedAnchorConfig::Raw(target) => { - let bytes: [u8; 32] = target.resource_id.into(); - webb_proposals::ResourceId::from(bytes) - } - _ => unreachable!("unsupported"), - }; - - let _ = match target_resource_id.target_system() { - webb_proposals::TargetSystem::ContractAddress(_) => { - let proposal = proposal_handler::evm_anchor_update_proposal( - root, - leaf_index, - target_resource_id, - src_resource_id, - ); - proposal_handler::handle_proposal( - &proposal, - &self.proposal_signing_backend, - metrics.clone(), - ) - .await - } - webb_proposals::TargetSystem::Substrate(_) => { - let proposal = - proposal_handler::substrate_anchor_update_proposal( - root, - leaf_index, - target_resource_id, - src_resource_id, - ); - proposal_handler::handle_proposal( - &proposal, - &self.proposal_signing_backend, - metrics.clone(), - ) - .await - } - }; - } - // mark this event as processed. - let events_bytes = serde_json::to_vec(&event_data)?; - store.store_event(&events_bytes)?; - Ok(()) - } -} diff --git a/event-watchers/evm/src/open_vanchor/open_vanchor_leaves_handler.rs b/event-watchers/evm/src/open_vanchor/open_vanchor_leaves_handler.rs deleted file mode 100644 index f5e249f42..000000000 --- a/event-watchers/evm/src/open_vanchor/open_vanchor_leaves_handler.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2022 Webb Technologies Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{HttpProvider, OpenVAnchorContractWrapper}; -use ethereum_types::H256; -use std::sync::Arc; -use tokio::sync::Mutex; -use webb::evm::contract::protocol_solidity::OpenVAnchorContractEvents; -use webb::evm::ethers::prelude::{LogMeta, Middleware}; -use webb_event_watcher_traits::evm::EventHandler; -use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; -use webb_relayer_store::SledStore; -use webb_relayer_store::{EventHashStore, LeafCacheStore}; -use webb_relayer_utils::metric; -/// An VAnchor Leaves Handler that handles `NewCommitment` events and saves the leaves to the store. -/// It serves as a cache for leaves that could be used by dApp for proof generation. -#[derive(Copy, Clone, Debug, Default)] -pub struct OpenVAnchorLeavesHandler; - -#[async_trait::async_trait] -impl EventHandler for OpenVAnchorLeavesHandler { - type Contract = OpenVAnchorContractWrapper; - - type Events = OpenVAnchorContractEvents; - - type Store = SledStore; - - async fn can_handle_events( - &self, - events: Self::Events, - _wrapper: &Self::Contract, - ) -> webb_relayer_utils::Result { - use OpenVAnchorContractEvents::*; - let has_event = matches!(events, NewCommitmentFilter(_)); - Ok(has_event) - } - - #[tracing::instrument(skip_all)] - async fn handle_event( - &self, - store: Arc, - wrapper: &Self::Contract, - (event, log): (Self::Events, LogMeta), - _metrics: Arc>, - ) -> webb_relayer_utils::Result<()> { - use OpenVAnchorContractEvents::*; - match event { - NewCommitmentFilter(deposit) => { - let commitment: [u8; 32] = deposit.commitment.into(); - let leaf_index = deposit.leaf_index.as_u32(); - let value = (leaf_index, commitment.to_vec()); - let chain_id = wrapper.contract.client().get_chainid().await?; - let target_system = TargetSystem::new_contract_address( - wrapper.contract.address().to_fixed_bytes(), - ); - let typed_chain_id = TypedChainId::Evm(chain_id.as_u32()); - let history_store_key = - ResourceId::new(target_system, typed_chain_id); - store.insert_leaves(history_store_key, &[value.clone()])?; - store.insert_last_deposit_block_number( - history_store_key, - log.block_number.as_u64(), - )?; - let events_bytes = serde_json::to_vec(&deposit)?; - store.store_event(&events_bytes)?; - tracing::trace!( - %log.block_number, - "detected block number", - ); - tracing::event!( - target: webb_relayer_utils::probe::TARGET, - tracing::Level::DEBUG, - kind = %webb_relayer_utils::probe::Kind::LeavesStore, - leaf_index = %value.0, - leaf = %format!("{:?}", value.1), - chain_id = %chain_id, - block_number = %log.block_number - ); - } - EdgeAdditionFilter(v) => { - let merkle_root: [u8; 32] = v.merkle_root.into(); - tracing::debug!( - "Edge Added of chain {} at index {} with root 0x{}", - v.chain_id, - v.latest_leaf_index, - hex::encode(merkle_root) - ); - } - EdgeUpdateFilter(v) => { - let merkle_root: [u8; 32] = v.merkle_root.into(); - tracing::debug!( - "Edge Updated of chain {} at index {} with root 0x{}", - v.chain_id, - v.latest_leaf_index, - hex::encode(merkle_root) - ); - } - NewNullifierFilter(v) => { - tracing::debug!( - "new nullifier {} found", - H256::from(&v.nullifier.into()) - ); - } - InsertionFilter(v) => { - tracing::debug!( - "Leaf {:?} inserted at index {} on time {}", - H256::from(&v.commitment.into()), - v.leaf_index, - v.timestamp - ); - } - _ => { - tracing::trace!("Unhandled event {:?}", event); - } - }; - - Ok(()) - } -} diff --git a/event-watchers/evm/src/signature_bridge_watcher.rs b/event-watchers/evm/src/signature_bridge_watcher.rs index 53eec2ac6..0566b1c57 100644 --- a/event-watchers/evm/src/signature_bridge_watcher.rs +++ b/event-watchers/evm/src/signature_bridge_watcher.rs @@ -23,24 +23,31 @@ use webb::evm::contract::protocol_solidity::{ use webb::evm::ethers::contract::Contract; use webb::evm::ethers::core::types::transaction::eip2718::TypedTransaction; use webb::evm::ethers::prelude::*; -use webb::evm::ethers::providers; use webb::evm::ethers::types; use webb::evm::ethers::utils; use webb_event_watcher_traits::evm::{ BridgeWatcher, EventHandler, EventWatcher, WatchableContract, }; +use webb_event_watcher_traits::EthersTimeLagClient; use webb_relayer_store::sled::{SledQueueKey, SledStore}; use webb_relayer_store::{BridgeCommand, QueueStore}; use webb_relayer_utils::metric; -type HttpProvider = providers::Provider; - /// A Wrapper around the `SignatureBridgeContract` contract. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct SignatureBridgeContractWrapper { config: webb_relayer_config::evm::SignatureBridgeContractConfig, - contract: SignatureBridgeContract, + contract: Arc>, +} + +impl Clone for SignatureBridgeContractWrapper { + fn clone(&self) -> Self { + Self { + config: self.config.clone(), + contract: Arc::clone(&self.contract), + } + } } impl SignatureBridgeContractWrapper { @@ -49,10 +56,10 @@ impl SignatureBridgeContractWrapper { client: Arc, ) -> Self { Self { - contract: SignatureBridgeContract::new( + contract: Arc::new(SignatureBridgeContract::new( config.common.address, client, - ), + )), config, } } @@ -97,7 +104,7 @@ pub struct SignatureBridgeGovernanceOwnershipTransferredHandler; impl EventWatcher for SignatureBridgeContractWatcher { const TAG: &'static str = "Signature Bridge Watcher"; - type Contract = SignatureBridgeContractWrapper; + type Contract = SignatureBridgeContractWrapper; type Events = SignatureBridgeContractEvents; @@ -106,7 +113,7 @@ impl EventWatcher for SignatureBridgeContractWatcher { #[async_trait::async_trait] impl EventHandler for SignatureBridgeGovernanceOwnershipTransferredHandler { - type Contract = SignatureBridgeContractWrapper; + type Contract = SignatureBridgeContractWrapper; type Events = SignatureBridgeContractEvents; @@ -114,7 +121,7 @@ impl EventHandler for SignatureBridgeGovernanceOwnershipTransferredHandler { async fn can_handle_events( &self, - events: Self::Events, + (events, _meta): (Self::Events, LogMeta), _wrapper: &Self::Contract, ) -> webb_relayer_utils::Result { use SignatureBridgeContractEvents::*; @@ -205,7 +212,7 @@ where async fn execute_proposal_with_signature( &self, store: Arc<::Store>, - contract: &SignatureBridgeContract, + contract: &SignatureBridgeContract, (proposal_data, signature): (Vec, Vec), ) -> webb_relayer_utils::Result<()> { let proposal_data_hex = hex::encode(&proposal_data); @@ -291,7 +298,7 @@ where async fn transfer_ownership_with_signature( &self, store: Arc<::Store>, - contract: &SignatureBridgeContract, + contract: &SignatureBridgeContract, (public_key, nonce, signature): (Vec, u32, Vec), ) -> webb_relayer_utils::Result<()> { // before doing anything, we need to do just two things: diff --git a/event-watchers/evm/src/tests.rs b/event-watchers/evm/src/tests.rs index 12d8ab809..526e31862 100644 --- a/event-watchers/evm/src/tests.rs +++ b/event-watchers/evm/src/tests.rs @@ -10,6 +10,8 @@ use arkworks_utils::{bytes_vec_to_f, parse_vec}; // merkle tree test #[test] fn test_merkle_root() { + type Smt = SparseMerkleTree, 30>; + let relayer_leaves = vec![ "0x017dc570cb5c6807dbaa475c9d4e445ac95a73400692541c367786c009c844cf", "0x04568790fcfc67d855dfb60de6844f6d82f4b8dc6dd0115f9f04ece21ebffb8d", @@ -23,7 +25,6 @@ fn test_merkle_root() { .enumerate() .map(|(i, l)| (i as u32, *l)) .collect(); - type Smt = SparseMerkleTree, 30>; let default_leaf_hex = vec![ "0x2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c", ]; diff --git a/event-watchers/evm/src/vanchor/vanchor_deposit_handler.rs b/event-watchers/evm/src/vanchor/vanchor_deposit_handler.rs index afe612e1f..36222a745 100644 --- a/event-watchers/evm/src/vanchor/vanchor_deposit_handler.rs +++ b/event-watchers/evm/src/vanchor/vanchor_deposit_handler.rs @@ -12,44 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::VAnchorContractWrapper; use ethereum_types::H256; use std::sync::Arc; use tokio::sync::Mutex; use webb::evm::contract::protocol_solidity::VAnchorContractEvents; -use webb::evm::ethers::prelude::{LogMeta, Middleware}; +use webb::evm::ethers::prelude::LogMeta; +use webb::evm::ethers::types; +use webb_bridge_registry_backends::BridgeRegistryBackend; use webb_event_watcher_traits::evm::EventHandler; -use webb_proposal_signing_backends::{ - proposal_handler, ProposalSigningBackend, +use webb_event_watcher_traits::EthersTimeLagClient; +use webb_proposal_signing_backends::proposal_handler; +use webb_proposal_signing_backends::queue::policy::ProposalPolicy; +use webb_proposal_signing_backends::queue::{ + ProposalsQueue, QueuedAnchorUpdateProposal, }; use webb_relayer_config::anchor::LinkedAnchorConfig; -use webb_relayer_store::EventHashStore; use webb_relayer_store::SledStore; +use webb_relayer_store::{EventHashStore, HistoryStore}; use webb_relayer_utils::metric; -use crate::{HttpProvider, VAnchorContractWrapper}; - /// Represents an VAnchor Contract Watcher which will use a configured signing backend for signing proposals. -pub struct VAnchorDepositHandler { - proposal_signing_backend: B, -} - -impl VAnchorDepositHandler -where - B: ProposalSigningBackend, -{ - pub fn new(proposal_signing_backend: B) -> Self { - Self { - proposal_signing_backend, - } - } +#[derive(typed_builder::TypedBuilder)] +pub struct VAnchorDepositHandler { + #[builder(setter(into))] + chain_id: types::U256, + #[builder(setter(into))] + store: Arc, + proposals_queue: Q, + policy: P, + bridge_registry_backend: C, } #[async_trait::async_trait] -impl EventHandler for VAnchorDepositHandler +impl EventHandler for VAnchorDepositHandler where - B: ProposalSigningBackend + Send + Sync, + Q: ProposalsQueue + Send + Sync, + P: ProposalPolicy + Send + Sync + Clone, + C: BridgeRegistryBackend + Send + Sync, { - type Contract = VAnchorContractWrapper; + type Contract = VAnchorContractWrapper; type Events = VAnchorContractEvents; @@ -57,12 +59,33 @@ where async fn can_handle_events( &self, - events: Self::Events, - _wrapper: &Self::Contract, + (events, meta): (Self::Events, LogMeta), + wrapper: &Self::Contract, ) -> webb_relayer_utils::Result { use VAnchorContractEvents::*; - let has_event = matches!(events, NewCommitmentFilter(_)); - Ok(has_event) + let has_event = matches!(events, NewCommitmentFilter(event_data) if event_data.leaf_index.as_u32() % 2 != 0); + if !has_event { + return Ok(false); + } + // only handle events if we fully synced. + let src_chain_id = + webb_proposals::TypedChainId::Evm(self.chain_id.as_u32()); + let src_target_system = + webb_proposals::TargetSystem::new_contract_address( + wrapper.contract.address().to_fixed_bytes(), + ); + let history_key = + webb_proposals::ResourceId::new(src_target_system, src_chain_id); + let target_block_number = self + .store + .get_target_block_number(history_key, meta.block_number.as_u64())?; + let event_block = meta.block_number.as_u64(); + let allowed_margin = 10u64; + // Check if the event is in the latest block or within the allowed margin. + let fully_synced = event_block >= target_block_number + || event_block.saturating_add(allowed_margin) + >= target_block_number; + Ok(fully_synced) } #[tracing::instrument(skip_all)] @@ -74,10 +97,8 @@ where metrics: Arc>, ) -> webb_relayer_utils::Result<()> { use VAnchorContractEvents::*; - let metrics_clone = metrics.clone(); let event_data = match event { NewCommitmentFilter(data) => { - let chain_id = wrapper.contract.client().get_chainid().await?; let commitment: [u8; 32] = data.commitment.into(); let info = (data.leaf_index.as_u32(), H256::from(commitment)); tracing::event!( @@ -86,7 +107,7 @@ where kind = %webb_relayer_utils::probe::Kind::MerkleTreeInsertion, leaf_index = %info.0, leaf = %info.1, - chain_id = %chain_id, + chain_id = %self.chain_id, block_number = %log.block_number ); data @@ -112,12 +133,11 @@ where return Ok(()); } - let client = wrapper.contract.client(); - let chain_id = client.get_chainid().await?; let root: [u8; 32] = wrapper.contract.get_last_root().call().await?.into(); let leaf_index = event_data.leaf_index.as_u32(); - let src_chain_id = webb_proposals::TypedChainId::Evm(chain_id.as_u32()); + let src_chain_id = + webb_proposals::TypedChainId::Evm(self.chain_id.as_u32()); let src_target_system = webb_proposals::TargetSystem::new_contract_address( wrapper.contract.address().to_fixed_bytes(), @@ -125,17 +145,13 @@ where let src_resource_id = webb_proposals::ResourceId::new(src_target_system, src_chain_id); - let linked_anchors = match &wrapper.config.linked_anchors { - Some(anchors) => anchors, - None => { - tracing::error!( - "Linked anchors not configured for : ({})", - chain_id - ); - return Ok(()); - } - }; - + let linked_anchors = self + .bridge_registry_backend + .config_or_dkg_bridges( + &wrapper.config.linked_anchors, + &src_resource_id, + ) + .await?; for linked_anchor in linked_anchors { let target_resource_id = match linked_anchor { LinkedAnchorConfig::Raw(target) => { @@ -147,37 +163,29 @@ where // Anchor update proposal proposed metric metrics.lock().await.anchor_update_proposals.inc(); - let _ = match target_resource_id.target_system() { + let proposal = match target_resource_id.target_system() { webb_proposals::TargetSystem::ContractAddress(_) => { - let proposal = proposal_handler::evm_anchor_update_proposal( + let p = proposal_handler::evm_anchor_update_proposal( root, leaf_index, target_resource_id, src_resource_id, ); - proposal_handler::handle_proposal( - &proposal, - &self.proposal_signing_backend, - metrics_clone.clone(), - ) - .await + QueuedAnchorUpdateProposal::new(p) } webb_proposals::TargetSystem::Substrate(_) => { - let proposal = - proposal_handler::substrate_anchor_update_proposal( - root, - leaf_index, - target_resource_id, - src_resource_id, - ); - proposal_handler::handle_proposal( - &proposal, - &self.proposal_signing_backend, - metrics_clone.clone(), - ) - .await + let p = proposal_handler::substrate_anchor_update_proposal( + root, + leaf_index, + target_resource_id, + src_resource_id, + ); + QueuedAnchorUpdateProposal::new(p) } }; + + self.proposals_queue + .enqueue(proposal, self.policy.clone())?; } // mark this event as processed. let events_bytes = serde_json::to_vec(&event_data)?; diff --git a/event-watchers/evm/src/vanchor/vanchor_encrypted_outputs_handler.rs b/event-watchers/evm/src/vanchor/vanchor_encrypted_outputs_handler.rs index 9ef845271..7dcf3334d 100644 --- a/event-watchers/evm/src/vanchor/vanchor_encrypted_outputs_handler.rs +++ b/event-watchers/evm/src/vanchor/vanchor_encrypted_outputs_handler.rs @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{HttpProvider, VAnchorContractWrapper}; +use super::VAnchorContractWrapper; use ethereum_types::H256; use std::sync::Arc; use tokio::sync::Mutex; use webb::evm::contract::protocol_solidity::VAnchorContractEvents; -use webb::evm::ethers::prelude::{LogMeta, Middleware}; +use webb::evm::ethers::prelude::LogMeta; +use webb::evm::ethers::types; use webb_event_watcher_traits::evm::EventHandler; +use webb_event_watcher_traits::EthersTimeLagClient; use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; use webb_relayer_store::SledStore; use webb_relayer_store::{EncryptedOutputCacheStore, EventHashStore}; @@ -26,12 +28,20 @@ use webb_relayer_utils::metric; /// An Encrypted Output Handler that handles `NewCommitment` events and saves the encrypted_output to the store. /// It serves as a cache for encrypted_output that could be used by dApp for proof generation. -#[derive(Copy, Clone, Debug, Default)] -pub struct VAnchorEncryptedOutputHandler; +#[derive(Copy, Clone, Debug)] +pub struct VAnchorEncryptedOutputHandler { + chain_id: types::U256, +} + +impl VAnchorEncryptedOutputHandler { + pub fn new(chain_id: types::U256) -> Self { + Self { chain_id } + } +} #[async_trait::async_trait] impl EventHandler for VAnchorEncryptedOutputHandler { - type Contract = VAnchorContractWrapper; + type Contract = VAnchorContractWrapper; type Events = VAnchorContractEvents; @@ -39,7 +49,7 @@ impl EventHandler for VAnchorEncryptedOutputHandler { async fn can_handle_events( &self, - events: Self::Events, + (events, _meta): (Self::Events, LogMeta), _wrapper: &Self::Contract, ) -> webb_relayer_utils::Result { use VAnchorContractEvents::*; @@ -61,20 +71,17 @@ impl EventHandler for VAnchorEncryptedOutputHandler { let encrypted_output_bytes = deposit.encrypted_output.clone(); let encrypted_output = deposit.encrypted_output.to_vec(); let encrypted_output_index = deposit.leaf_index.as_u32(); - let value = (encrypted_output_index, encrypted_output.clone()); - let chain_id = wrapper.contract.client().get_chainid().await?; + let value = (encrypted_output_index, encrypted_output); let target_system = TargetSystem::new_contract_address( wrapper.contract.address().to_fixed_bytes(), ); - let typed_chain_id = TypedChainId::Evm(chain_id.as_u32()); + let typed_chain_id = TypedChainId::Evm(self.chain_id.as_u32()); let history_store_key = ResourceId::new(target_system, typed_chain_id); - store.insert_encrypted_output( + + store.insert_encrypted_output_and_last_deposit_block_number( history_store_key, &[value.clone()], - )?; - store.insert_last_deposit_block_number_for_encrypted_output( - history_store_key, log.block_number.as_u64(), )?; let events_bytes = serde_json::to_vec(&deposit)?; @@ -88,8 +95,8 @@ impl EventHandler for VAnchorEncryptedOutputHandler { tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::EncryptedOutputStore, encrypted_output_index = %value.0, - encrypted_output = %encrypted_output_bytes, - chain_id = %chain_id, + encrypted_output = %hex::encode(encrypted_output_bytes), + chain_id = %self.chain_id, block_number = %log.block_number ); } diff --git a/event-watchers/evm/src/vanchor/vanchor_leaves_handler.rs b/event-watchers/evm/src/vanchor/vanchor_leaves_handler.rs index 0c48b321c..b9b8c5ae2 100644 --- a/event-watchers/evm/src/vanchor/vanchor_leaves_handler.rs +++ b/event-watchers/evm/src/vanchor/vanchor_leaves_handler.rs @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{HttpProvider, VAnchorContractWrapper}; +use super::VAnchorContractWrapper; +use ark_bn254::Fr as Bn254Fr; use ark_ff::{BigInteger, PrimeField}; +use arkworks_native_gadgets::merkle_tree::SparseMerkleTree; use arkworks_native_gadgets::poseidon::Poseidon; use arkworks_setups::common::setup_params; use arkworks_setups::Curve; @@ -23,15 +25,15 @@ use std::collections::BTreeMap; use std::sync::Arc; use tokio::sync::Mutex; use webb::evm::contract::protocol_solidity::VAnchorContractEvents; -use webb::evm::ethers::prelude::{LogMeta, Middleware}; +use webb::evm::ethers::prelude::LogMeta; +use webb::evm::ethers::types; use webb_event_watcher_traits::evm::EventHandler; +use webb_event_watcher_traits::EthersTimeLagClient; use webb_proposals::{ResourceId, TargetSystem, TypedChainId}; +use webb_relayer_store::SledStore; use webb_relayer_store::{EventHashStore, LeafCacheStore}; -use webb_relayer_store::{HistoryStore, SledStore}; use webb_relayer_utils::metric; - -use ark_bn254::Fr as Bn254Fr; -use arkworks_native_gadgets::merkle_tree::SparseMerkleTree; +use webb_relayer_utils::Error; /// An VAnchor Leaves Handler that handles `NewCommitment` events and saves the leaves to the store. /// It serves as a cache for leaves that could be used by dApp for proof generation. @@ -40,28 +42,66 @@ type MerkleTree = SparseMerkleTree, 30>; pub struct VAnchorLeavesHandler { mt: Arc>, - storage: Arc, + hasher: Poseidon, + chain_id: types::U256, } + impl VAnchorLeavesHandler { - pub fn new(storage: Arc, default_leaf: Vec) -> Self { + /// Creates a new Leaves Handler for the given contract address. + /// on the given chain id. + /// + /// Using the storage, it will try to load any old leaves and + /// construct the merkle tree in memory. + pub fn new( + chain_id: types::U256, + contract_address: types::Address, + storage: Arc, + empty_leaf: Vec, + ) -> webb_relayer_utils::Result { let params = setup_params::(Curve::Bn254, 5, 3); let poseidon = Poseidon::::new(params); - let default_leaf_scalar: Vec = - bytes_vec_to_f(&vec![default_leaf]); - let default_leaf_vec = default_leaf_scalar[0].into_repr().to_bytes_be(); - let pairs: BTreeMap = BTreeMap::new(); - let mt = MerkleTree::new(&pairs, &poseidon, &default_leaf_vec).unwrap(); + let empty_leaf_scalar: Vec = bytes_vec_to_f(&vec![empty_leaf]); + let empty_leaf_vec = empty_leaf_scalar + .get(0) + .map(|d| d.into_repr().to_bytes_be()) + .ok_or(webb_relayer_utils::Error::ConvertLeafScalarError)?; - Self { - mt: Arc::new(Mutex::new(mt)), - storage, + let target_system = TargetSystem::new_contract_address( + contract_address.to_fixed_bytes(), + ); + let typed_chain_id = TypedChainId::Evm(chain_id.as_u32()); + let history_store_key = ResourceId::new(target_system, typed_chain_id); + // Load all the old leaves + let leaves = storage.get_leaves(history_store_key)?; + let mut batch: BTreeMap = BTreeMap::new(); + for (i, leaf) in leaves.into_iter() { + tracing::trace!( + leaf_index = i, + leaf = hex::encode(leaf.as_bytes()), + "Inserting leaf into merkle tree", + ); + + let leaf: Bn254Fr = + Bn254Fr::from_be_bytes_mod_order(leaf.as_bytes()); + batch.insert(i as _, leaf); } + let mt = MerkleTree::new(&batch, &poseidon, &empty_leaf_vec)?; + tracing::debug!( + root = hex::encode(mt.root().into_repr().to_bytes_be()), + "Loaded merkle tree from store", + ); + + Ok(Self { + chain_id, + mt: Arc::new(Mutex::new(mt)), + hasher: poseidon, + }) } } #[async_trait::async_trait] impl EventHandler for VAnchorLeavesHandler { - type Contract = VAnchorContractWrapper; + type Contract = VAnchorContractWrapper; type Events = VAnchorContractEvents; @@ -69,77 +109,12 @@ impl EventHandler for VAnchorLeavesHandler { async fn can_handle_events( &self, - events: Self::Events, - wrapper: &Self::Contract, + (events, _log): (Self::Events, LogMeta), + _wrapper: &Self::Contract, ) -> webb_relayer_utils::Result { use VAnchorContractEvents::*; - // In this we will validate leaf/commitment we are trying to save is valid with following steps - // 1. We create a local merkle tree and insert leaf to it. - // 2. Compute merkle root for tree - // 3. Check if computed root is known root on contract - // 4. If it fails to validate we restart syncing events - match events { - NewCommitmentFilter(event_data) => { - let leaf_index = event_data.leaf_index.as_u32(); - let commitment: [u8; 32] = event_data.commitment.into(); - - let leaf: Bn254Fr = - Bn254Fr::from_be_bytes_mod_order(commitment.as_slice()); - let mut batch: BTreeMap = BTreeMap::new(); - let params = setup_params::(Curve::Bn254, 5, 3); - let poseidon = Poseidon::::new(params); - batch.insert(leaf_index, leaf); - let mut mt = self.mt.lock().await; - mt.insert_batch(&batch, &poseidon)?; - let root_bytes = mt.root().into_repr().to_bytes_be(); - let root: U256 = U256::from_big_endian(root_bytes.as_slice()); - let is_known_root = - wrapper.contract.is_known_root(root).call().await?; - tracing::trace!("Is known root: {:?}", is_known_root); - if event_data.leaf_index.as_u32() % 2 == 0 { - return Ok(true); - } - if !is_known_root { - tracing::warn!("Invalid merkle root .. Restarting"); - // In case of invalid merkle root, relayer should clear its storage and restart syncing. - // 1. We define history store key which will be used to access storage. - let chain_id = - wrapper.contract.client().get_chainid().await?; - let target_system = TargetSystem::new_contract_address( - wrapper.contract.address().to_fixed_bytes(), - ); - let typed_chain_id = TypedChainId::Evm(chain_id.as_u32()); - let history_store_key = - ResourceId::new(target_system, typed_chain_id); - - // 2. Clear merkle tree on relayer. - tracing::warn!("Clear merkle tree...!"); - mt.tree.clear(); - // 3. Clear LeafStore cache on relayer. - tracing::warn!("Clear local leaves cache...!"); - self.storage.clear_leaves_cache(history_store_key)?; - // 4. Reset last block no processed by leaf handler. - tracing::warn!("Clear last deposit block number...!"); - self.storage.insert_last_deposit_block_number( - history_store_key, - 0, - )?; - - // 5. Reset last saved block number by event watcher. - // Relayer will restart syncing from block number contract was deployed at. - tracing::warn!( - "Reset block number to contract deployed at...!" - ); - self.storage.set_last_block_number( - history_store_key, - wrapper.config.common.deployed_at, - )?; - return Ok(true); - } - return Ok(true); - } - _ => return Ok(false), - }; + let has_event = matches!(events, NewCommitmentFilter(_)); + Ok(has_event) } #[tracing::instrument(skip_all)] @@ -151,24 +126,70 @@ impl EventHandler for VAnchorLeavesHandler { _metrics: Arc>, ) -> webb_relayer_utils::Result<()> { use VAnchorContractEvents::*; + let mut batch: BTreeMap = BTreeMap::new(); + let mut mt = self.mt.lock().await; + // We will clone the tree to compare it with the new one. + let mt_snapshot = mt.tree.clone(); + match event { - NewCommitmentFilter(deposit) => { - let commitment: [u8; 32] = deposit.commitment.into(); - let leaf_index = deposit.leaf_index.as_u32(); + NewCommitmentFilter(event_data) => { + let commitment: [u8; 32] = event_data.commitment.into(); + let leaf_index = event_data.leaf_index.as_u32(); let value = (leaf_index, commitment.to_vec()); - let chain_id = wrapper.contract.client().get_chainid().await?; let target_system = TargetSystem::new_contract_address( wrapper.contract.address().to_fixed_bytes(), ); - let typed_chain_id = TypedChainId::Evm(chain_id.as_u32()); + let typed_chain_id = TypedChainId::Evm(self.chain_id.as_u32()); let history_store_key = ResourceId::new(target_system, typed_chain_id); - store.insert_leaves(history_store_key, &[value.clone()])?; - store.insert_last_deposit_block_number( + + // 1. We will validate leaf before inserting it into store + let leaf: Bn254Fr = + Bn254Fr::from_be_bytes_mod_order(commitment.as_slice()); + batch.insert(leaf_index, leaf); + mt.insert_batch(&batch, &self.hasher)?; + // If leaf index is even number then we don't need to verify commitment + if event_data.leaf_index.as_u32() % 2 == 0 { + tracing::debug!( + leaf_index = leaf_index, + commitment = hex::encode(commitment.as_slice()), + "Verified commitment", + ); + } else { + // We will verify commitment + let root_bytes = mt.root().into_repr().to_bytes_be(); + let root = U256::from_big_endian(root_bytes.as_slice()); + let is_known_root = wrapper + .contract + .is_known_root(root) + .block(log.block_number) + .call() + .await?; + + tracing::debug!( + leaf_index = leaf_index, + root = hex::encode(root_bytes.as_slice()), + is_known_root, + "New commitment need to be verified", + ); + + if !is_known_root { + tracing::warn!( + expected_root = ?root, + "Invalid merkle root. Maybe invalid leaf or commitment" + ); + // Restore previous state of the tree. + mt.tree = mt_snapshot; + return Err(Error::InvalidMerkleRootError(leaf_index)); + } + } + // 2. We will insert leaf and last deposit block number into store + store.insert_leaves_and_last_deposit_block_number( history_store_key, + &[value.clone()], log.block_number.as_u64(), )?; - let events_bytes = serde_json::to_vec(&deposit)?; + let events_bytes = serde_json::to_vec(&event_data)?; store.store_event(&events_bytes)?; tracing::trace!( %log.block_number, @@ -179,8 +200,8 @@ impl EventHandler for VAnchorLeavesHandler { tracing::Level::DEBUG, kind = %webb_relayer_utils::probe::Kind::LeavesStore, leaf_index = %value.0, - leaf = %format!("{:?}", value.1), - chain_id = %chain_id, + leaf = %hex::encode(value.1), + chain_id = %self.chain_id, block_number = %log.block_number ); } diff --git a/event-watchers/substrate/Cargo.toml b/event-watchers/substrate/Cargo.toml index 88c6192d0..30b70e23e 100644 --- a/event-watchers/substrate/Cargo.toml +++ b/event-watchers/substrate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-ew-substrate" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -12,6 +12,7 @@ repository = { workspace = true } [dependencies] webb-proposal-signing-backends = { workspace = true } +webb-bridge-registry-backends = { workspace = true } webb-event-watcher-traits = { workspace = true } webb-relayer-types = { workspace = true } webb-relayer-store = { workspace = true } @@ -29,3 +30,4 @@ sp-core = { workspace = true } native-tls = { workspace = true, optional = true } webb-proposals = { workspace = true } libsecp256k1 = { workspace = true } +typed-builder = { workspace = true } diff --git a/event-watchers/substrate/src/lib.rs b/event-watchers/substrate/src/lib.rs index c7862bda0..9aa95f951 100644 --- a/event-watchers/substrate/src/lib.rs +++ b/event-watchers/substrate/src/lib.rs @@ -27,8 +27,8 @@ pub use vanchor_encrypted_output_handler::*; pub use vanchor_leaves_handler::*; use webb::substrate::subxt::events::StaticEvent; use webb::substrate::{ - protocol_substrate_runtime::api::v_anchor_bn254::events::Transaction, - subxt::{OnlineClient, SubstrateConfig}, + subxt::PolkadotConfig, + tangle_runtime::api::v_anchor_bn254::events::Transaction, }; use webb_event_watcher_traits::SubstrateEventWatcher; use webb_relayer_store::SledStore; @@ -37,12 +37,10 @@ use webb_relayer_store::SledStore; pub struct SubstrateVAnchorEventWatcher; #[async_trait::async_trait] -impl SubstrateEventWatcher for SubstrateVAnchorEventWatcher { +impl SubstrateEventWatcher for SubstrateVAnchorEventWatcher { const TAG: &'static str = "Substrate VAnchor Event Watcher"; const PALLET_NAME: &'static str = Transaction::PALLET; - type Client = OnlineClient; - type Store = SledStore; } diff --git a/event-watchers/substrate/src/signature_bridge_watcher.rs b/event-watchers/substrate/src/signature_bridge_watcher.rs index ddb153467..3efba293a 100644 --- a/event-watchers/substrate/src/signature_bridge_watcher.rs +++ b/event-watchers/substrate/src/signature_bridge_watcher.rs @@ -16,27 +16,27 @@ use std::sync::Arc; use tokio::sync::Mutex; use sp_core::hashing::keccak_256; -use webb::substrate::subxt::config::SubstrateConfig; +use webb::substrate::subxt::config::PolkadotConfig; use webb::substrate::subxt::events::StaticEvent; -use webb::substrate::subxt::{self, dynamic::Value, OnlineClient}; - -use webb::substrate::protocol_substrate_runtime::api::signature_bridge::calls::{ExecuteProposal,SetMaintainer}; -use webb_event_watcher_traits::substrate::{SubstrateBridgeWatcher, EventHandler}; +use sp_core::sr25519::Pair as Sr25519Pair; +use webb::substrate::subxt::{self, OnlineClient}; +use webb_event_watcher_traits::substrate::{ + EventHandler, SubstrateBridgeWatcher, +}; use webb_event_watcher_traits::SubstrateEventWatcher; -use webb_relayer_store::sled::{SledQueueKey,SledStore}; +use webb_relayer_store::sled::{SledQueueKey, SledStore}; use webb_relayer_store::{BridgeCommand, QueueStore}; use webb::evm::ethers::utils; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; -use webb::substrate::protocol_substrate_runtime::api::signature_bridge::events::MaintainerSet; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::signature_bridge::events::MaintainerSet; -use std::borrow::Cow; use webb::substrate::scale::Encode; -use webb_relayer_types::dynamic_payload::WebbDynamicTxPayload; -use webb_relayer_utils::metric; +use webb_relayer_utils::static_tx_payload::TypeErasedStaticTxPayload; +use webb_relayer_utils::{metric, Error}; -use webb::substrate::protocol_substrate_runtime::api::runtime_types::sp_core::bounded::bounded_vec::BoundedVec; +use webb::substrate::tangle_runtime::api::runtime_types::bounded_collections::bounded_vec::BoundedVec; /// A MaintainerSetEvent handler handles `MaintainerSet` events and signals signature bridge watcher /// to remove pending tx trying to do governor transfer. @@ -44,14 +44,14 @@ use webb::substrate::protocol_substrate_runtime::api::runtime_types::sp_core::bo pub struct MaintainerSetEventHandler; #[async_trait::async_trait] -impl EventHandler for MaintainerSetEventHandler { - type Client = OnlineClient; +impl EventHandler for MaintainerSetEventHandler { + type Client = OnlineClient; type Store = SledStore; async fn can_handle_events( &self, - events: subxt::events::Events, + events: subxt::events::Events, ) -> webb_relayer_utils::Result { let has_event = events.has::()?; Ok(has_event) @@ -61,7 +61,7 @@ impl EventHandler for MaintainerSetEventHandler { &self, _store: Arc, _client: Arc, - (events, _block_number): (subxt::events::Events, u64), + (events, _block_number): (subxt::events::Events, u64), _metrics: Arc>, ) -> webb_relayer_utils::Result<()> { // todo @@ -95,21 +95,21 @@ impl EventHandler for MaintainerSetEventHandler { #[derive(Copy, Clone, Debug, Default)] pub struct SubstrateBridgeEventWatcher; -impl SubstrateEventWatcher for SubstrateBridgeEventWatcher { +impl SubstrateEventWatcher for SubstrateBridgeEventWatcher { const TAG: &'static str = "Substrate bridge pallet Watcher"; const PALLET_NAME: &'static str = MaintainerSet::PALLET; - type Client = OnlineClient; type Store = SledStore; } #[async_trait::async_trait] -impl SubstrateBridgeWatcher for SubstrateBridgeEventWatcher { +impl SubstrateBridgeWatcher for SubstrateBridgeEventWatcher { #[tracing::instrument(skip_all)] async fn handle_cmd( &self, chain_id: u32, store: Arc, - client: Arc, + client: Arc>, + _pair: Sr25519Pair, cmd: BridgeCommand, ) -> webb_relayer_utils::Result<()> { use BridgeCommand::*; @@ -144,14 +144,14 @@ impl SubstrateBridgeWatcher for SubstrateBridgeEventWatcher { impl SubstrateBridgeEventWatcher where - Self: SubstrateBridgeWatcher, + Self: SubstrateBridgeWatcher, { #[tracing::instrument(skip_all)] async fn execute_proposal_with_signature( &self, chain_id: u32, - store: Arc<>::Store>, - api: Arc<>::Client>, + store: Arc<>::Store>, + api: Arc>, (proposal_data, signature): (Vec, Vec), ) -> webb_relayer_utils::Result<()> { let proposal_data_hex = hex::encode(&proposal_data); @@ -159,7 +159,7 @@ where if proposal_data.len() < 40 { tracing::warn!( proposal_data = ?proposal_data_hex, - "Skipping execution of this proposal : Invalid Proposal", + "Skipping execution of this proposal: Invalid Proposal", ); return Ok(()); } @@ -177,7 +177,7 @@ where .await? .fetch(¤t_maintainer_addrs) .await? - .unwrap() + .ok_or(Error::ReadSubstrateStorageError)? .0; // Verify proposal signature @@ -210,37 +210,25 @@ where let typed_chain_id = webb_proposals::TypedChainId::Substrate(chain_id); - // Enqueue transaction call data in protocol-substrate transaction queue - let execute_proposal_call = ExecuteProposal { - src_id: typed_chain_id.chain_id(), - proposal_data: BoundedVec(proposal_data.clone()), - signature: BoundedVec(signature.clone()), - }; - // webb dynamic payload - let execute_proposal_tx = WebbDynamicTxPayload { - pallet_name: Cow::Borrowed("SignatureBridge"), - call_name: Cow::Borrowed("execute_proposal"), - fields: vec![ - Value::u128(typed_chain_id.chain_id() as u128), - Value::from_bytes(proposal_data), - Value::from_bytes(signature), - ], - }; + let execute_proposal_tx = + RuntimeApi::tx().signature_bridge().execute_proposal( + typed_chain_id.chain_id(), + BoundedVec(proposal_data), + BoundedVec(signature), + ); - let data_hash = utils::keccak256(execute_proposal_call.encode()); + // Enqueue transaction in protocol-substrate transaction queue + let data_hash = + utils::keccak256(execute_proposal_tx.call_data().encode()); let tx_key = SledQueueKey::from_substrate_with_custom_key( chain_id, make_execute_proposal_key(data_hash), ); - // Enqueue WebbDynamicTxPayload in protocol-substrate transaction queue - QueueStore::::enqueue_item( - &store, - tx_key, - execute_proposal_tx, - )?; + let tx = TypeErasedStaticTxPayload::try_from(execute_proposal_tx)?; + QueueStore::enqueue_item(&store, tx_key, tx)?; tracing::debug!( data_hash = ?hex::encode(data_hash), - "Enqueued execute-proposal call for execution through protocol-substrate tx queue", + "Enqueued execute-proposal tx for execution through protocol-substrate tx queue", ); Ok(()) } @@ -249,8 +237,8 @@ where async fn transfer_ownership_with_signature( &self, chain_id: u32, - store: Arc<>::Store>, - api: Arc<>::Client>, + store: Arc<>::Store>, + api: Arc>, (public_key, nonce, signature): (Vec, u32, Vec), ) -> webb_relayer_utils::Result<()> { let new_maintainer = public_key.clone(); @@ -264,7 +252,7 @@ where .await? .fetch(¤t_maintainer_addrs) .await? - .unwrap() + .ok_or(Error::ReadSubstrateStorageError)? .0; // we need to do some checks here: // 1. convert the public key to address and check it is not the same as the current maintainer. @@ -290,7 +278,7 @@ where .await? .fetch(¤t_nonce) .await? - .unwrap(); + .expect("fetch current nonce from storage"); if nonce <= current_nonce { tracing::warn!( @@ -314,50 +302,31 @@ where signature = %hex::encode(&signature), ); - let set_maintainer_call = SetMaintainer { - message: BoundedVec(new_maintainer.clone()), - signature: BoundedVec(signature.clone()), - }; + let mut message = nonce.to_be_bytes().to_vec(); + message.extend_from_slice(&new_maintainer); - // webb dynamic payload - let set_maintainer_tx = WebbDynamicTxPayload { - pallet_name: Cow::Borrowed("SignatureBridge"), - call_name: Cow::Borrowed("set_maintainer"), - fields: vec![ - Value::from_bytes(new_maintainer), - Value::from_bytes(signature), - ], - }; + let set_maintainer_tx = RuntimeApi::tx() + .signature_bridge() + .set_maintainer(BoundedVec(message), BoundedVec(signature)); - let data_hash = utils::keccak256(set_maintainer_call.encode()); + let data_hash = + utils::keccak256(set_maintainer_tx.call_data().encode()); let tx_key = SledQueueKey::from_substrate_with_custom_key( chain_id, make_execute_proposal_key(data_hash), ); - // Enqueue WebbDynamicTxPayload in protocol-substrate transaction queue - QueueStore::::enqueue_item( - &store, - tx_key, - set_maintainer_tx, - )?; + + let tx = TypeErasedStaticTxPayload::try_from(set_maintainer_tx)?; + // Enqueue transaction in protocol-substrate transaction queue + QueueStore::enqueue_item(&store, tx_key, tx)?; tracing::debug!( data_hash = ?hex::encode(data_hash), - "Enqueued set-maintainer call for execution through protocol-substrate tx queue", + "Enqueued set-maintainer tx for execution through protocol-substrate tx queue", ); Ok(()) } } -pub fn parse_nonce_from_proposal_data(proposal_data: &[u8]) -> u32 { - let nonce_bytes = proposal_data[36..40].try_into().unwrap_or_default(); - u32::from_be_bytes(nonce_bytes) -} - -pub fn parse_call_from_proposal_data(proposal_data: &[u8]) -> Vec { - // Not [36..] because there are 4 byte of zero padding to match Solidity side - proposal_data[40..].to_vec() -} - pub fn validate_ecdsa_signature( data: &[u8], signature: &[u8], @@ -399,6 +368,7 @@ fn secp256k1_ecdsa_recover( fn make_execute_proposal_key(data_hash: [u8; 32]) -> [u8; 64] { let mut result = [0u8; 64]; let prefix = b"execute_proposal_with_signature_"; + debug_assert!(prefix.len() == 32); result[0..32].copy_from_slice(prefix); result[32..64].copy_from_slice(&data_hash); result diff --git a/event-watchers/substrate/src/vanchor_deposit_handler.rs b/event-watchers/substrate/src/vanchor_deposit_handler.rs index 095f43e3d..fea54a934 100644 --- a/event-watchers/substrate/src/vanchor_deposit_handler.rs +++ b/event-watchers/substrate/src/vanchor_deposit_handler.rs @@ -15,10 +15,11 @@ use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; -use webb::substrate::protocol_substrate_runtime::api::v_anchor_bn254; use webb::substrate::scale::Encode; -use webb::substrate::subxt::{self, OnlineClient, SubstrateConfig}; +use webb::substrate::subxt::{self, OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::v_anchor_bn254; +use webb_bridge_registry_backends::BridgeRegistryBackend; use webb_event_watcher_traits::substrate::EventHandler; use webb_proposal_signing_backends::{ @@ -29,38 +30,43 @@ use webb_relayer_store::EventHashStore; use webb_relayer_store::SledStore; use webb_relayer_utils::metric; /// SubstrateVAnchorDeposit handler handles `Transaction` event and creates `AnchorUpdate` proposals for linked anchors. -pub struct SubstrateVAnchorDepositHandler { +pub struct SubstrateVAnchorDepositHandler { proposal_signing_backend: B, - linked_anchors: Vec, + bridge_registry_backend: C, + linked_anchors: Option>, } -impl SubstrateVAnchorDepositHandler +impl SubstrateVAnchorDepositHandler where B: ProposalSigningBackend, + C: BridgeRegistryBackend, { pub fn new( proposal_signing_backend: B, - linked_anchors: Vec, + bridge_registry_backend: C, + linked_anchors: Option>, ) -> Self { Self { proposal_signing_backend, + bridge_registry_backend, linked_anchors, } } } #[async_trait::async_trait] -impl EventHandler for SubstrateVAnchorDepositHandler +impl EventHandler for SubstrateVAnchorDepositHandler where B: ProposalSigningBackend + Send + Sync, + C: BridgeRegistryBackend + Send + Sync, { - type Client = OnlineClient; + type Client = OnlineClient; type Store = SledStore; async fn can_handle_events( &self, - events: subxt::events::Events, + events: subxt::events::Events, ) -> webb_relayer_utils::Result { let has_event = events.has::()?; Ok(has_event) @@ -71,7 +77,7 @@ where &self, store: Arc, client: Arc, - (events, _block_number): (subxt::events::Events, u64), + (events, _block_number): (subxt::events::Events, u64), metrics: Arc>, ) -> webb_relayer_utils::Result<()> { let at_hash = events.block_hash(); @@ -130,8 +136,13 @@ where let mut merkle_root = [0; 32]; merkle_root.copy_from_slice(&root.encode()); + let linked_anchors = self + .bridge_registry_backend + .config_or_dkg_bridges(&self.linked_anchors, &src_resource_id) + .await?; + // update linked anchors - for linked_anchor in &self.linked_anchors { + for linked_anchor in linked_anchors { let target_resource_id = match linked_anchor { LinkedAnchorConfig::Raw(target) => { let bytes: [u8; 32] = target.resource_id.into(); @@ -141,7 +152,7 @@ where }; // Proposal proposed metric metrics.lock().await.anchor_update_proposals.inc(); - let _ = match target_resource_id.target_system() { + match target_resource_id.target_system() { webb_proposals::TargetSystem::ContractAddress(_) => { let proposal = proposal_handler::evm_anchor_update_proposal( @@ -155,7 +166,7 @@ where &self.proposal_signing_backend, metrics_clone.clone(), ) - .await + .await? } webb_proposals::TargetSystem::Substrate(_) => { let proposal = @@ -170,7 +181,7 @@ where &self.proposal_signing_backend, metrics_clone.clone(), ) - .await + .await? } }; } diff --git a/event-watchers/substrate/src/vanchor_encrypted_output_handler.rs b/event-watchers/substrate/src/vanchor_encrypted_output_handler.rs index ffe7eef02..2beb0c8b8 100644 --- a/event-watchers/substrate/src/vanchor_encrypted_output_handler.rs +++ b/event-watchers/substrate/src/vanchor_encrypted_output_handler.rs @@ -14,9 +14,9 @@ use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; -use webb::substrate::protocol_substrate_runtime::api::v_anchor_bn254; -use webb::substrate::subxt::{self, OnlineClient, SubstrateConfig}; +use webb::substrate::subxt::{self, OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::v_anchor_bn254; use webb_event_watcher_traits::substrate::EventHandler; use webb_proposals::{ @@ -25,21 +25,21 @@ use webb_proposals::{ use webb_relayer_store::sled::SledStore; use webb_relayer_store::EncryptedOutputCacheStore; -use webb_relayer_utils::metric; +use webb_relayer_utils::{metric, Error}; // An Substrate VAnchor encrypted output Watcher that watches for Deposit events and save the encrypted output to the store. /// It serves as a cache for encrypted outputs that could be used by dApp. #[derive(Clone, Debug, Default)] pub struct SubstrateVAnchorEncryptedOutputHandler; #[async_trait::async_trait] -impl EventHandler for SubstrateVAnchorEncryptedOutputHandler { - type Client = OnlineClient; +impl EventHandler for SubstrateVAnchorEncryptedOutputHandler { + type Client = OnlineClient; type Store = SledStore; async fn can_handle_events( &self, - events: subxt::events::Events, + events: subxt::events::Events, ) -> webb_relayer_utils::Result { let has_event = events.has::()?; Ok(has_event) @@ -49,7 +49,7 @@ impl EventHandler for SubstrateVAnchorEncryptedOutputHandler { &self, store: Arc, client: Arc, - (events, block_number): (subxt::events::Events, u64), + (events, block_number): (subxt::events::Events, u64), _metrics: Arc>, ) -> webb_relayer_utils::Result<()> { let at_hash = events.block_hash(); @@ -68,7 +68,7 @@ impl EventHandler for SubstrateVAnchorEncryptedOutputHandler { .await? .fetch(&next_leaf_index_addr) .await? - .unwrap(); + .ok_or(Error::ReadSubstrateStorageError)?; // fetch chain_id let chain_id_addr = RuntimeApi::constants() @@ -101,9 +101,9 @@ impl EventHandler for SubstrateVAnchorEncryptedOutputHandler { [event.encrypted_output1, event.encrypted_output2] { let value = (index, encrypted_output.clone()); - store.insert_encrypted_output(history_store_key, &[value])?; - store.insert_last_deposit_block_number_for_encrypted_output( + store.insert_encrypted_output_and_last_deposit_block_number( history_store_key, + &[value], block_number, )?; index += 1; diff --git a/event-watchers/substrate/src/vanchor_leaves_handler.rs b/event-watchers/substrate/src/vanchor_leaves_handler.rs index 2a68319dc..bc7f0a35c 100644 --- a/event-watchers/substrate/src/vanchor_leaves_handler.rs +++ b/event-watchers/substrate/src/vanchor_leaves_handler.rs @@ -15,9 +15,9 @@ use std::sync::Arc; use tokio::sync::Mutex; -use webb::substrate::protocol_substrate_runtime::api as RuntimeApi; -use webb::substrate::protocol_substrate_runtime::api::v_anchor_bn254; -use webb::substrate::subxt::{self, OnlineClient, SubstrateConfig}; +use webb::substrate::subxt::{self, OnlineClient, PolkadotConfig}; +use webb::substrate::tangle_runtime::api as RuntimeApi; +use webb::substrate::tangle_runtime::api::v_anchor_bn254; use webb_event_watcher_traits::substrate::EventHandler; use webb_proposals::{ @@ -25,19 +25,19 @@ use webb_proposals::{ }; use webb_relayer_store::sled::SledStore; use webb_relayer_store::LeafCacheStore; -use webb_relayer_utils::metric; +use webb_relayer_utils::{metric, Error}; // An Substrate VAnchor Leaves Watcher that watches for Deposit events and save the leaves to the store. /// It serves as a cache for leaves that could be used by dApp for proof generation. #[derive(Clone, Debug, Default)] pub struct SubstrateVAnchorLeavesHandler; #[async_trait::async_trait] -impl EventHandler for SubstrateVAnchorLeavesHandler { - type Client = OnlineClient; +impl EventHandler for SubstrateVAnchorLeavesHandler { + type Client = OnlineClient; type Store = SledStore; async fn can_handle_events( &self, - events: subxt::events::Events, + events: subxt::events::Events, ) -> webb_relayer_utils::Result { let has_event = events.has::()?; Ok(has_event) @@ -47,7 +47,7 @@ impl EventHandler for SubstrateVAnchorLeavesHandler { &self, store: Arc, api: Arc, - (events, block_number): (subxt::events::Events, u64), + (events, block_number): (subxt::events::Events, u64), _metrics: Arc>, ) -> webb_relayer_utils::Result<()> { let at_hash = events.block_hash(); @@ -66,7 +66,7 @@ impl EventHandler for SubstrateVAnchorLeavesHandler { .await? .fetch(&next_leaf_index_addr) .await? - .unwrap(); + .ok_or(Error::ReadSubstrateStorageError)?; // fetch chain_id let chain_id_addr = RuntimeApi::constants() .linkable_tree_bn254() @@ -94,9 +94,9 @@ impl EventHandler for SubstrateVAnchorLeavesHandler { let mut leaf_store = Vec::with_capacity(leaf_count); for leaf in event.leafs { let value = (leaf_index, leaf.0.to_vec()); - store.insert_leaves(history_store_key, &[value])?; - store.insert_last_deposit_block_number( + store.insert_leaves_and_last_deposit_block_number( history_store_key, + &[value], block_number, )?; leaf_index += 1; diff --git a/examples/in_depth.rs b/examples/in_depth.rs index 4fe83f55e..24460d876 100644 --- a/examples/in_depth.rs +++ b/examples/in_depth.rs @@ -77,6 +77,7 @@ async fn main() -> anyhow::Result<()> { polling_interval: 3000, max_blocks_per_step: 1000, print_progress_interval: 60_000, + sync_blocks_from: None, }, proposal_signing_backend: Some( ProposalSigningBackendConfig::Mocked( @@ -87,6 +88,7 @@ async fn main() -> anyhow::Result<()> { ), ), linked_anchors: None, + smart_anchor_updates: Default::default(), }), Contract::SignatureBridge(SignatureBridgeContractConfig { common: CommonContractConfig { @@ -99,6 +101,7 @@ async fn main() -> anyhow::Result<()> { polling_interval: 3000, max_blocks_per_step: 1000, print_progress_interval: 60_000, + sync_blocks_from: None, }, }), ], @@ -118,11 +121,11 @@ async fn main() -> anyhow::Result<()> { )?; let config = webb_relayer_config::utils::parse_from_files(&config_files)?; - // finally, after loading the config files, we can build the relayer context. - let ctx = RelayerContext::new(config); - // next is to build the store, or the storage backend: let store = webb_relayer_store::sled::SledStore::open("path/to/store")?; + + // finally, after loading the config files, we can build the relayer context. + let ctx = RelayerContext::new(config, store.clone())?; // or temporary store: let store = webb_relayer_store::sled::SledStore::temporary()?; @@ -130,13 +133,9 @@ async fn main() -> anyhow::Result<()> { // services. // Start the web server: - let (addr, web_services) = - service::build_web_services(ctx.clone(), store.clone())?; + service::build_web_services(ctx.clone()).await?; // and also the background services: // this does not block, will fire the services on background tasks. - service::ignite(&ctx, Arc::new(store)).await?; - - println!("Listening on {}", addr); - web_services.await; + service::ignite(ctx, Arc::new(store)).await?; Ok(()) } diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..e2306b58a --- /dev/null +++ b/flake.lock @@ -0,0 +1,85 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1684212024, + "narHash": "sha256-/3ZvkPuIXdyZqPR53qC7aaV5wiwMOY+ddbESOykZ9Vo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d4825e5e4ac1de7d5bb99381534fd0af3875a26d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1684203630, + "narHash": "sha256-ZOWNixdHU4qFZUgYNEULFB3ifctMQO9H4Oo+Zrz+4L8=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "65c3f2655f52a81e1b3e629d4c07df4873d0f2bb", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..ac5aeb93d --- /dev/null +++ b/flake.nix @@ -0,0 +1,67 @@ +{ + description = "Webb Relayer development environment"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + # Rust + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + lib = pkgs.lib; + toolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + in + { + devShells.default = pkgs.mkShell { + name = "relayer"; + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.clang + # Mold Linker for faster builds (only on Linux) + (lib.optionals pkgs.stdenv.isLinux pkgs.mold) + ]; + buildInputs = [ + # Used for DVC + pkgs.python311 + pkgs.python311Packages.pipx + # We want the unwrapped version, wrapped comes with nixpkgs' toolchain + pkgs.rust-analyzer-unwrapped + # Nodejs for test suite + pkgs.nodejs_18 + pkgs.nodePackages.typescript-language-server + pkgs.nodePackages.yarn + # Finally the toolchain + toolchain + ]; + packages = [ + pkgs.cargo-nextest + ]; + + # Runs DVC pull in the fixtures + # we do not install dvc globally, since it + # is broken on nixos + shellHook = '' + ROOT=$(git rev-parse --show-toplevel) + cd $ROOT/tests && pipx run dvc pull + cd $ROOT + ''; + + # Environment variables + RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; + # Needed for running DKG/Tangle locally + LD_LIBRARY_PATH = "${pkgs.gmp}/lib"; + }; + }); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e6bd1ddc0..e867523a3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly" -components = ["rustfmt", "clippy"] +channel = "nightly-2023-03-12" +components = ["rustfmt", "clippy", "rust-src"] targets = [] diff --git a/sample_seed.txt b/sample_seed.txt deleted file mode 100644 index ab52fe1bb..000000000 --- a/sample_seed.txt +++ /dev/null @@ -1 +0,0 @@ -point shiver hurt flight fun online hub antenna engine pave chef fantasy front interest poem accident catch load frequent praise elite pet remove used \ No newline at end of file diff --git a/services/block-poller/Cargo.toml b/services/block-poller/Cargo.toml index 3579189cb..b81a9c9df 100644 --- a/services/block-poller/Cargo.toml +++ b/services/block-poller/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webb-block-poller" -version = "0.1.0" +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/services/block-poller/src/block_poller.rs b/services/block-poller/src/block_poller.rs index 1e2442425..cacacb07c 100644 --- a/services/block-poller/src/block_poller.rs +++ b/services/block-poller/src/block_poller.rs @@ -2,10 +2,11 @@ use futures::prelude::*; use std::cmp; use std::sync::Arc; use std::time::Duration; +use webb_relayer::service::evm::Client; use webb_relayer_config::block_poller::BlockPollerConfig; use webb::evm::ethers::{ - providers::{self, Middleware}, + providers::Middleware, types::{Block, TxHash}, }; @@ -90,7 +91,7 @@ pub trait BlockPoller { )] async fn run( &self, - client: Arc>, + client: Arc, store: Arc, listener_config: BlockPollerConfig, handlers: Vec>, @@ -170,7 +171,7 @@ pub trait BlockPoller { // this because, for the failed events, we arleady tried to handle them // many times (at this point), and there is no point in trying again. let mark_as_handled = - result.iter().any(|r| r.is_ok()); + result.iter().any(std::result::Result::is_ok); // also, for all the failed event handlers, we should print what went // wrong. result.iter().for_each(|r| { diff --git a/services/block-poller/src/lib.rs b/services/block-poller/src/lib.rs index 2aea3c2b1..bbfbc4c59 100644 --- a/services/block-poller/src/lib.rs +++ b/services/block-poller/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::block_poller::{BlockPoller, BlockPollingHandler}; use ethereum_types::U256; use webb::evm::ethers::types::{Block, TxHash}; -use webb_relayer::service::{Client, Store}; +use webb_relayer::service::{evm::Client, Store}; use webb_relayer_config::block_poller::BlockPollerConfig; use webb_relayer_context::RelayerContext; use webb_relayer_store::SledStore; diff --git a/services/block-poller/src/main.rs b/services/block-poller/src/main.rs index a9bc2c0d3..6f0c653a6 100644 --- a/services/block-poller/src/main.rs +++ b/services/block-poller/src/main.rs @@ -36,8 +36,7 @@ pub async fn ignite( } let chain_name = &chain_config.name; let chain_id = U256::from(chain_config.chain_id); - let provider = ctx.evm_provider(&chain_id.to_string()).await?; - let client = Arc::new(provider); + let client = ctx.evm_provider(chain_id).await?; tracing::debug!( "Starting Background Services for ({}) chain. ({:?})", chain_name, @@ -84,7 +83,7 @@ async fn main(args: Opts) -> anyhow::Result<()> { // The RelayerContext takes a configuration, and populates objects that are needed // throughout the lifetime of the relayer. Items such as wallets and providers, as well // as a convenient place to access the configuration. - let ctx = RelayerContext::new(config, store.clone()); + let ctx = RelayerContext::new(config, store.clone())?; tracing::trace!("Created persistent storage.."); // the build_web_relayer command sets up routing (endpoint queries / requests mapped to handled code) // so clients can interact with the relayer diff --git a/services/light-client-relayer/Cargo.toml b/services/light-client-relayer/Cargo.toml index b1dc685a8..7dd900f7e 100644 --- a/services/light-client-relayer/Cargo.toml +++ b/services/light-client-relayer/Cargo.toml @@ -39,7 +39,7 @@ backoff = { version = "0.4.0", features = ["tokio"] } tokio = { version = "^1", features = ["full"] } serde_json = { version = "^1", default-features = false } paw = { version = "^1.0", optional = true } -webb = { git = "https://github.com/webb-tools/webb-rs" , default-features = false, features = ["evm-runtime"] } +webb = { workspace = true, default-features = false, features = ["evm-runtime"] } webb-proposals = { version = "0.5.4", default-features = false, features = ["scale", "evm", "substrate"] } # Used by ethers (but we need it to be vendored with the lib). native-tls = { version = "^0.2", features = ["vendored"], optional = true } diff --git a/services/light-client-relayer/src/main.rs b/services/light-client-relayer/src/main.rs index 19cdb2129..8515c18c8 100644 --- a/services/light-client-relayer/src/main.rs +++ b/services/light-client-relayer/src/main.rs @@ -11,7 +11,6 @@ use webb_relayer_config::{ cli::{create_store, load_config, setup_logger, Opts}, }; use webb_relayer_context::RelayerContext; -use webb_relayer_utils::Result; /// Starts all background services for all chains configured in the config file. /// @@ -21,7 +20,7 @@ use webb_relayer_utils::Result; /// /// * `ctx` - RelayContext reference that holds the configuration /// * `store` -[Sled](https://sled.rs)-based database store -pub async fn ignite(ctx: &RelayerContext) -> crate::Result<()> { +pub async fn ignite(ctx: &RelayerContext) -> anyhow::Result<()> { tracing::debug!( "Relayer configuration: {}", serde_json::to_string_pretty(&ctx.config)? @@ -34,7 +33,7 @@ pub async fn ignite(ctx: &RelayerContext) -> crate::Result<()> { } let mut chain_config = chain_config.clone(); - let api_key_string = std::env::var("ETH1_INFURA_API_KEY").unwrap(); + let api_key_string = std::env::var("ETH1_INFURA_API_KEY")?; chain_config.eth1_endpoint = chain_config .eth1_endpoint .replace("ETH1_INFURA_API_KEY", &api_key_string); @@ -79,7 +78,7 @@ async fn main(args: Opts) -> anyhow::Result<()> { // The RelayerContext takes a configuration, and populates objects that are needed // throughout the lifetime of the relayer. Items such as wallets and providers, as well // as a convenient place to access the configuration. - let ctx = RelayerContext::new(config, store); + let ctx = RelayerContext::new(config, store)?; tracing::trace!("Created persistent storage.."); // The build_web_relayer command sets up routing (endpoint queries / requests mapped to handled code) // so clients can interact with the relayer diff --git a/services/webb-relayer/Cargo.toml b/services/webb-relayer/Cargo.toml index 6e9295b9a..2b1960906 100644 --- a/services/webb-relayer/Cargo.toml +++ b/services/webb-relayer/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "webb-relayer" -version = "0.4.1" description = "The Webb Relayer toolkit" exclude = ["tests", "config", ".github", "ci", "assets", "docker"] +version = { workspace = true } authors = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -10,6 +10,9 @@ documentation = { workspace = true } homepage = { workspace = true } repository = { workspace = true } +[build-dependencies] +build-data = "0.1.4" + [lib] doctest = false @@ -25,6 +28,7 @@ required-features = ["cli"] [dependencies] webb-proposal-signing-backends = { workspace = true } +webb-bridge-registry-backends = { workspace = true } webb-relayer-tx-queue = { workspace = true } webb-relayer-handlers = { workspace = true } webb-relayer-store = { workspace = true } @@ -45,6 +49,8 @@ config = { workspace = true } serde_json = { workspace = true } paw = { workspace = true, optional = true } webb = { workspace = true } +sp-core = { workspace = true, optional = true } +sp-runtime = { workspace = true, optional = true } # Used by ethers (but we need it to be vendored with the lib). native-tls = { workspace = true, optional = true } webb-proposals = { workspace = true } @@ -61,5 +67,11 @@ tempfile = { workspace = true } default = ["evm-runtime", "substrate-runtime"] cli = ["evm-runtime", "substrate-runtime", "anyhow", "paw", "webb-relayer-config/cli"] evm-runtime = ["webb/evm-runtime", "webb-proposals/evm"] -substrate-runtime = ["webb/substrate-runtime", "webb-proposals/substrate"] +substrate-runtime = [ + "webb/substrate-runtime", + "webb-proposals/substrate", + "webb-relayer-tx-queue/substrate", + "sp-core", + "sp-runtime", +] integration-tests = ["webb-relayer-config/integration-tests"] diff --git a/services/webb-relayer/build.rs b/services/webb-relayer/build.rs new file mode 100644 index 000000000..684382f93 --- /dev/null +++ b/services/webb-relayer/build.rs @@ -0,0 +1,9 @@ +// build.rs + +fn main() { + build_data::set_GIT_BRANCH(); + build_data::set_GIT_COMMIT(); + build_data::set_GIT_DIRTY(); + build_data::set_SOURCE_TIMESTAMP(); + build_data::no_debug_rebuilds(); +} diff --git a/services/webb-relayer/src/lib.rs b/services/webb-relayer/src/lib.rs index 34c571c76..a0de9f406 100644 --- a/services/webb-relayer/src/lib.rs +++ b/services/webb-relayer/src/lib.rs @@ -37,14 +37,14 @@ //! //! #### Private Transaction Relaying //! -//! The relayer allows for submitting proofs for privacy-preserving transactions against the Mixer, Anchor and -//! VAnchor protocols. The users generate zero-knowledge proof data, format a proper payload, and submit +//! The relayer allows for submitting proofs for privacy-preserving transactions against the VAnchor protocols. +//! The users generate zero-knowledge proof data, format a proper payload, and submit //! it to a compatible relayer for submission. //! //! #### Data Querying //! //! The relayer also supplements users who need to generate witness data for their zero-knowledge proofs. -//! The relayers cache the leaves of the trees of Mixer, Anchor or VAnchor that they are supporting. +//! The relayers cache the leaves of the trees of VAnchor that they are supporting. //! This allows users to query for the leaf data faster than querying from a chain directly. //! //! #### Data Proposing and Signature Relaying diff --git a/services/webb-relayer/src/main.rs b/services/webb-relayer/src/main.rs index 872965e2f..9f27a9b9a 100644 --- a/services/webb-relayer/src/main.rs +++ b/services/webb-relayer/src/main.rs @@ -53,7 +53,7 @@ async fn main(args: Opts) -> anyhow::Result<()> { // The RelayerContext takes a configuration, and populates objects that are needed // throughout the lifetime of the relayer. Items such as wallets and providers, as well // as a convenient place to access the configuration. - let ctx = RelayerContext::new(config, store.clone()); + let ctx = RelayerContext::new(config, store.clone())?; let metrics_clone = ctx.metrics.clone(); // metric for data stored which is determined every 1 hour @@ -76,7 +76,8 @@ async fn main(args: Opts) -> anyhow::Result<()> { let server_handle = tokio::spawn(build_web_services(ctx.clone())); // start all background services. // this does not block, will fire the services on background tasks. - webb_relayer::service::ignite(&ctx, Arc::new(store)).await?; + webb_relayer::service::ignite(ctx.clone(), Arc::new(store)).await?; + tracing::event!( target: webb_relayer_utils::probe::TARGET, tracing::Level::DEBUG, diff --git a/services/webb-relayer/src/service.rs b/services/webb-relayer/src/service.rs deleted file mode 100644 index 50e98c9eb..000000000 --- a/services/webb-relayer/src/service.rs +++ /dev/null @@ -1,1339 +0,0 @@ -// Copyright 2022 Webb Technologies Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Relayer Service Module 🕸️ -//! -//! A module for starting long-running tasks for event watching. -//! -//! ## Overview -//! -//! Services are tasks which the relayer constantly runs throughout its lifetime. -//! Services handle keeping up to date with the configured chains. - -use axum::routing::get; -use axum::Router; -use ethereum_types::U256; -use std::collections::HashSet; -use std::net::SocketAddr; -use std::sync::Arc; -use tower_http::cors::Any; -use tower_http::cors::CorsLayer; -use tower_http::trace::TraceLayer; -use webb::evm::ethers::providers; -use webb_ew_dkg::DKGMetadataWatcher; -use webb_ew_dkg::DKGProposalHandlerWatcher; -use webb_ew_dkg::DKGPublicKeyChangedHandler; -use webb_ew_dkg::ProposalSignedHandler; -use webb_ew_substrate::MaintainerSetEventHandler; -use webb_ew_substrate::SubstrateBridgeEventWatcher; -use webb_ew_substrate::SubstrateVAnchorEventWatcher; - -use webb::substrate::subxt::config::{PolkadotConfig, SubstrateConfig}; -use webb::substrate::subxt::{tx::PairSigner, OnlineClient}; -use webb_event_watcher_traits::evm::{BridgeWatcher, EventWatcher}; -use webb_event_watcher_traits::substrate::SubstrateBridgeWatcher; -use webb_event_watcher_traits::SubstrateEventWatcher; -use webb_ew_evm::open_vanchor::{ - OpenVAnchorDepositHandler, OpenVAnchorLeavesHandler, -}; -use webb_ew_evm::signature_bridge_watcher::{ - SignatureBridgeContractWatcher, SignatureBridgeContractWrapper, - SignatureBridgeGovernanceOwnershipTransferredHandler, -}; -use webb_ew_evm::{ - vanchor::*, OpenVAnchorContractWatcher, OpenVAnchorContractWrapper, - VAnchorContractWatcher, VAnchorContractWrapper, -}; -use webb_ew_substrate::{ - SubstrateVAnchorDepositHandler, SubstrateVAnchorEncryptedOutputHandler, - SubstrateVAnchorLeavesHandler, -}; -use webb_relayer_config::anchor::LinkedAnchorConfig; -use webb_relayer_config::evm::{ - Contract, SignatureBridgeContractConfig, VAnchorContractConfig, -}; -use webb_relayer_config::signing_backend::ProposalSigningBackendConfig; -use webb_relayer_config::substrate::{ - DKGPalletConfig, DKGProposalHandlerPalletConfig, Pallet, - SignatureBridgePalletConfig, SubstrateRuntime, VAnchorBn254PalletConfig, -}; - -use webb_ew_evm::vanchor::vanchor_encrypted_outputs_handler::VAnchorEncryptedOutputHandler; -use webb_proposal_signing_backends::*; -use webb_relayer_context::RelayerContext; -use webb_relayer_handlers::{ - handle_fee_info, handle_socket_info, websocket_handler, -}; - -use webb_relayer_handlers::routes::info::handle_relayer_info; -use webb_relayer_handlers::routes::leaves::{ - handle_leaves_cache_evm, handle_leaves_cache_substrate, -}; -use webb_relayer_handlers::routes::{encrypted_outputs, metric}; -use webb_relayer_store::SledStore; -use webb_relayer_tx_queue::{evm::TxQueue, substrate::SubstrateTxQueue}; - -/// Type alias for providers -pub type Client = providers::Provider; -/// Type alias for the DKG DefaultConfig -pub type DkgClient = OnlineClient; -/// Type alias for the WebbProtocol DefaultConfig -pub type WebbProtocolClient = OnlineClient; -/// Type alias for [Sled](https://sled.rs)-based database store -pub type Store = SledStore; - -/// Sets up the web socket server for the relayer, routing (endpoint queries / requests mapped to -/// handled code) and instantiates the database store. Allows clients to interact with the relayer. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration and database -pub async fn build_web_services(ctx: RelayerContext) -> crate::Result<()> { - let socket_addr = SocketAddr::new([0, 0, 0, 0].into(), ctx.config.port); - let api = Router::new() - .route("/ip", get(handle_socket_info)) - .route("/info", get(handle_relayer_info)) - .route( - "/leaves/evm/:chain_id/:contract", - get(handle_leaves_cache_evm), - ) - .route( - "/leaves/substrate/:chain_id/:tree_id/:pallet_id", - get(handle_leaves_cache_substrate), - ) - .route( - "/encrypted_outputs/evm/:chain_id/:contract_address", - get(encrypted_outputs::handle_encrypted_outputs_cache_evm), - ) - .route("/metrics", get(metric::handle_metric_info)) - .route( - "/metrics/evm/:chain_id/:contract", - get(metric::handle_evm_metric_info), - ) - .route( - "/metrics/substrate/:chain_id/:tree_id/:pallet_id", - get(metric::handle_substrate_metric_info), - ) - .route( - "/fee_info/:chain_id/:vanchor/:gas_amount", - get(handle_fee_info), - ); - - let app = Router::new() - .nest("/api/v1", api) - .route("/ws", get(websocket_handler)) - .layer(CorsLayer::new().allow_origin(Any)) - .layer(TraceLayer::new_for_http()) - .with_state(Arc::new(ctx)) - .into_make_service_with_connect_info::(); - - tracing::info!("Starting the server on {}", socket_addr); - axum::Server::bind(&socket_addr).serve(app).await?; - Ok(()) -} - -/// Starts all background services for all chains configured in the config file. -/// -/// Returns a future that resolves when all services are started successfully. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `store` -[Sled](https://sled.rs)-based database store -pub async fn ignite( - ctx: &RelayerContext, - store: Arc, -) -> crate::Result<()> { - tracing::debug!( - "Relayer configuration: {}", - serde_json::to_string_pretty(&ctx.config)? - ); - - // now we go through each chain, in our configuration - for chain_config in ctx.config.evm.values() { - if !chain_config.enabled { - continue; - } - let chain_name = &chain_config.name; - let chain_id = chain_config.chain_id; - let provider = ctx.evm_provider(&chain_id.to_string()).await?; - let client = Arc::new(provider); - tracing::debug!( - "Starting Background Services for ({}) chain.", - chain_name - ); - - for contract in &chain_config.contracts { - match contract { - Contract::VAnchor(config) => { - start_evm_vanchor_events_watcher( - ctx, - config, - chain_id, - client.clone(), - store.clone(), - ) - .await?; - } - Contract::OpenVAnchor(config) => { - start_evm_open_vanchor_events_watcher( - ctx, - config, - chain_id, - client.clone(), - store.clone(), - ) - .await?; - } - Contract::SignatureBridge(config) => { - start_signature_bridge_events_watcher( - ctx, - config, - client.clone(), - store.clone(), - ) - .await?; - } - } - } - // start the transaction queue after starting other tasks. - start_tx_queue( - ctx.clone(), - chain_config.chain_id.to_string().clone(), - store.clone(), - )?; - } - // now, we start substrate service/tasks - for (node_name, node_config) in &ctx.config.substrate { - if !node_config.enabled { - continue; - } - let chain_id = node_config.chain_id; - match node_config.runtime { - SubstrateRuntime::Dkg => { - let client = - ctx.substrate_provider::(node_name).await?; - - let chain_id = chain_id; - for pallet in &node_config.pallets { - match pallet { - Pallet::DKGProposalHandler(config) => { - start_dkg_proposal_handler( - ctx, - config, - client.clone(), - chain_id, - store.clone(), - )?; - } - Pallet::Dkg(config) => { - start_dkg_pallet_watcher( - ctx, - config, - client.clone(), - chain_id, - store.clone(), - )?; - } - Pallet::DKGProposals(_) => { - // TODO(@shekohex): start the dkg proposals service - } - Pallet::SignatureBridge(_) => { - unreachable!() - } - Pallet::VAnchorBn254(_) => { - unreachable!() - } - } - } - // start the transaction queue for dkg-substrate extrinsics after starting other tasks. - start_dkg_substrate_tx_queue( - ctx.clone(), - chain_id, - store.clone(), - )?; - } - SubstrateRuntime::WebbProtocol => { - let client = ctx - .substrate_provider::(node_name) - .await?; - let chain_id = chain_id; - for pallet in &node_config.pallets { - match pallet { - Pallet::VAnchorBn254(config) => { - start_substrate_vanchor_event_watcher( - ctx, - config, - client.clone(), - chain_id, - store.clone(), - )?; - } - Pallet::SignatureBridge(config) => { - start_substrate_signature_bridge_events_watcher( - ctx.clone(), - config, - client.clone(), - chain_id, - store.clone(), - ) - .await?; - } - Pallet::DKGProposals(_) => { - unreachable!() - } - Pallet::DKGProposalHandler(_) => { - unreachable!() - } - Pallet::Dkg(_) => { - unreachable!() - } - } - } - - // start the transaction queue for protocol-substrate after starting other tasks. - start_protocol_substrate_tx_queue( - ctx.clone(), - chain_id, - store.clone(), - )?; - } - }; - } - Ok(()) -} - -/// Starts the event watcher for Substrate vanchor events. -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `config` - VAnchorBn254 configuration -/// * `client` - WebbProtocol client -/// * `chain_id` - An u32 representing the chain id of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_substrate_vanchor_event_watcher( - ctx: &RelayerContext, - config: &VAnchorBn254PalletConfig, - client: WebbProtocolClient, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - if !config.events_watcher.enabled { - tracing::warn!( - "Substrate VAnchor events watcher is disabled for ({}).", - chain_id, - ); - return Ok(()); - } - tracing::debug!( - "Substrate VAnchor events watcher for ({}) Started.", - chain_id, - ); - - let my_ctx = ctx.clone(); - let my_config = config.clone(); - let mut shutdown_signal = ctx.shutdown_signal(); - let metrics = ctx.metrics.clone(); - let task = async move { - let proposal_signing_backend = make_substrate_proposal_signing_backend( - &my_ctx, - store.clone(), - chain_id, - my_config.linked_anchors.clone(), - my_config.proposal_signing_backend, - ) - .await?; - match proposal_signing_backend { - ProposalSigningBackendSelector::Dkg(backend) => { - // its safe to use unwrap on linked_anchors here - // since this option is always going to return Some(value). - // linked_anchors are validated in make_proposal_signing_backend() method - let deposit_handler = SubstrateVAnchorDepositHandler::new( - backend, - my_config.linked_anchors.unwrap(), - ); - let leaves_handler = SubstrateVAnchorLeavesHandler::default(); - let encrypted_output_handler = - SubstrateVAnchorEncryptedOutputHandler::default(); - - let watcher = SubstrateVAnchorEventWatcher::default(); - let substrate_vanchor_watcher_task = watcher.run( - chain_id, - client.clone().into(), - store.clone(), - my_config.events_watcher, - vec![ - Box::new(deposit_handler), - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - metrics.clone(), - ); - - tokio::select! { - _ = substrate_vanchor_watcher_task => { - tracing::warn!( - "Substrate VAnchor watcher (DKG Backend) task stopped for ({})", - chain_id, - ); - }, - - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Substrate VAnchor watcher (DKG Backend) for ({})", - chain_id, - ); - }, - } - } - ProposalSigningBackendSelector::Mocked(backend) => { - // its safe to use unwrap on linked_anchors here - // since this option is always going to return Some(value). - let deposit_handler = SubstrateVAnchorDepositHandler::new( - backend, - my_config.linked_anchors.unwrap(), - ); - let leaves_handler = SubstrateVAnchorLeavesHandler::default(); - let encrypted_output_handler = - SubstrateVAnchorEncryptedOutputHandler::default(); - - let watcher = SubstrateVAnchorEventWatcher::default(); - let substrate_vanchor_watcher_task = watcher.run( - chain_id, - client.clone().into(), - store.clone(), - my_config.events_watcher, - vec![ - Box::new(deposit_handler), - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - metrics.clone(), - ); - tokio::select! { - _ = substrate_vanchor_watcher_task => { - tracing::warn!( - "Substrate VAnchor watcher (Mocked Backend) task stopped for ({})", - chain_id, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Substrate VAnchor watcher (Mocked Backend) for ({})", - chain_id, - ); - }, - } - } - ProposalSigningBackendSelector::None => { - let leaves_handler = SubstrateVAnchorLeavesHandler::default(); - let encrypted_output_handler = - SubstrateVAnchorEncryptedOutputHandler::default(); - - let watcher = SubstrateVAnchorEventWatcher::default(); - let substrate_vanchor_watcher_task = watcher.run( - chain_id, - client.clone().into(), - store.clone(), - my_config.events_watcher, - vec![ - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - metrics.clone(), - ); - tokio::select! { - _ = substrate_vanchor_watcher_task => { - tracing::warn!( - "Substrate VAnchor watcher task stopped for ({})", - chain_id, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Substrate VAnchor watcher task for ({})", - chain_id, - ); - }, - } - } - }; - crate::Result::Ok(()) - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for DKG proposal handler events. -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `config` - DKG proposal handler configuration -/// * `client` - DKG client -/// * `chain_id` - An u32 representing the chain id of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_dkg_proposal_handler( - ctx: &RelayerContext, - config: &DKGProposalHandlerPalletConfig, - client: DkgClient, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - // check first if we should start the events watcher for this contract. - if !config.events_watcher.enabled { - tracing::warn!( - "DKG Proposal Handler events watcher is disabled for ({}).", - chain_id, - ); - return Ok(()); - } - tracing::debug!( - "DKG Proposal Handler events watcher for ({}) Started.", - chain_id, - ); - let mut shutdown_signal = ctx.shutdown_signal(); - let metrics = ctx.metrics.clone(); - let my_config = config.clone(); - let task = async move { - let proposal_handler_watcher = DKGProposalHandlerWatcher::default(); - let proposal_signed_handler = ProposalSignedHandler::default(); - let proposal_handler_watcher_task = proposal_handler_watcher.run( - chain_id, - client.into(), - store, - my_config.events_watcher, - vec![Box::new(proposal_signed_handler)], - metrics, - ); - tokio::select! { - _ = proposal_handler_watcher_task => { - tracing::warn!( - "DKG Proposal Handler events watcher stopped for ({})", - chain_id, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping DKG Proposal Handler events watcher for ({})", - chain_id, - ); - }, - } - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for DKG pallet events watcher. -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `config` - DKG pallet configuration -/// * `client` - DKG client -/// * `chain_id` - An u32 representing the chain id of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_dkg_pallet_watcher( - ctx: &RelayerContext, - config: &DKGPalletConfig, - client: DkgClient, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - // check first if we should start the events watcher for this pallet. - if !config.events_watcher.enabled { - tracing::warn!( - "DKG Pallet events watcher is disabled for ({}).", - chain_id, - ); - return Ok(()); - } - tracing::debug!("DKG Pallet events watcher for ({}) Started.", chain_id,); - let mut shutdown_signal = ctx.shutdown_signal(); - let webb_config = ctx.config.clone(); - let metrics = ctx.metrics.clone(); - let my_config = config.clone(); - let task = async move { - let dkg_event_watcher = DKGMetadataWatcher::default(); - let public_key_changed_handler = - DKGPublicKeyChangedHandler::new(webb_config); - - let dkg_event_watcher_task = dkg_event_watcher.run( - chain_id, - client.into(), - store, - my_config.events_watcher, - vec![Box::new(public_key_changed_handler)], - metrics, - ); - tokio::select! { - _ = dkg_event_watcher_task => { - tracing::warn!( - "DKG Pallet events watcher stopped for ({})", - chain_id, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping DKG Pallet events watcher for ({})", - chain_id, - ); - }, - } - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for EVM VAnchor events. -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `config` - VAnchor contract configuration -/// * `client` - EVM Chain api client -/// * `store` -[Sled](https://sled.rs)-based database store -async fn start_evm_vanchor_events_watcher( - ctx: &RelayerContext, - config: &VAnchorContractConfig, - chain_id: u32, - client: Arc, - store: Arc, -) -> crate::Result<()> { - if !config.events_watcher.enabled { - tracing::warn!( - "VAnchor events watcher is disabled for ({}).", - config.common.address, - ); - return Ok(()); - } - let wrapper = VAnchorContractWrapper::new( - config.clone(), - ctx.config.clone(), // the original config to access all networks. - client.clone(), - ); - let mut shutdown_signal = ctx.shutdown_signal(); - let contract_address = config.common.address; - let my_ctx = ctx.clone(); - let my_config = config.clone(); - let default_leaf: U256 = wrapper.contract.get_zero_hash(0).call().await?; - - let mut default_leaf_bytes = [0u8; 32]; - default_leaf.to_big_endian(&mut default_leaf_bytes); - let task = async move { - tracing::debug!( - "VAnchor events watcher for ({}) Started.", - contract_address, - ); - let contract_watcher = VAnchorContractWatcher::default(); - let proposal_signing_backend = make_proposal_signing_backend( - &my_ctx, - store.clone(), - chain_id, - my_config.linked_anchors, - my_config.proposal_signing_backend, - ) - .await?; - match proposal_signing_backend { - ProposalSigningBackendSelector::Dkg(backend) => { - let deposit_handler = VAnchorDepositHandler::new(backend); - let leaves_handler = VAnchorLeavesHandler::new( - store.clone(), - default_leaf_bytes.to_vec(), - ); - let encrypted_output_handler = - VAnchorEncryptedOutputHandler::default(); - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![ - Box::new(deposit_handler), - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - ProposalSigningBackendSelector::Mocked(backend) => { - let deposit_handler = VAnchorDepositHandler::new(backend); - let leaves_handler = VAnchorLeavesHandler::new( - store.clone(), - default_leaf_bytes.to_vec(), - ); - let encrypted_output_handler = - VAnchorEncryptedOutputHandler::default(); - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![ - Box::new(deposit_handler), - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - ProposalSigningBackendSelector::None => { - let leaves_handler = VAnchorLeavesHandler::new( - store.clone(), - default_leaf_bytes.to_vec(), - ); - let encrypted_output_handler = - VAnchorEncryptedOutputHandler::default(); - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![ - Box::new(leaves_handler), - Box::new(encrypted_output_handler), - ], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - }; - - crate::Result::Ok(()) - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for EVM VAnchor events. -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `config` - VAnchor contract configuration -/// * `client` - EVM Chain api client -/// * `store` -[Sled](https://sled.rs)-based database store -pub async fn start_evm_open_vanchor_events_watcher( - ctx: &RelayerContext, - config: &VAnchorContractConfig, - chain_id: u32, - client: Arc, - store: Arc, -) -> crate::Result<()> { - if !config.events_watcher.enabled { - tracing::warn!( - "Open VAnchor events watcher is disabled for ({}).", - config.common.address, - ); - return Ok(()); - } - let wrapper = OpenVAnchorContractWrapper::new( - config.clone(), - ctx.config.clone(), // the original config to access all networks. - client.clone(), - ); - let mut shutdown_signal = ctx.shutdown_signal(); - let contract_address = config.common.address; - let my_ctx = ctx.clone(); - let my_config = config.clone(); - let task = async move { - tracing::debug!( - "Open VAnchor events watcher for ({}) Started.", - contract_address, - ); - let contract_watcher = OpenVAnchorContractWatcher::default(); - let proposal_signing_backend = make_proposal_signing_backend( - &my_ctx, - store.clone(), - chain_id, - my_config.linked_anchors, - my_config.proposal_signing_backend, - ) - .await?; - match proposal_signing_backend { - ProposalSigningBackendSelector::Dkg(backend) => { - let deposit_handler = OpenVAnchorDepositHandler::new(backend); - let leaves_handler = OpenVAnchorLeavesHandler::default(); - - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![Box::new(deposit_handler), Box::new(leaves_handler)], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "Open VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Open VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - ProposalSigningBackendSelector::Mocked(backend) => { - let deposit_handler = OpenVAnchorDepositHandler::new(backend); - let leaves_handler = OpenVAnchorLeavesHandler::default(); - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![Box::new(deposit_handler), Box::new(leaves_handler)], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "Open VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Open VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - ProposalSigningBackendSelector::None => { - let leaves_handler = OpenVAnchorLeavesHandler::default(); - let vanchor_watcher_task = contract_watcher.run( - client, - store, - wrapper, - vec![Box::new(leaves_handler)], - &my_ctx, - ); - tokio::select! { - _ = vanchor_watcher_task => { - tracing::warn!( - "Open VAnchor watcher task stopped for ({})", - contract_address, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Open VAnchor watcher for ({})", - contract_address, - ); - }, - } - } - }; - - crate::Result::Ok(()) - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for Signature Bridge contract. -pub async fn start_signature_bridge_events_watcher( - ctx: &RelayerContext, - config: &SignatureBridgeContractConfig, - client: Arc, - store: Arc, -) -> crate::Result<()> { - if !config.events_watcher.enabled { - tracing::warn!( - "Signature Bridge events watcher is disabled for ({}).", - config.common.address, - ); - return Ok(()); - } - let mut shutdown_signal = ctx.shutdown_signal(); - let contract_address = config.common.address; - let wrapper = - SignatureBridgeContractWrapper::new(config.clone(), client.clone()); - let metrics = ctx.metrics.clone(); - let my_ctx = ctx.clone(); - let task = async move { - tracing::debug!( - "Signature Bridge watcher for ({}) Started.", - contract_address - ); - let bridge_contract_watcher = SignatureBridgeContractWatcher::default(); - let governance_transfer_handler = - SignatureBridgeGovernanceOwnershipTransferredHandler::default(); - let events_watcher_task = EventWatcher::run( - &bridge_contract_watcher, - client.clone(), - store.clone(), - wrapper.clone(), - vec![Box::new(governance_transfer_handler)], - &my_ctx, - ); - let cmd_handler_task = BridgeWatcher::run( - &bridge_contract_watcher, - client, - store, - wrapper, - metrics.clone(), - ); - tokio::select! { - _ = events_watcher_task => { - tracing::warn!( - "signature bridge events watcher task stopped for ({})", - contract_address - ); - }, - _ = cmd_handler_task => { - tracing::warn!( - "signature bridge cmd handler task stopped for ({})", - contract_address - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Signature Bridge watcher for ({})", - contract_address, - ); - }, - } - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the event watcher for Signature Bridge Pallet. -pub async fn start_substrate_signature_bridge_events_watcher( - ctx: RelayerContext, - config: &SignatureBridgePalletConfig, - client: WebbProtocolClient, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - if !config.events_watcher.enabled { - tracing::warn!( - "Substrate Signature Bridge events watcher is disabled for ({}).", - chain_id, - ); - return Ok(()); - } - let mut shutdown_signal = ctx.shutdown_signal(); - let my_config = config.clone(); - let task = async move { - tracing::debug!( - "Substrate Signature Bridge watcher for ({}) Started.", - chain_id - ); - let substrate_bridge_watcher = SubstrateBridgeEventWatcher::default(); - let bridge_event_handler = MaintainerSetEventHandler::default(); - let events_watcher_task = SubstrateEventWatcher::run( - &substrate_bridge_watcher, - chain_id, - client.clone().into(), - store.clone(), - my_config.events_watcher, - vec![Box::new(bridge_event_handler)], - ctx.metrics.clone(), - ); - let cmd_handler_task = SubstrateBridgeWatcher::run( - &substrate_bridge_watcher, - chain_id, - client.into(), - store.clone(), - ); - tokio::select! { - _ = events_watcher_task => { - tracing::warn!( - "Substrate signature bridge events watcher task stopped for ({})", - chain_id - ); - }, - _ = cmd_handler_task => { - tracing::warn!( - "Substrate signature bridge cmd handler task stopped for ({})", - chain_id - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Substrate Signature Bridge watcher for ({})", - chain_id, - ); - }, - } - }; - // kick off the watcher. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the transaction queue task -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `chain_name` - Name of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_tx_queue( - ctx: RelayerContext, - chain_id: String, - store: Arc, -) -> crate::Result<()> { - // Start tx_queue only when governance relaying feature is enabled for relayer. - if !ctx.config.features.governance_relay { - tracing::warn!("Tx Queue disabled for ({})", chain_id,); - return Ok(()); - } - - let mut shutdown_signal = ctx.shutdown_signal(); - let tx_queue = TxQueue::new(ctx, chain_id.clone(), store); - - tracing::debug!("Transaction Queue for ({}) Started.", chain_id); - let task = async move { - tokio::select! { - _ = tx_queue.run() => { - tracing::warn!( - "Transaction Queue task stopped for ({})", - chain_id, - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Transaction Queue for ({})", - chain_id, - ); - }, - } - }; - // kick off the tx_queue. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the transaction queue task for protocol-substrate extrinsic -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `chain_name` - Name of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_protocol_substrate_tx_queue( - ctx: RelayerContext, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - // Start tx_queue only when governance relaying feature is enabled for relayer. - if !ctx.config.features.governance_relay { - tracing::warn!( - "Tx Queue disabled for Protocol-Substrate node ({})", - chain_id, - ); - return Ok(()); - } - - let mut shutdown_signal = ctx.shutdown_signal(); - - let tx_queue = SubstrateTxQueue::new(ctx, chain_id, store); - - tracing::debug!( - "Transaction Queue for Protocol-Substrate node({}) Started.", - chain_id - ); - let task = async move { - tokio::select! { - _ = tx_queue.run::() => { - tracing::warn!( - "Transaction Queue task stopped for Protocol-Substrate node({})", - chain_id - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Transaction Queue for Protocol-Substrate node({})", - chain_id - ); - }, - } - }; - // kick off the substrate tx_queue. - tokio::task::spawn(task); - Ok(()) -} - -/// Starts the transaction queue task for dkg-substrate extrinsics -/// -/// Returns Ok(()) if successful, or an error if not. -/// -/// # Arguments -/// -/// * `ctx` - RelayContext reference that holds the configuration -/// * `chain_name` - Name of the chain -/// * `store` -[Sled](https://sled.rs)-based database store -pub fn start_dkg_substrate_tx_queue( - ctx: RelayerContext, - chain_id: u32, - store: Arc, -) -> crate::Result<()> { - let mut shutdown_signal = ctx.shutdown_signal(); - - let tx_queue = SubstrateTxQueue::new(ctx, chain_id, store); - - tracing::debug!( - "Transaction Queue for Dkg-Substrate node({}) Started.", - chain_id - ); - let task = async move { - tokio::select! { - _ = tx_queue.run::() => { - tracing::warn!( - "Transaction Queue task stopped for Dkg-Substrate node({})", - chain_id - ); - }, - _ = shutdown_signal.recv() => { - tracing::trace!( - "Stopping Transaction Queue for Dkg-Substrate node({})", - chain_id - ); - }, - } - }; - // kick off the substrate tx_queue. - tokio::task::spawn(task); - Ok(()) -} - -/// Proposal signing backend config -#[allow(clippy::large_enum_variant)] -pub enum ProposalSigningBackendSelector { - /// None - None, - /// Mocked - Mocked(MockedProposalSigningBackend), - /// Dkg - Dkg(DkgProposalSigningBackend), -} -/// utility to configure proposal signing backend -pub async fn make_proposal_signing_backend( - ctx: &RelayerContext, - store: Arc, - chain_id: u32, - linked_anchors: Option>, - proposal_signing_backend: Option, -) -> crate::Result { - // Check if contract is configured with governance support for the relayer. - if !ctx.config.features.governance_relay { - tracing::warn!("Governance relaying is not enabled for relayer"); - return Ok(ProposalSigningBackendSelector::None); - } - - // We do this by checking if linked anchors are provided. - let linked_anchors = match linked_anchors { - Some(anchors) => { - if anchors.is_empty() { - tracing::warn!("Misconfigured Network: Linked anchors cannot be empty for governance relaying"); - return Ok(ProposalSigningBackendSelector::None); - } else { - anchors - } - } - None => { - tracing::warn!("Misconfigured Network: Linked anchors must be configured for governance relaying"); - return Ok(ProposalSigningBackendSelector::None); - } - }; - - // we need to check/match on the proposal signing backend configured for this anchor. - match proposal_signing_backend { - Some(ProposalSigningBackendConfig::DkgNode(c)) => { - // if it is the dkg backend, we will need to connect to that node first, - // and then use the DkgProposalSigningBackend to sign the proposal. - let typed_chain_id = webb_proposals::TypedChainId::Evm(chain_id); - let dkg_client = - ctx.substrate_provider::(&c.node).await?; - let pair = ctx.substrate_wallet(&c.node).await?; - let backend = DkgProposalSigningBackend::new( - dkg_client, - PairSigner::new(pair), - typed_chain_id, - ); - Ok(ProposalSigningBackendSelector::Dkg(backend)) - } - Some(ProposalSigningBackendConfig::Mocked(mocked)) => { - // if it is the mocked backend, we will use the MockedProposalSigningBackend to sign the proposal. - // which is a bit simpler than the DkgProposalSigningBackend. - // get only the linked chains to that anchor. - let mut signature_bridges: HashSet = - HashSet::new(); - linked_anchors.iter().for_each(|anchor| { - // using chain_id to ensure that we have only one signature bridge - let resource_id = match anchor { - LinkedAnchorConfig::Raw(target) => { - let bytes: [u8; 32] = target.resource_id.into(); - webb_proposals::ResourceId::from(bytes) - } - _ => unreachable!("unsupported"), - }; - signature_bridges.insert(resource_id); - }); - let backend = MockedProposalSigningBackend::builder() - .store(store.clone()) - .private_key(mocked.private_key) - .signature_bridges(signature_bridges) - .build(); - Ok(ProposalSigningBackendSelector::Mocked(backend)) - } - None => { - tracing::warn!("Misconfigured Network: Proposal signing backend must be configured for governance relaying"); - Ok(ProposalSigningBackendSelector::None) - } - } -} - -/// utility to configure proposal signing backend for substrate chain -pub async fn make_substrate_proposal_signing_backend( - ctx: &RelayerContext, - store: Arc, - chain_id: u32, - linked_anchors: Option>, - proposal_signing_backend: Option, -) -> crate::Result { - // Check if contract is configured with governance support for the relayer. - if !ctx.config.features.governance_relay { - tracing::warn!("Governance relaying is not enabled for relayer"); - return Ok(ProposalSigningBackendSelector::None); - } - // check if linked anchors are provided. - let linked_anchors = match linked_anchors { - Some(anchors) => { - if anchors.is_empty() { - tracing::warn!("Misconfigured Network: Linked anchors cannot be empty for governance relaying"); - return Ok(ProposalSigningBackendSelector::None); - } else { - anchors - } - } - None => { - tracing::warn!("Misconfigured Network: Linked anchors must be configured for governance relaying"); - return Ok(ProposalSigningBackendSelector::None); - } - }; - // we need to check/match on the proposal signing backend configured for this anchor. - match proposal_signing_backend { - Some(ProposalSigningBackendConfig::DkgNode(c)) => { - // if it is the dkg backend, we will need to connect to that node first, - // and then use the DkgProposalSigningBackend to sign the proposal. - let typed_chain_id = - webb_proposals::TypedChainId::Substrate(chain_id); - let dkg_client = - ctx.substrate_provider::(&c.node).await?; - let pair = ctx.substrate_wallet(&c.node).await?; - let backend = DkgProposalSigningBackend::new( - dkg_client, - PairSigner::new(pair), - typed_chain_id, - ); - Ok(ProposalSigningBackendSelector::Dkg(backend)) - } - Some(ProposalSigningBackendConfig::Mocked(mocked)) => { - // if it is the mocked backend, we will use the MockedProposalSigningBackend to sign the proposal. - // which is a bit simpler than the DkgProposalSigningBackend. - // get only the linked chains to that anchor. - let mut signature_bridges: HashSet = - HashSet::new(); - linked_anchors.iter().for_each(|anchor| { - // using chain_id to ensure that we have only one signature bridge - let resource_id = match anchor { - LinkedAnchorConfig::Raw(target) => { - let bytes: [u8; 32] = target.resource_id.into(); - webb_proposals::ResourceId::from(bytes) - } - _ => unreachable!("unsupported"), - }; - signature_bridges.insert(resource_id); - }); - - let backend = MockedProposalSigningBackend::builder() - .store(store.clone()) - .private_key(mocked.private_key) - .signature_bridges(signature_bridges) - .build(); - Ok(ProposalSigningBackendSelector::Mocked(backend)) - } - None => { - tracing::warn!("Misconfigured Network: Proposal signing backend must be configured for governance relaying"); - Ok(ProposalSigningBackendSelector::None) - } - } -} diff --git a/services/webb-relayer/src/service/evm.rs b/services/webb-relayer/src/service/evm.rs new file mode 100644 index 000000000..4a576f7f5 --- /dev/null +++ b/services/webb-relayer/src/service/evm.rs @@ -0,0 +1,506 @@ +use std::sync::Arc; + +use axum::routing::get; +use axum::Router; +use webb::evm::ethers::prelude::TimeLag; +use webb_bridge_registry_backends::dkg::DkgBridgeRegistryBackend; +use webb_bridge_registry_backends::mocked::MockedBridgeRegistryBackend; +use webb_event_watcher_traits::{ + BridgeWatcher, EthersClient, EthersTimeLagClient, EventWatcher, +}; + +use webb_ew_evm::signature_bridge_watcher::{ + SignatureBridgeContractWatcher, SignatureBridgeContractWrapper, + SignatureBridgeGovernanceOwnershipTransferredHandler, +}; +use webb_ew_evm::vanchor::{ + VAnchorDepositHandler, VAnchorEncryptedOutputHandler, VAnchorLeavesHandler, +}; +use webb_ew_evm::{VAnchorContractWatcher, VAnchorContractWrapper}; +use webb_proposal_signing_backends::queue::{self, policy}; +use webb_proposals::TypedChainId; +use webb_relayer_config::evm::{ + Contract, SignatureBridgeContractConfig, SmartAnchorUpdatesConfig, + VAnchorContractConfig, +}; +use webb_relayer_context::RelayerContext; +use webb_relayer_handlers::handle_evm_fee_info; +use webb_relayer_handlers::routes::{encrypted_outputs, leaves, metric}; +use webb_relayer_tx_queue::evm::TxQueue; + +use super::make_proposal_signing_backend; +use super::ProposalSigningBackendSelector; + +/// Type alias for providers +pub type Client = EthersClient; +/// Type alias for Timelag provider +pub type TimeLagClient = EthersTimeLagClient; + +/// Setup and build all the EVM web services and handlers. +pub fn build_web_services() -> Router> { + Router::new() + .route( + "/leaves/evm/:chain_id/:contract", + get(leaves::handle_leaves_cache_evm), + ) + .route( + "/encrypted_outputs/evm/:chain_id/:contract_address", + get(encrypted_outputs::handle_encrypted_outputs_cache_evm), + ) + .route( + "/metrics/evm/:chain_id/:contract", + get(metric::handle_evm_metric_info), + ) + // for backward compatibility + .route("/metrics", get(metric::handle_metric_info)) + .route( + "/fee_info/evm/:chain_id/:vanchor/:gas_amount", + get(handle_evm_fee_info), + ) +} + +/// Fires up all background services for all EVM chains configured in the config file. +/// +/// Returns a future that resolves when all services are started successfully. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `store` -[Sled](https://sled.rs)-based database store +pub async fn ignite( + ctx: &RelayerContext, + store: Arc, +) -> crate::Result<()> { + for chain_config in ctx.config.evm.values() { + if !chain_config.enabled { + continue; + } + let chain_name = &chain_config.name; + let chain_id = chain_config.chain_id; + let client = ctx.evm_provider(chain_id).await?; + // Time lag offset tip. + let block_confirmations = chain_config.block_confirmations; + let timelag_client = + Arc::new(TimeLag::new(client.clone(), block_confirmations)); + tracing::debug!( + "Starting Background Services for ({}) chain.", + chain_name + ); + + for contract in &chain_config.contracts { + match contract { + Contract::VAnchor(config) => { + start_vanchor_events_watcher( + ctx, + config, + chain_id, + timelag_client.clone(), + store.clone(), + ) + .await?; + } + Contract::SignatureBridge(config) => { + start_signature_bridge_events_watcher( + ctx, + config, + timelag_client.clone(), + store.clone(), + ) + .await?; + } + } + } + // start the transaction queue after starting other tasks. + start_tx_queue(ctx.clone(), chain_config.chain_id, store.clone())?; + } + Ok(()) +} + +/// Starts the event watcher for EVM VAnchor events. +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `config` - VAnchor contract configuration +/// * `client` - EVM Chain api client +/// * `store` -[Sled](https://sled.rs)-based database store +async fn start_vanchor_events_watcher( + ctx: &RelayerContext, + config: &VAnchorContractConfig, + chain_id: u32, + client: Arc, + store: Arc, +) -> crate::Result<()> { + if !config.events_watcher.enabled { + tracing::warn!( + "VAnchor events watcher is disabled for ({}).", + config.common.address, + ); + return Ok(()); + } + let wrapper = VAnchorContractWrapper::new( + config.clone(), + ctx.config.clone(), // the original config to access all networks. + client.clone(), + ); + let mut shutdown_signal = ctx.shutdown_signal(); + let contract_address = config.common.address; + let my_ctx = ctx.clone(); + let my_config = config.clone(); + let task = async move { + tracing::debug!( + "VAnchor events watcher for ({}) Started.", + contract_address, + ); + let contract_watcher = VAnchorContractWatcher::default(); + let proposal_signing_backend = make_proposal_signing_backend( + &my_ctx, + store.clone(), + TypedChainId::Evm(chain_id), + my_config.linked_anchors, + my_config.proposal_signing_backend, + ) + .await?; + tracing::debug!( + %chain_id, + %contract_address, + "Fetching the Zero Hash from the contract", + ); + let zero_hash = wrapper.contract.get_zero_hash(0).call().await?; + tracing::debug!( + %chain_id, + %contract_address, + %zero_hash, + "Found the Zero Hash", + ); + let mut zero_hash_bytes = [0u8; 32]; + zero_hash.to_big_endian(&mut zero_hash_bytes); + + let proposals_queue = queue::mem::InMemoryProposalsQueue::new(); + let time_delay_policy = { + let defaults = SmartAnchorUpdatesConfig::default(); + let v = &my_config.smart_anchor_updates; + let initial_delay = v + .initial_time_delay + .or(defaults.initial_time_delay) + .expect("initial time delay is set by default"); + let min_delay = v + .min_time_delay + .or(defaults.min_time_delay) + .expect("min time delay is set by default"); + let max_delay = v + .max_time_delay + .or(defaults.max_time_delay) + .expect("max time delay is set by default"); + let window_size = v + .time_delay_window_size + .or(defaults.time_delay_window_size) + .expect("time delay window size is set by default"); + + policy::TimeDelayPolicy::builder() + .initial_delay(initial_delay) + .min_delay(min_delay) + .max_delay(max_delay) + .window_size(window_size) + .build() + }; + + if my_config.smart_anchor_updates.enabled { + tracing::info!( + %chain_id, + %contract_address, + "Smart Anchor Updates enabled", + ); + } else { + tracing::info!( + chain_id, + %contract_address, + "Smart Anchor Updates disabled", + ); + } + + let enqueue_policy = my_config.smart_anchor_updates.enabled.then_some( + (policy::AlwaysHigherNoncePolicy, time_delay_policy.clone()), + ); + let dequeue_policy = my_config + .smart_anchor_updates + .enabled + .then_some(time_delay_policy); + + let metrics = my_ctx.metrics.clone(); + match proposal_signing_backend { + ProposalSigningBackendSelector::Dkg(backend) => { + let bridge_registry = + DkgBridgeRegistryBackend::new(backend.client.clone()); + let deposit_handler = VAnchorDepositHandler::builder() + .chain_id(chain_id) + .store(store.clone()) + .bridge_registry_backend(bridge_registry) + .proposals_queue(proposals_queue.clone()) + .policy(enqueue_policy) + .build(); + let leaves_handler = VAnchorLeavesHandler::new( + chain_id.into(), + contract_address, + store.clone(), + zero_hash_bytes.to_vec(), + )?; + let encrypted_output_handler = + VAnchorEncryptedOutputHandler::new(chain_id.into()); + let vanchor_watcher_task = contract_watcher.run( + client, + store, + wrapper, + vec![ + Box::new(deposit_handler), + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + &my_ctx, + ); + + let proposals_queue_task = queue::run( + proposals_queue, + dequeue_policy, + backend, + metrics, + ); + + tokio::select! { + _ = proposals_queue_task => { + tracing::warn!( + "Proposals queue task stopped for ({})", + contract_address, + ); + }, + _ = vanchor_watcher_task => { + tracing::warn!( + "VAnchor watcher task stopped for ({})", + contract_address, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping VAnchor watcher for ({})", + contract_address, + ); + }, + } + } + ProposalSigningBackendSelector::Mocked(backend) => { + let bridge_registry = + MockedBridgeRegistryBackend::builder().build(); + let deposit_handler = VAnchorDepositHandler::builder() + .chain_id(chain_id) + .store(store.clone()) + .bridge_registry_backend(bridge_registry) + .proposals_queue(proposals_queue.clone()) + .policy(enqueue_policy) + .build(); + let leaves_handler = VAnchorLeavesHandler::new( + chain_id.into(), + contract_address, + store.clone(), + zero_hash_bytes.to_vec(), + )?; + let encrypted_output_handler = + VAnchorEncryptedOutputHandler::new(chain_id.into()); + let vanchor_watcher_task = contract_watcher.run( + client, + store, + wrapper, + vec![ + Box::new(deposit_handler), + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + &my_ctx, + ); + + let proposals_queue_task = queue::run( + proposals_queue, + dequeue_policy, + backend, + metrics, + ); + + tokio::select! { + _ = proposals_queue_task => { + tracing::warn!( + "Proposals queue task stopped for ({})", + contract_address, + ); + }, + _ = vanchor_watcher_task => { + tracing::warn!( + "VAnchor watcher task stopped for ({})", + contract_address, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping VAnchor watcher for ({})", + contract_address, + ); + }, + } + } + ProposalSigningBackendSelector::None => { + let leaves_handler = VAnchorLeavesHandler::new( + chain_id.into(), + contract_address, + store.clone(), + zero_hash_bytes.to_vec(), + )?; + let encrypted_output_handler = + VAnchorEncryptedOutputHandler::new(chain_id.into()); + let vanchor_watcher_task = contract_watcher.run( + client, + store, + wrapper, + vec![ + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + &my_ctx, + ); + tokio::select! { + _ = vanchor_watcher_task => { + tracing::warn!( + "VAnchor watcher task stopped for ({})", + contract_address, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping VAnchor watcher for ({})", + contract_address, + ); + }, + } + } + }; + + crate::Result::Ok(()) + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the event watcher for Signature Bridge contract. +pub async fn start_signature_bridge_events_watcher( + ctx: &RelayerContext, + config: &SignatureBridgeContractConfig, + client: Arc, + store: Arc, +) -> crate::Result<()> { + if !config.events_watcher.enabled { + tracing::warn!( + "Signature Bridge events watcher is disabled for ({}).", + config.common.address, + ); + return Ok(()); + } + let mut shutdown_signal = ctx.shutdown_signal(); + let contract_address = config.common.address; + + let wrapper = + SignatureBridgeContractWrapper::new(config.clone(), client.clone()); + let metrics = ctx.metrics.clone(); + let my_ctx = ctx.clone(); + let task = async move { + tracing::debug!( + "Signature Bridge watcher for ({}) Started.", + contract_address + ); + let bridge_contract_watcher = SignatureBridgeContractWatcher::default(); + let governance_transfer_handler = + SignatureBridgeGovernanceOwnershipTransferredHandler::default(); + let events_watcher_task = EventWatcher::run( + &bridge_contract_watcher, + client.clone(), + store.clone(), + wrapper.clone(), + vec![Box::new(governance_transfer_handler)], + &my_ctx, + ); + let cmd_handler_task = BridgeWatcher::run( + &bridge_contract_watcher, + client, + store, + wrapper, + metrics.clone(), + ); + tokio::select! { + _ = events_watcher_task => { + tracing::warn!( + "signature bridge events watcher task stopped for ({})", + contract_address + ); + }, + _ = cmd_handler_task => { + tracing::warn!( + "signature bridge cmd handler task stopped for ({})", + contract_address + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Signature Bridge watcher for ({})", + contract_address, + ); + }, + } + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the transaction queue task +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `chain_name` - Name of the chain +/// * `store` -[Sled](https://sled.rs)-based database store +pub fn start_tx_queue( + ctx: RelayerContext, + chain_id: u32, + store: Arc, +) -> crate::Result<()> { + // Start tx_queue only when governance relaying feature is enabled for relayer. + if !ctx.config.features.governance_relay { + tracing::warn!("Tx Queue disabled for ({})", chain_id,); + return Ok(()); + } + + let mut shutdown_signal = ctx.shutdown_signal(); + let tx_queue = TxQueue::new(ctx, chain_id.into(), store); + + tracing::debug!("Transaction Queue for ({}) Started.", chain_id); + let task = async move { + tokio::select! { + _ = tx_queue.run() => { + tracing::warn!( + "Transaction Queue task stopped for ({})", + chain_id, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Transaction Queue for ({})", + chain_id, + ); + }, + } + }; + // kick off the tx_queue. + tokio::task::spawn(task); + Ok(()) +} diff --git a/services/webb-relayer/src/service/mod.rs b/services/webb-relayer/src/service/mod.rs new file mode 100644 index 000000000..8ae4ff834 --- /dev/null +++ b/services/webb-relayer/src/service/mod.rs @@ -0,0 +1,185 @@ +// Copyright 2022 Webb Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Relayer Service Module 🕸️ +//! +//! A module for starting long-running tasks for event watching. +//! +//! ## Overview +//! +//! Services are tasks which the relayer constantly runs throughout its lifetime. +//! Services handle keeping up to date with the configured chains. + +use std::collections::HashSet; +use std::net::SocketAddr; +use std::sync::Arc; + +use axum::routing::get; +use axum::Router; +use tower_http::cors::Any; +use tower_http::cors::CorsLayer; +use tower_http::trace::TraceLayer; +use webb::substrate::subxt; +use webb_proposal_signing_backends::{ + DkgProposalSigningBackend, MockedProposalSigningBackend, +}; +use webb_relayer_config::anchor::LinkedAnchorConfig; + +use webb_relayer_config::signing_backend::ProposalSigningBackendConfig; +use webb_relayer_context::RelayerContext; +use webb_relayer_handlers::routes::info::handle_relayer_info; +use webb_relayer_handlers::{handle_socket_info, websocket_handler}; +use webb_relayer_store::SledStore; + +/// EVM Specific Services +pub mod evm; +/// Substrate Specific Services +pub mod substrate; + +/// Type alias for [Sled](https://sled.rs)-based database store +pub type Store = SledStore; + +/// Sets up the web socket server for the relayer, routing (endpoint queries / requests mapped to +/// handled code) and instantiates the database store. Allows clients to interact with the relayer. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration and database +pub async fn build_web_services(ctx: RelayerContext) -> crate::Result<()> { + let socket_addr = SocketAddr::new([0, 0, 0, 0].into(), ctx.config.port); + let api = Router::new() + .route("/ip", get(handle_socket_info)) + .route("/info", get(handle_relayer_info)) + .merge(evm::build_web_services()) + .merge(substrate::build_web_services()); + + let app = Router::new() + .nest("/api/v1", api) + .route("/ws", get(websocket_handler)) + .layer(CorsLayer::new().allow_origin(Any)) + .layer(TraceLayer::new_for_http()) + .with_state(Arc::new(ctx)) + .into_make_service_with_connect_info::(); + + tracing::info!("Starting the server on {}", socket_addr); + axum::Server::bind(&socket_addr).serve(app).await?; + Ok(()) +} + +/// Starts all background services for all chains configured in the config file. +/// +/// Returns a future that resolves when all services are started successfully. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `store` -[Sled](https://sled.rs)-based database store +pub async fn ignite( + ctx: RelayerContext, + store: Arc, +) -> crate::Result<()> { + tracing::trace!( + "Relayer configuration: {}", + serde_json::to_string_pretty(&ctx.config)? + ); + evm::ignite(&ctx, store.clone()).await?; + substrate::ignite(ctx.clone(), store.clone()).await?; + Ok(()) +} + +/// Proposal signing backend config +#[allow(clippy::large_enum_variant)] +pub enum ProposalSigningBackendSelector { + /// None + None, + /// Mocked + Mocked(MockedProposalSigningBackend), + /// Dkg + Dkg(DkgProposalSigningBackend), +} +/// utility to configure proposal signing backend +pub async fn make_proposal_signing_backend( + ctx: &RelayerContext, + store: Arc, + typed_chain_id: webb_proposals::TypedChainId, + linked_anchors: Option>, + proposal_signing_backend: Option, +) -> crate::Result { + // Check if contract is configured with governance support for the relayer. + if !ctx.config.features.governance_relay { + tracing::warn!("Governance relaying is not enabled for relayer"); + return Ok(ProposalSigningBackendSelector::None); + } + + // we need to check/match on the proposal signing backend configured for this anchor. + match proposal_signing_backend { + Some(ProposalSigningBackendConfig::DkgNode(c)) => { + // if it is the dkg backend, we will need to connect to that node first, + // and then use the DkgProposalSigningBackend to sign the proposal. + let dkg_client = ctx + .substrate_provider::(c.chain_id) + .await?; + let backend = DkgProposalSigningBackend::builder() + .client(dkg_client) + .src_chain_id(typed_chain_id) + .store(store.clone()) + .build(); + Ok(ProposalSigningBackendSelector::Dkg(backend)) + } + Some(ProposalSigningBackendConfig::Mocked(mocked)) => { + // if it is the mocked backend, we will use the MockedProposalSigningBackend to sign the proposal. + // which is a bit simpler than the DkgProposalSigningBackend. + // get only the linked chains to that anchor. + let mut signature_bridges: HashSet = + HashSet::new(); + + // Check if linked anchors are provided. + let linked_anchors = match linked_anchors { + Some(anchors) => { + if anchors.is_empty() { + tracing::warn!("Misconfigured Network: Linked anchors cannot be empty for governance relaying"); + return Ok(ProposalSigningBackendSelector::None); + } else { + anchors + } + } + None => { + tracing::warn!("Misconfigured Network: Linked anchors must be configured for governance relaying"); + return Ok(ProposalSigningBackendSelector::None); + } + }; + linked_anchors.iter().for_each(|anchor| { + // using chain_id to ensure that we have only one signature bridge + let resource_id = match anchor { + LinkedAnchorConfig::Raw(target) => { + let bytes: [u8; 32] = target.resource_id.into(); + webb_proposals::ResourceId::from(bytes) + } + _ => unreachable!("unsupported"), + }; + signature_bridges.insert(resource_id); + }); + let backend = MockedProposalSigningBackend::builder() + .store(store.clone()) + .private_key(mocked.private_key) + .signature_bridges(signature_bridges) + .build(); + Ok(ProposalSigningBackendSelector::Mocked(backend)) + } + None => { + tracing::warn!("Misconfigured Network: Proposal signing backend must be configured for governance relaying"); + Ok(ProposalSigningBackendSelector::None) + } + } +} diff --git a/services/webb-relayer/src/service/substrate.rs b/services/webb-relayer/src/service/substrate.rs new file mode 100644 index 000000000..1e393cf9c --- /dev/null +++ b/services/webb-relayer/src/service/substrate.rs @@ -0,0 +1,534 @@ +use std::sync::Arc; + +use axum::routing::get; +use axum::Router; +use sp_core::sr25519; +use webb::substrate::subxt::config::ExtrinsicParams; +use webb::substrate::subxt::{self, PolkadotConfig}; +use webb_bridge_registry_backends::dkg::DkgBridgeRegistryBackend; +use webb_bridge_registry_backends::mocked::MockedBridgeRegistryBackend; +use webb_event_watcher_traits::{ + SubstrateBridgeWatcher, SubstrateEventWatcher, +}; +use webb_ew_dkg::{ + DKGMetadataWatcher, DKGProposalHandlerWatcher, DKGPublicKeyChangedHandler, + ProposalSignedHandler, +}; +use webb_ew_substrate::{ + MaintainerSetEventHandler, SubstrateBridgeEventWatcher, + SubstrateVAnchorDepositHandler, SubstrateVAnchorEncryptedOutputHandler, + SubstrateVAnchorEventWatcher, SubstrateVAnchorLeavesHandler, +}; +use webb_relayer_config::substrate::{ + DKGPalletConfig, DKGProposalHandlerPalletConfig, Pallet, + SignatureBridgePalletConfig, SubstrateConfig, VAnchorBn254PalletConfig, +}; +use webb_relayer_context::RelayerContext; +use webb_relayer_handlers::handle_substrate_fee_info; +use webb_relayer_handlers::routes::{leaves, metric}; +use webb_relayer_tx_queue::substrate::SubstrateTxQueue; + +use super::ProposalSigningBackendSelector; + +/// Type alias for the Tangle DefaultConfig +pub type TangleClient = subxt::OnlineClient; + +/// Setup and build all the Substrate web services and handlers. +pub fn build_web_services() -> Router> { + Router::new() + .route( + "/leaves/substrate/:chain_id/:tree_id/:pallet_id", + get(leaves::handle_leaves_cache_substrate), + ) + .route( + "/metrics/substrate/:chain_id/:tree_id/:pallet_id", + get(metric::handle_substrate_metric_info), + ) + .route( + "/fee_info/substrate/:chain_id/:estimated_tx_fees", + get(handle_substrate_fee_info), + ) +} + +/// Fires up all background services for all Substrate chains configured in the config file. +/// +/// Returns a future that resolves when all services are started successfully. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `store` -[Sled](https://sled.rs)-based database store +pub async fn ignite( + ctx: RelayerContext, + store: Arc, +) -> crate::Result<()> { + for (_, node_config) in ctx.clone().config.substrate { + if !node_config.enabled { + continue; + } + ignite_tangle_runtime(ctx.clone(), store.clone(), &node_config).await?; + } + Ok(()) +} + +async fn ignite_tangle_runtime( + ctx: RelayerContext, + store: Arc, + node_config: &SubstrateConfig, +) -> crate::Result<()> { + let chain_id = node_config.chain_id; + for pallet in &node_config.pallets { + match pallet { + Pallet::DKGProposalHandler(config) => { + start_dkg_proposal_handler( + ctx.clone(), + config, + chain_id, + store.clone(), + )?; + } + Pallet::Dkg(config) => { + start_dkg_pallet_watcher( + ctx.clone(), + config, + chain_id, + store.clone(), + )?; + } + Pallet::DKGProposals(_) => { + // TODO(@shekohex): start the dkg proposals service + } + Pallet::SignatureBridge(config) => { + start_substrate_signature_bridge_events_watcher( + ctx.clone(), + config, + chain_id, + store.clone(), + ) + .await?; + } + Pallet::VAnchorBn254(config) => { + start_substrate_vanchor_event_watcher( + ctx.clone(), + config, + chain_id, + store.clone(), + )?; + } + } + } + // start the transaction queue for dkg-substrate extrinsics after starting other tasks. + start_tx_queue::(ctx.clone(), chain_id, store.clone())?; + Ok(()) +} + +/// Starts the event watcher for DKG proposal handler events. +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `config` - DKG proposal handler configuration +/// * `client` - DKG client +/// * `chain_id` - An u32 representing the chain id of the chain +/// * `store` -[Sled](https://sled.rs)-based database store +pub fn start_dkg_proposal_handler( + ctx: RelayerContext, + config: &DKGProposalHandlerPalletConfig, + chain_id: u32, + store: Arc, +) -> crate::Result<()> { + // check first if we should start the events watcher for this contract. + if !config.events_watcher.enabled { + tracing::warn!( + "DKG Proposal Handler events watcher is disabled for ({}).", + chain_id, + ); + return Ok(()); + } + tracing::debug!( + "DKG Proposal Handler events watcher for ({}) Started.", + chain_id, + ); + let mut shutdown_signal = ctx.shutdown_signal(); + let metrics = ctx.metrics.clone(); + let my_config = config.clone(); + let task = async move { + let proposal_handler_watcher = DKGProposalHandlerWatcher::default(); + let proposal_signed_handler = ProposalSignedHandler::default(); + let proposal_handler_watcher_task = proposal_handler_watcher.run( + chain_id, + ctx.clone(), + store, + my_config.events_watcher, + vec![Box::new(proposal_signed_handler)], + metrics, + ); + tokio::select! { + _ = proposal_handler_watcher_task => { + tracing::warn!( + "DKG Proposal Handler events watcher stopped for ({})", + chain_id, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping DKG Proposal Handler events watcher for ({})", + chain_id, + ); + }, + } + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the event watcher for DKG pallet events watcher. +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `config` - DKG pallet configuration +/// * `client` - DKG client +/// * `chain_id` - An u32 representing the chain id of the chain +/// * `store` -[Sled](https://sled.rs)-based database store +pub fn start_dkg_pallet_watcher( + ctx: RelayerContext, + config: &DKGPalletConfig, + chain_id: u32, + store: Arc, +) -> crate::Result<()> { + // check first if we should start the events watcher for this pallet. + if !config.events_watcher.enabled { + tracing::warn!( + "DKG Pallet events watcher is disabled for ({}).", + chain_id, + ); + return Ok(()); + } + tracing::debug!("DKG Pallet events watcher for ({}) Started.", chain_id,); + let mut shutdown_signal = ctx.shutdown_signal(); + let webb_config = ctx.config.clone(); + let metrics = ctx.metrics.clone(); + let my_config = config.clone(); + let task = async move { + let dkg_event_watcher = DKGMetadataWatcher::default(); + let public_key_changed_handler = + DKGPublicKeyChangedHandler::new(webb_config); + + let dkg_event_watcher_task = dkg_event_watcher.run( + chain_id, + ctx.clone(), + store, + my_config.events_watcher, + vec![Box::new(public_key_changed_handler)], + metrics, + ); + tokio::select! { + _ = dkg_event_watcher_task => { + tracing::warn!( + "DKG Pallet events watcher stopped for ({})", + chain_id, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping DKG Pallet events watcher for ({})", + chain_id, + ); + }, + } + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the event watcher for Substrate vanchor events. +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `config` - VAnchorBn254 configuration +/// * `client` - WebbProtocol client +/// * `chain_id` - An u32 representing the chain id of the chain +/// * `store` -[Sled](https://sled.rs)-based database store +pub fn start_substrate_vanchor_event_watcher( + ctx: RelayerContext, + config: &VAnchorBn254PalletConfig, + chain_id: u32, + store: Arc, +) -> crate::Result<()> { + if !config.events_watcher.enabled { + tracing::warn!( + "Substrate VAnchor events watcher is disabled for ({}).", + chain_id, + ); + return Ok(()); + } + tracing::debug!( + "Substrate VAnchor events watcher for ({}) Started.", + chain_id, + ); + let my_ctx = ctx.clone(); + let my_config = config.clone(); + let mut shutdown_signal = ctx.shutdown_signal(); + let metrics = ctx.metrics.clone(); + let task = async move { + let proposal_signing_backend = super::make_proposal_signing_backend( + &my_ctx, + store.clone(), + webb_proposals::TypedChainId::Substrate(chain_id), + my_config.linked_anchors.clone(), + my_config.proposal_signing_backend, + ) + .await?; + match proposal_signing_backend { + ProposalSigningBackendSelector::Dkg(backend) => { + let bridge_registry = + DkgBridgeRegistryBackend::new(backend.client.clone()); + + let deposit_handler = SubstrateVAnchorDepositHandler::new( + backend, + bridge_registry, + my_config.linked_anchors, + ); + let leaves_handler = SubstrateVAnchorLeavesHandler::default(); + let encrypted_output_handler = + SubstrateVAnchorEncryptedOutputHandler::default(); + + let watcher = SubstrateVAnchorEventWatcher::default(); + let substrate_vanchor_watcher_task = watcher.run( + chain_id, + ctx.clone(), + store.clone(), + my_config.events_watcher, + vec![ + Box::new(deposit_handler), + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + metrics.clone(), + ); + + tokio::select! { + _ = substrate_vanchor_watcher_task => { + tracing::warn!( + "Substrate VAnchor watcher (DKG Backend) task stopped for ({})", + chain_id, + ); + }, + + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Substrate VAnchor watcher (DKG Backend) for ({})", + chain_id, + ); + }, + } + } + ProposalSigningBackendSelector::Mocked(backend) => { + let bridge_registry = + MockedBridgeRegistryBackend::builder().build(); + + let deposit_handler = SubstrateVAnchorDepositHandler::new( + backend, + bridge_registry, + my_config.linked_anchors, + ); + let leaves_handler = SubstrateVAnchorLeavesHandler::default(); + let encrypted_output_handler = + SubstrateVAnchorEncryptedOutputHandler::default(); + + let watcher = SubstrateVAnchorEventWatcher::default(); + let substrate_vanchor_watcher_task = watcher.run( + chain_id, + ctx.clone(), + store.clone(), + my_config.events_watcher, + vec![ + Box::new(deposit_handler), + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + metrics.clone(), + ); + tokio::select! { + _ = substrate_vanchor_watcher_task => { + tracing::warn!( + "Substrate VAnchor watcher (Mocked Backend) task stopped for ({})", + chain_id, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Substrate VAnchor watcher (Mocked Backend) for ({})", + chain_id, + ); + }, + } + } + ProposalSigningBackendSelector::None => { + let leaves_handler = SubstrateVAnchorLeavesHandler::default(); + let encrypted_output_handler = + SubstrateVAnchorEncryptedOutputHandler::default(); + + let watcher = SubstrateVAnchorEventWatcher::default(); + let substrate_vanchor_watcher_task = watcher.run( + chain_id, + ctx.clone(), + store.clone(), + my_config.events_watcher, + vec![ + Box::new(leaves_handler), + Box::new(encrypted_output_handler), + ], + metrics.clone(), + ); + tokio::select! { + _ = substrate_vanchor_watcher_task => { + tracing::warn!( + "Substrate VAnchor watcher task stopped for ({})", + chain_id, + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Substrate VAnchor watcher task for ({})", + chain_id, + ); + }, + } + } + }; + + tracing::debug!("Task resturned"); + crate::Result::Ok(()) + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the event watcher for Signature Bridge Pallet. +pub async fn start_substrate_signature_bridge_events_watcher( + ctx: RelayerContext, + config: &SignatureBridgePalletConfig, + chain_id: u32, + store: Arc, +) -> crate::Result<()> { + if !config.events_watcher.enabled { + tracing::warn!( + "Substrate Signature Bridge events watcher is disabled for ({}).", + chain_id, + ); + return Ok(()); + } + let mut shutdown_signal = ctx.shutdown_signal(); + let my_config = config.clone(); + let pair = ctx.substrate_wallet(chain_id).await?; + let task = async move { + tracing::debug!( + "Substrate Signature Bridge watcher for ({}) Started.", + chain_id + ); + let substrate_bridge_watcher = SubstrateBridgeEventWatcher::default(); + let bridge_event_handler = MaintainerSetEventHandler::default(); + let events_watcher_task = SubstrateEventWatcher::run( + &substrate_bridge_watcher, + chain_id, + ctx.clone(), + store.clone(), + my_config.events_watcher, + vec![Box::new(bridge_event_handler)], + ctx.metrics.clone(), + ); + let cmd_handler_task = SubstrateBridgeWatcher::run( + &substrate_bridge_watcher, + chain_id, + ctx.clone(), + pair.clone(), + store.clone(), + ); + tokio::select! { + _ = events_watcher_task => { + tracing::warn!( + "Substrate signature bridge events watcher task stopped for ({})", + chain_id + ); + }, + _ = cmd_handler_task => { + tracing::warn!( + "Substrate signature bridge cmd handler task stopped for ({})", + chain_id + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Substrate Signature Bridge watcher for ({})", + chain_id, + ); + }, + } + }; + // kick off the watcher. + tokio::task::spawn(task); + Ok(()) +} + +/// Starts the transaction queue task for Substrate extrinsics +/// +/// Returns Ok(()) if successful, or an error if not. +/// +/// # Arguments +/// +/// * `ctx` - RelayContext reference that holds the configuration +/// * `chain_name` - Name of the chain +/// * `store` -[Sled](https://sled.rs)-based database store +pub fn start_tx_queue( + ctx: RelayerContext, + chain_id: u32, + store: Arc, +) -> crate::Result<()> +where + X: subxt::Config + Send + Sync, + <::ExtrinsicParams as ExtrinsicParams< + ::Index, + ::Hash, + >>::OtherParams: Default + Send + Sync, + ::Signature: From, + ::Address: From<::AccountId>, + ::AccountId: + From + Send + Sync, +{ + let mut shutdown_signal = ctx.shutdown_signal(); + + let tx_queue = SubstrateTxQueue::new(ctx, chain_id, store); + + tracing::debug!("Transaction Queue for node({}) Started.", chain_id); + let task = async move { + tokio::select! { + _ = tx_queue.run::() => { + tracing::warn!( + "Transaction Queue task stopped for node({})", + chain_id + ); + }, + _ = shutdown_signal.recv() => { + tracing::trace!( + "Stopping Transaction Queue for node({})", + chain_id + ); + }, + } + }; + // kick off the substrate tx_queue. + tokio::task::spawn(task); + Ok(()) +} diff --git a/tests/.envrc b/tests/.envrc new file mode 100644 index 000000000..4f721ca2c --- /dev/null +++ b/tests/.envrc @@ -0,0 +1,5 @@ +source_env_if_exists ../.envrc +dotenv_if_exists +layout node + +# vi: ft=sh diff --git a/tests/.mocharc.jsonc b/tests/.mocharc.jsonc index 007e0f325..f1da9efc7 100644 --- a/tests/.mocharc.jsonc +++ b/tests/.mocharc.jsonc @@ -1,12 +1,14 @@ { "loader": "ts-node/esm", - "extensions": ["ts"], - "spec": ["**/*.test.*"], + "extensions": [ + "ts" + ], "exit": true, - "ignore": ["node_modules/**"], - "slow": 90000, + "ignore": [ + "node_modules/**" + ], + "slow": 120000, "timeout": 600000, - "retries": 0, // jobs (how many tests run in parallel) // for now it is 1, since running many nodes at the same time causes // the tests to fail. diff --git a/tests/.nvmrc b/tests/.nvmrc index 6f7f377bf..ea438c37e 100644 --- a/tests/.nvmrc +++ b/tests/.nvmrc @@ -1 +1 @@ -v16 +v18.14 diff --git a/tests/lib/localDkg.ts b/tests/lib/localDkg.ts deleted file mode 100644 index 20e88415d..000000000 --- a/tests/lib/localDkg.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2022 Webb Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/// A Helper Class to Start and Manage a Local DKG Node. -/// This Could be through a Docker Container or a Local Compiled node. - -import '@webb-tools/dkg-substrate-types'; -import fs from 'fs'; -import { spawn } from 'child_process'; -import { ECPairAPI, TinySecp256k1Interface, ECPairFactory } from 'ecpair'; -import isCI from 'is-ci'; -import * as TinySecp256k1 from 'tiny-secp256k1'; - -import { LocalNodeOpts, SubstrateNodeBase } from '@webb-tools/test-utils'; -import { - EventsWatcher, - LinkedAnchor, - NodeInfo, - Pallet, - ProposalSigningBackend, -} from './webbRelayer.js'; -import { ConvertToKebabCase } from './tsHacks.js'; - -type ExportedConfigOptions = { - suri: string; - proposalSigningBackend?: ProposalSigningBackend; - linkedAnchors?: LinkedAnchor[]; - chainId: number; - enabledPallets?: Pallet[]; -}; - -type FullNodeInfo = NodeInfo & { - name: string; - httpEndpoint: string; - wsEndpoint: string; - suri: string; - chainId: number; -}; - -const DKG_STANDALONE_DOCKER_IMAGE_URL = - 'ghcr.io/webb-tools/dkg-standalone-node:edge'; - -export class LocalDkg extends SubstrateNodeBase { - public static async start(opts: LocalNodeOpts): Promise { - opts.ports = await SubstrateNodeBase.makePorts(opts); - const startArgs: string[] = [ - '-ldkg=debug', - '-ldkg_metadata=debug', - '-lruntime::offchain=debug', - '-ldkg_proposal_handler=debug', - ]; - if (opts.usageMode.mode === 'docker') { - super.pullImage({ - forcePull: opts.usageMode.forcePullImage, - image: DKG_STANDALONE_DOCKER_IMAGE_URL, - }); - const dockerArgs = [ - 'run', - '--rm', - '--name', - `${opts.authority}-node-${opts.ports.ws}`, - '-p', - `${opts.ports.ws}:9944`, - '-p', - `${opts.ports.http}:9933`, - '-p', - `${opts.ports.p2p}:30333`, - DKG_STANDALONE_DOCKER_IMAGE_URL, - 'dkg-standalone-node', - '--tmp', - '--rpc-cors', - 'all', - '--ws-external', - '--rpc-methods=unsafe', - `--${opts.authority}`, - ...startArgs, - ]; - const proc = spawn('docker', dockerArgs); - if (opts.enableLogging) { - proc.stdout.on('data', (data: Buffer) => { - console.log(data.toString()); - }); - proc.stderr.on('data', (data: Buffer) => { - console.error(data.toString()); - }); - } - return new LocalDkg(opts, proc); - } else { - startArgs.push( - '--tmp', - '--rpc-cors', - 'all', - '--rpc-methods=unsafe', - '--ws-external', - `--ws-port=${opts.ports.ws}`, - `--rpc-port=${opts.ports.http}`, - `--port=${opts.ports.p2p}`, - `--${opts.authority}` - ); - const proc = spawn(opts.usageMode.nodePath, startArgs); - if (opts.enableLogging) { - proc.stdout.on('data', (data: Buffer) => { - console.log(data.toString()); - }); - proc.stderr.on('data', (data: Buffer) => { - console.error(data.toString()); - }); - } - return new LocalDkg(opts, proc); - } - } - - public async fetchDkgPublicKey(): Promise<`0x${string}` | null> { - const api = await super.api(); - const res = await api.query.dkg.dkgPublicKey(); - const json = res.toJSON() as [number, string]; - const tinysecp: TinySecp256k1Interface = TinySecp256k1; - const ECPair: ECPairAPI = ECPairFactory(tinysecp); - if (json && json[1] !== '0x') { - const key = json[1]; - const dkgPubKey = ECPair.fromPublicKey(Buffer.from(key.slice(2), 'hex'), { - compressed: false, - }).publicKey.toString('hex'); - // now we remove the `04` prefix byte and return it. - return `0x${dkgPubKey.slice(2)}`; - } else { - return null; - } - } - // get chainId - public async getChainId(): Promise { - const api = await super.api(); - const chainId = (await api.consts.dkgProposals.chainIdentifier).toNumber(); - return chainId; - } - - public async exportConfig( - opts: ExportedConfigOptions - ): Promise { - const ports = this.opts.ports as { ws: number; http: number; p2p: number }; - const host = isCI ? 'localhost' : '127.0.0.1'; - const nodeInfo: FullNodeInfo = { - name: 'localDKG', - enabled: true, - httpEndpoint: `http://${host}:${ports.http}`, - wsEndpoint: `ws://${host}:${ports.ws}`, - runtime: 'DKG', - pallets: opts.enabledPallets ?? [], - suri: opts.suri, - chainId: opts.chainId, - }; - return nodeInfo; - } - - public async writeConfig(path: string, opts: ExportedConfigOptions) { - const config = await this.exportConfig(opts); - type ConvertedPallet = Omit< - ConvertToKebabCase, - 'events-watcher' - > & { - 'events-watcher': ConvertToKebabCase; - }; - type ConvertedConfig = Omit< - ConvertToKebabCase, - 'pallets' - > & { - pallets: ConvertedPallet[]; - }; - - const convertedConfig: ConvertedConfig = { - name: config.name, - enabled: config.enabled, - 'http-endpoint': config.httpEndpoint, - 'ws-endpoint': config.wsEndpoint, - 'chain-id': config.chainId, - runtime: config.runtime, - suri: config.suri, - pallets: config.pallets.map((c: Pallet) => { - const convertedPallet: ConvertedPallet = { - pallet: c.pallet, - 'events-watcher': { - enabled: c.eventsWatcher.enabled, - 'polling-interval': c.eventsWatcher.pollingInterval, - 'print-progress-interval' : c.eventsWatcher.printProgressInterval, - 'sync-blocks-from': c.eventsWatcher.syncBlocksFrom - }, - }; - return convertedPallet; - }), - }; - - type FullConfigFile = { - substrate: { - [key: string]: ConvertedConfig; - }; - }; - const fullConfigFile: FullConfigFile = { - substrate: { - [this.opts.name]: convertedConfig, - }, - }; - const configString = JSON.stringify(fullConfigFile, null, 2); - fs.writeFileSync(path, configString); - } -} - -export type TypedEvent = - | NewSession - | NextPublicKeySubmitted - | NextPublicKeySignatureSubmitted - | PublicKeySubmitted - | PublicKeyChanged - | PublicKeySignatureChanged - | ProposalSigned; - -type NewSession = { section: 'session'; method: 'NewSession' }; -type NextPublicKeySubmitted = { - section: 'dkg'; - method: 'NextPublicKeySubmitted'; -}; -type NextPublicKeySignatureSubmitted = { - section: 'dkg'; - method: 'NextPublicKeySignatureSubmitted'; -}; -type PublicKeySubmitted = { section: 'dkg'; method: 'PublicKeySubmitted' }; -type PublicKeyChanged = { section: 'dkg'; method: 'PublicKeyChanged' }; -type PublicKeySignatureChanged = { - section: 'dkg'; - method: 'PublicKeySignatureChanged'; -}; - -type ProposalSigned = { - section: 'dkgProposalHandler'; - method: 'ProposalSigned'; -}; diff --git a/tests/lib/localProtocolSubstrate.ts b/tests/lib/localTangle.ts similarity index 66% rename from tests/lib/localProtocolSubstrate.ts rename to tests/lib/localTangle.ts index 08c139515..5289e59f8 100644 --- a/tests/lib/localProtocolSubstrate.ts +++ b/tests/lib/localTangle.ts @@ -1,7 +1,7 @@ /// A Helper Class to Start and Manage a Local Protocol Substrate Node. /// This Could be through a Docker Container or a Local Compiled node. import fs from 'fs'; - +import '@webb-tools/tangle-substrate-types'; import { spawn } from 'child_process'; import { EventsWatcher, @@ -10,15 +10,15 @@ import { Pallet, ProposalSigningBackend, } from './webbRelayer.js'; -import { - LocalProtocolSubstrate as BaseLocalSubstrate, - LocalNodeOpts, -} from '@webb-tools/test-utils'; - +import { LocalNodeOpts } from '@webb-tools/test-utils'; +import { ECPairAPI, TinySecp256k1Interface, ECPairFactory } from 'ecpair'; +import * as TinySecp256k1 from 'tiny-secp256k1'; import { ConvertToKebabCase } from './tsHacks.js'; +import { SubstrateNodeBase } from './substrateNodeBase.js'; + +const TANGLE_DOCKER_IMAGE_URL = + 'ghcr.io/webb-tools/tangle/tangle-standalone-integration-tests:main'; -const STANDALONE_DOCKER_IMAGE_URL = - 'ghcr.io/webb-tools/protocol-substrate-standalone-node:edge'; type ExportedConfigOptions = { suri: string; proposalSigningBackend?: ProposalSigningBackend; @@ -35,18 +35,17 @@ type FullNodeInfo = NodeInfo & { chainId: number; }; -export class LocalProtocolSubstrate extends BaseLocalSubstrate { - public static async start( - opts: LocalNodeOpts - ): Promise { +export class LocalTangle extends SubstrateNodeBase { + public static async start(opts: LocalNodeOpts): Promise { opts.ports = await super.makePorts(opts); + // opts.usageMode.mode = 'docker' const startArgs: string[] = []; if (opts.usageMode.mode === 'docker') { - LocalProtocolSubstrate.pullImage({ + LocalTangle.pullImage({ forcePull: opts.usageMode.forcePullImage, - image: STANDALONE_DOCKER_IMAGE_URL, + image: TANGLE_DOCKER_IMAGE_URL, }); - startArgs.push( + const dockerArgs = [ 'run', '--rm', '--name', @@ -57,32 +56,31 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { `${opts.ports.http}:9933`, '-p', `${opts.ports.p2p}:30333`, - STANDALONE_DOCKER_IMAGE_URL, - 'webb-standalone-node', + TANGLE_DOCKER_IMAGE_URL, + 'tangle-standalone', '--tmp', + '--chain=relayer', '--rpc-cors', 'all', '--ws-external', '--rpc-methods=unsafe', - `--${opts.authority}` - ); - if (!opts.isManual) { - const proc = spawn('docker', startArgs, {}); - if (opts.enableLogging) { - proc.stdout.on('data', (data: Buffer) => { - console.log(data.toString()); - }); - proc.stderr.on('data', (data: Buffer) => { - console.error(data.toString()); - }); - } - return new LocalProtocolSubstrate(opts, proc); + `--${opts.authority}`, + ...startArgs, + ]; + const proc = spawn('docker', dockerArgs); + if (opts.enableLogging) { + proc.stdout.on('data', (data: Buffer) => { + console.log(data.toString()); + }); + proc.stderr.on('data', (data: Buffer) => { + console.error(data.toString()); + }); } - - return new LocalProtocolSubstrate(opts); + return new LocalTangle(opts, proc); } else { startArgs.push( '--tmp', + '--chain=relayer', '--rpc-cors', 'all', '--rpc-methods=unsafe', @@ -101,9 +99,28 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { console.error(data.toString()); }); } - return new LocalProtocolSubstrate(opts, proc); + return new LocalTangle(opts, proc); + } + } + + public async fetchDkgPublicKey(): Promise<`0x${string}`> { + const api = await super.api(); + const res = await api.query.dkg.dkgPublicKey(); + const json = res.toJSON() as [number, string]; + const tinysecp: TinySecp256k1Interface = TinySecp256k1; + const ECPair: ECPairAPI = ECPairFactory(tinysecp); + if (json && json[1] !== '0x') { + const key = json[1]; + const dkgPubKey = ECPair.fromPublicKey(Buffer.from(key.slice(2), 'hex'), { + compressed: false, + }).publicKey.toString('hex'); + // now we remove the `04` prefix byte and return it. + return `0x${dkgPubKey.slice(2)}`; + } else { + return `0x`; } } + // get chainId public async getChainId(): Promise { const api = await super.api(); @@ -130,7 +147,6 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { enabled: true, httpEndpoint: `http://127.0.0.1:${ports.http}`, wsEndpoint: `ws://127.0.0.1:${ports.ws}`, - runtime: 'WebbProtocol', pallets: enabledPallets, suri: opts.suri, chainId: opts.chainId, @@ -170,7 +186,6 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { 'http-endpoint': config.httpEndpoint, 'ws-endpoint': config.wsEndpoint, 'chain-id': config.chainId, - runtime: config.runtime, suri: config.suri, pallets: config.pallets.map((c: Pallet) => { const convertedPallet: ConvertedPallet = { @@ -178,8 +193,8 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { 'events-watcher': { enabled: c.eventsWatcher.enabled, 'polling-interval': c.eventsWatcher.pollingInterval, - 'print-progress-interval' : c.eventsWatcher.printProgressInterval, - 'sync-blocks-from': c.eventsWatcher.syncBlocksFrom + 'print-progress-interval': c.eventsWatcher.printProgressInterval, + 'sync-blocks-from': c.eventsWatcher.syncBlocksFrom, }, 'proposal-signing-backend': c.proposalSigningBackend?.type === 'Mocked' @@ -190,7 +205,7 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { : c.proposalSigningBackend?.type === 'DKGNode' ? { type: 'DKGNode', - node: c.proposalSigningBackend?.node, + 'chain-id': c.proposalSigningBackend?.chainId, } : undefined, @@ -227,7 +242,32 @@ export class LocalProtocolSubstrate extends BaseLocalSubstrate { } } -export type TypedEvent = MixerBn254DepositEvent | MixerBn254WithdrawEvent; +export type TypedEvent = + | NewSession + | NextPublicKeySubmitted + | NextPublicKeySignatureSubmitted + | PublicKeySubmitted + | PublicKeyChanged + | PublicKeySignatureChanged + | ProposalSigned; + +type NewSession = { section: 'session'; method: 'NewSession' }; +type NextPublicKeySubmitted = { + section: 'dkg'; + method: 'NextPublicKeySubmitted'; +}; +type NextPublicKeySignatureSubmitted = { + section: 'dkg'; + method: 'NextPublicKeySignatureSubmitted'; +}; +type PublicKeySubmitted = { section: 'dkg'; method: 'PublicKeySubmitted' }; +type PublicKeyChanged = { section: 'dkg'; method: 'PublicKeyChanged' }; +type PublicKeySignatureChanged = { + section: 'dkg'; + method: 'PublicKeySignatureChanged'; +}; -type MixerBn254DepositEvent = { section: 'mixerBn254'; method: 'Deposit' }; -type MixerBn254WithdrawEvent = { section: 'mixerBn254'; method: 'Withdraw' }; +type ProposalSigned = { + section: 'dkgProposalHandler'; + method: 'ProposalSigned'; +}; diff --git a/tests/lib/localTestnet.ts b/tests/lib/localTestnet.ts index 357a304ad..da9929efa 100644 --- a/tests/lib/localTestnet.ts +++ b/tests/lib/localTestnet.ts @@ -18,14 +18,16 @@ import fs from 'fs'; import { BigNumberish, ethers, Wallet } from 'ethers'; import { Anchors, Utility, VBridge } from '@webb-tools/protocol-solidity'; import { - DeployerConfig, - GovernorConfig, IVariableAnchorExtData, IVariableAnchorPublicInputs, -} from '@webb-tools/interfaces'; +} from '@webb-tools/interfaces/dist/vanchor'; +import { + DeployerConfig, + GovernorConfig, +} from '@webb-tools/interfaces/dist/bridge'; import { FungibleTokenWrapper, MintableToken } from '@webb-tools/tokens'; import { fetchComponentsFromFilePaths } from '@webb-tools/utils'; -import { LocalEvmChain } from '@webb-tools/test-utils'; + import path from 'path'; import child from 'child_process'; import { @@ -35,12 +37,14 @@ import { EventsWatcher, LinkedAnchor, ProposalSigningBackend, + SmartAnchorUpdatesConfig, WithdrawConfig, } from './webbRelayer'; import { ConvertToKebabCase } from './tsHacks'; import { CircomUtxo, Keypair, Utxo } from '@webb-tools/sdk-core'; import { hexToU8a, u8aToHex } from '@polkadot/util'; -import { TokenConfig } from '@webb-tools/vbridge/lib/VBridge'; +import { TokenConfig } from '@webb-tools/vbridge/dist/VBridge'; +import { LocalEvmChain } from '@webb-tools/evm-test-utils'; export type GanacheAccounts = { balance: string; @@ -54,6 +58,9 @@ export type ExportedConfigOptions = { relayerWallet?: Wallet; linkedAnchors?: LinkedAnchor[]; blockConfirmations?: number; + privateKey?: string; + smartAnchorUpdates?: SmartAnchorUpdatesConfig; + httpEndpoints?: string[]; }; // Default Events watcher for the contracts. @@ -70,6 +77,7 @@ type LocalChainOpts = { populatedAccounts: GanacheAccounts[]; enableLogging?: boolean; enabledContracts: EnabledContracts[]; + evmOptions?: any; }; export class LocalChain { @@ -88,7 +96,8 @@ export class LocalChain { const evmChain = await LocalEvmChain.init( opts.name, opts.chainId, - opts.populatedAccounts + opts.populatedAccounts, + opts.evmOptions ); const localChain = new LocalChain(opts, evmChain); return localChain; @@ -123,8 +132,10 @@ export class LocalChain { symbol, }; } + public async deployVBridge( localToken: TokenConfig, + unwrappedToken: MintableToken, localWallet: ethers.Wallet, initialGovernor: ethers.Wallet ): Promise { @@ -137,7 +148,7 @@ export class LocalChain { const vBridgeInput: VBridge.VBridgeInput = { vAnchorInputs: { asset: { - [this.chainId]: [localWallet.address], + [this.chainId]: [unwrappedToken.contract.address], }, }, chainIDs: [this.chainId], @@ -295,15 +306,22 @@ export class LocalChain { const chainBridgeSide = this.signatureVBridge.getVBridgeSide( Number(entry[0]) ); - console.log('entry: ', entry); - console.log(await chainBridgeSide.contract.signer.getAddress()); const nonce = await chainBridgeSide.contract.proposalNonce(); + const initialGovernor = entry[1]; + const governorAddress = + typeof initialGovernor === 'string' + ? initialGovernor + : initialGovernor!.address!; + const governorNonce = + typeof initialGovernor === 'string' + ? nonce.toNumber() + : initialGovernor!.nonce!; // eslint-disable-next-line no-constant-condition while (true) { try { const tx = await chainBridgeSide.transferOwnership( - entry[1], - nonce.toNumber() + governorAddress, + governorNonce ); await tx.wait(); break; @@ -317,6 +335,89 @@ export class LocalChain { return vBridge; } + public static async deployManySignatureVBridge( + chains: LocalChain[], + wrappedTokens: TokenConfig[], + unwrappedTokens: string[], + deployerWallets: ethers.Wallet[], + initialGovernors: GovernorConfig + ): Promise { + const gitRoot = child + .execSync('git rev-parse --show-toplevel') + .toString() + .trim(); + const tokenConfigs = new Map(); + for (let i = 0; i < chains.length; i++) { + tokenConfigs.set(chains[i]!.chainId, wrappedTokens[i]); + } + const asset: VBridge.VBridgeInput['vAnchorInputs']['asset'] = {}; + for (let i = 0; i < chains.length; i++) { + asset[chains[i]!.chainId] = [unwrappedTokens[i]!]; + } + const vBridgeInput: VBridge.VBridgeInput = { + vAnchorInputs: { + asset, + }, + chainIDs: chains.map((chain) => chain.chainId), + tokenConfigs: tokenConfigs, + webbTokens: new Map(), + }; + const deployerConfig: DeployerConfig = {}; + for (let i = 0; i < chains.length; i++) { + deployerConfig[chains[i]!.chainId] = deployerWallets[i]!; + } + + const witnessCalculatorCjsPath_2 = path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_2/8/witness_calculator.cjs' + ); + + const witnessCalculatorCjsPath_16 = path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_16/8/witness_calculator.cjs' + ); + + const zkComponents_2 = await fetchComponentsFromFilePaths( + path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_2/8/poseidon_vanchor_2_8.wasm' + ), + witnessCalculatorCjsPath_2, + path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_2/8/circuit_final.zkey' + ) + ); + + const zkComponents_16 = await fetchComponentsFromFilePaths( + path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_16/8/poseidon_vanchor_16_8.wasm' + ), + witnessCalculatorCjsPath_16, + path.join( + gitRoot, + 'tests', + 'solidity-fixtures/vanchor_16/8/circuit_final.zkey' + ) + ); + + const vBridge = await VBridge.VBridge.deployVariableAnchorBridge( + vBridgeInput, + deployerConfig, + initialGovernors, + zkComponents_2, + zkComponents_16 + ); + + return vBridge; + } + private async getVAnchorChainConfig( opts: ExportedConfigOptions ): Promise { @@ -343,6 +444,7 @@ export class LocalChain { printProgressInterval: 7000, }, linkedAnchors: opts.linkedAnchors, + smartAnchorUpdates: opts.smartAnchorUpdates, }, { contract: 'SignatureBridge', @@ -358,7 +460,7 @@ export class LocalChain { const chainInfo: FullChainInfo = { name: this.underlyingChainId.toString(), enabled: true, - httpEndpoint: this.endpoint, + httpEndpoint: opts.httpEndpoints ?? [this.endpoint], wsEndpoint: this.endpoint.replace('http', 'ws'), blockConfirmations: opts.blockConfirmations ?? 0, chainId: this.underlyingChainId, @@ -375,12 +477,12 @@ export class LocalChain { const chainInfo: FullChainInfo = { name: this.underlyingChainId.toString(), enabled: true, - httpEndpoint: this.endpoint, + httpEndpoint: opts.httpEndpoints ?? [this.endpoint], wsEndpoint: this.endpoint.replace('http', 'ws'), blockConfirmations: opts.blockConfirmations ?? 1, chainId: this.underlyingChainId, beneficiary: '', - privateKey: '', + privateKey: opts.privateKey ?? '', contracts: [], }; for (const contract of this.opts.enabledContracts) { @@ -404,11 +506,13 @@ export class LocalChain { | 'proposal-signing-backend' | 'withdraw-config' | 'linked-anchors' + | 'smart-anchor-updates' > & { 'events-watcher': ConvertToKebabCase; 'proposal-signing-backend'?: ConvertToKebabCase; 'withdraw-config'?: ConvertToKebabCase; 'linked-anchors'?: ConvertedLinkedAnchor[]; + 'smart-anchor-updates'?: ConvertToKebabCase; }; type ConvertedConfig = Omit< ConvertToKebabCase, @@ -432,48 +536,59 @@ export class LocalChain { 'block-confirmations': config.blockConfirmations, beneficiary: config.beneficiary, 'private-key': config.privateKey, - contracts: config.contracts.map((contract) => ({ - contract: contract.contract, - address: contract.address, - 'deployed-at': contract.deployedAt, - 'proposal-signing-backend': - contract.proposalSigningBackend?.type === 'Mocked' - ? { - type: 'Mocked', - 'private-key': contract.proposalSigningBackend?.privateKey, - } - : contract.proposalSigningBackend?.type === 'DKGNode' - ? { - type: 'DKGNode', - node: contract.proposalSigningBackend?.node, - } - : undefined, - 'events-watcher': { - enabled: contract.eventsWatcher.enabled, - 'polling-interval': contract.eventsWatcher.pollingInterval, - 'print-progress-interval': - contract.eventsWatcher.printProgressInterval, - }, - 'linked-anchors': contract?.linkedAnchors?.map((anchor: LinkedAnchor) => - anchor.type === 'Evm' - ? { - 'chain-id': anchor.chainId, - type: 'Evm', - address: anchor.address, - } - : anchor.type === 'Substrate' - ? { - type: 'Substrate', - 'chain-id': anchor.chainId, - 'tree-id': anchor.treeId, - pallet: anchor.pallet, - } - : { - type: 'Raw', - 'resource-id': anchor.resourceId, - } - ), - })), + contracts: config.contracts.map( + (contract): ConvertedContract => ({ + contract: contract.contract, + address: contract.address, + 'deployed-at': contract.deployedAt, + 'proposal-signing-backend': + contract.proposalSigningBackend?.type === 'Mocked' + ? { + type: 'Mocked', + 'private-key': contract.proposalSigningBackend?.privateKey, + } + : contract.proposalSigningBackend?.type === 'DKGNode' + ? { + type: 'DKGNode', + 'chain-id': contract.proposalSigningBackend?.chainId, + } + : undefined, + 'events-watcher': { + enabled: contract.eventsWatcher.enabled, + 'polling-interval': contract.eventsWatcher.pollingInterval, + 'print-progress-interval': + contract.eventsWatcher.printProgressInterval, + }, + 'smart-anchor-updates': { + enabled: contract.smartAnchorUpdates?.enabled ?? false, + 'initial-time-delay': contract.smartAnchorUpdates?.initialTimeDelay, + 'max-time-delay': contract.smartAnchorUpdates?.maxTimeDelay, + 'min-time-delay': contract.smartAnchorUpdates?.minTimeDelay, + 'time-delay-window-size': + contract.smartAnchorUpdates?.timeDelayWindowSize, + }, + 'linked-anchors': contract?.linkedAnchors?.map( + (anchor: LinkedAnchor) => + anchor.type === 'Evm' + ? { + 'chain-id': anchor.chainId, + type: 'Evm', + address: anchor.address, + } + : anchor.type === 'Substrate' + ? { + type: 'Substrate', + 'chain-id': anchor.chainId, + 'tree-id': anchor.treeId, + pallet: anchor.pallet, + } + : { + type: 'Raw', + 'resource-id': anchor.resourceId, + } + ), + }) + ), }; const fullConfigFile: FullConfigFile = { evm: { @@ -486,7 +601,7 @@ export class LocalChain { } export type FullChainInfo = ChainInfo & { - httpEndpoint: string; + httpEndpoint: string[]; wsEndpoint: string; privateKey: string; blockConfirmations: number; diff --git a/tests/lib/localTestnetOpenVBridge.ts b/tests/lib/localTestnetOpenVBridge.ts deleted file mode 100644 index 930e8b79e..000000000 --- a/tests/lib/localTestnetOpenVBridge.ts +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2022 Webb Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -import fs from 'fs'; -import { ethers, Wallet } from 'ethers'; -import { Utility, VBridge } from '@webb-tools/protocol-solidity'; -import { DeployerConfig, GovernorConfig } from '@webb-tools/interfaces'; -import { MintableToken } from '@webb-tools/tokens'; -import { FungibleTokenWrapper } from '@webb-tools/tokens'; -import { LocalEvmChain } from '@webb-tools/test-utils'; -import child from 'child_process'; -import { - ChainInfo, - Contract, - EnabledContracts, - EventsWatcher, - FeaturesConfig, - LinkedAnchor, - ProposalSigningBackend, - WithdrawConfig, -} from './webbRelayer'; -import { ConvertToKebabCase } from './tsHacks'; - -export type GanacheAccounts = { - balance: string; - secretKey: string; -}; - -export type ExportedConfigOptions = { - signatureVBridge?: VBridge.OpenVBridge; - proposalSigningBackend?: ProposalSigningBackend; - features?: FeaturesConfig; - withdrawConfig?: WithdrawConfig; - relayerWallet?: Wallet; - linkedAnchors?: LinkedAnchor[]; - blockConfirmations?: number; -}; - -// Default Events watcher for the contracts. -export const defaultEventsWatcherValue: EventsWatcher = { - enabled: true, - pollingInterval: 1000, - printProgressInterval: 60_000, -}; - -type LocalChainOpts = { - name: string; - port: number; - chainId: number; - populatedAccounts: GanacheAccounts[]; - enableLogging?: boolean; - enabledContracts: EnabledContracts[]; -}; - -export class LocalChain { - private localEvmChain: LocalEvmChain; - public readonly endpoint: string; - private signatureVBridge: VBridge.OpenVBridge | null = null; - private constructor( - private readonly opts: LocalChainOpts, - localEvmChain: LocalEvmChain - ) { - this.localEvmChain = localEvmChain; - this.endpoint = `http://127.0.0.1:${opts.chainId}`; - } - - public static async init(opts: LocalChainOpts) { - const evmChain = await LocalEvmChain.init( - opts.name, - opts.chainId, - opts.populatedAccounts - ); - const localChain = new LocalChain(opts, evmChain); - return localChain; - } - - public get name(): string { - return this.opts.name; - } - - public get chainId(): number { - return Utility.getChainIdType(this.opts.chainId); - } - - public get underlyingChainId(): number { - return this.opts.chainId; - } - - public provider(): ethers.providers.WebSocketProvider { - return new ethers.providers.WebSocketProvider(this.endpoint, { - name: this.opts.name, - chainId: this.underlyingChainId, - }); - } - - public async stop() { - await this.localEvmChain.stop(); - } - - public async deployToken( - name: string, - symbol: string, - wallet: ethers.Wallet - ): Promise { - return MintableToken.createToken(name, symbol, wallet); - } - public async deployVBridge( - localToken: MintableToken, - localWallet: ethers.Wallet, - initialGovernor: ethers.Wallet - ): Promise { - const webbTokens1 = new Map(); - webbTokens1.set(this.chainId, null!); - const vBridgeInput: VBridge.VBridgeInput = { - vAnchorInputs: { - asset: { - [this.chainId]: [localToken.contract.address], - }, - }, - chainIDs: [this.chainId], - webbTokens: webbTokens1, - }; - const deployerConfig: DeployerConfig = { - [this.chainId]: localWallet, - }; - const deployerGovernors: GovernorConfig = { - [this.chainId]: initialGovernor.address, - }; - - const vBridge = await VBridge.OpenVBridge.deployVariableAnchorBridge( - vBridgeInput, - deployerConfig, - deployerGovernors - ); - - return vBridge; - } - - public async deploySignatureVBridge( - otherChain: LocalChain, - localToken: MintableToken, - otherToken: MintableToken, - localWallet: ethers.Wallet, - otherWallet: ethers.Wallet, - initialGovernors?: GovernorConfig - ): Promise { - const webbTokens1: Map = new Map< - number, - FungibleTokenWrapper | undefined - >(); - webbTokens1.set(this.chainId, null!); - webbTokens1.set(otherChain.chainId, null!); - const vBridgeInput: VBridge.VBridgeInput = { - vAnchorInputs: { - asset: { - [this.chainId]: [localToken.contract.address], - [otherChain.chainId]: [otherToken.contract.address], - }, - }, - chainIDs: [this.chainId, otherChain.chainId], - webbTokens: webbTokens1, - }; - const deployerConfig: DeployerConfig = { - [this.chainId]: localWallet, - [otherChain.chainId]: otherWallet, - }; - const deployerGovernors: GovernorConfig = { - [this.chainId]: localWallet.address, - [otherChain.chainId]: otherWallet.address, - }; - - const vBridge = await VBridge.OpenVBridge.deployVariableAnchorBridge( - vBridgeInput, - deployerConfig, - deployerGovernors - ); - - this.signatureVBridge = vBridge; - - if (initialGovernors) { - const govEntries = Object.entries(initialGovernors); - - for (const entry of govEntries) { - const chainBridgeSide = this.signatureVBridge.getVBridgeSide( - Number(entry[0]) - ); - console.log('entry: ', entry); - console.log(await chainBridgeSide.contract.signer.getAddress()); - const nonce = await chainBridgeSide.contract.proposalNonce(); - // eslint-disable-next-line no-constant-condition - while (true) { - try { - const tx = await chainBridgeSide.transferOwnership( - entry[1], - nonce.toNumber() - ); - await tx.wait(); - break; - } catch (e) { - console.log(e); - } - } - } - } - - return vBridge; - } - - private async getVAnchorChainConfig( - opts: ExportedConfigOptions - ): Promise { - const bridge = opts.signatureVBridge ?? this.signatureVBridge; - if (!bridge) { - throw new Error('Signature V bridge not deployed yet'); - } - const localAnchor = bridge.getVAnchor(this.chainId); - const side = bridge.getVBridgeSide(this.chainId); - const wallet = opts.relayerWallet ?? side.governor; - const contracts: Contract[] = [ - // first the local Anchor - { - contract: 'OpenVAnchor', - address: localAnchor.getAddress(), - deployedAt: 1, - size: 1, // Ethers - proposalSigningBackend: opts.proposalSigningBackend, - withdrawConfig: opts.withdrawConfig, - eventsWatcher: { - enabled: true, - pollingInterval: 1000, - printProgressInterval: 60_000, - }, - linkedAnchors: opts.linkedAnchors, - }, - { - contract: 'SignatureBridge', - address: side.contract.address, - deployedAt: 1, - eventsWatcher: { - enabled: true, - pollingInterval: 1000, - printProgressInterval: 60_000, - }, - }, - ]; - const chainInfo: FullChainInfo = { - name: this.underlyingChainId.toString(), - enabled: true, - httpEndpoint: this.endpoint, - wsEndpoint: this.endpoint.replace('http', 'ws'), - chainId: this.underlyingChainId, - beneficiary: (wallet as ethers.Wallet).address, - privateKey: (wallet as ethers.Wallet).privateKey, - contracts: contracts, - blockConfirmations: 1, - }; - return chainInfo; - } - - public async exportConfig( - opts: ExportedConfigOptions - ): Promise { - const chainInfo: FullChainInfo = { - name: this.underlyingChainId.toString(), - enabled: true, - httpEndpoint: this.endpoint, - wsEndpoint: this.endpoint.replace('http', 'ws'), - chainId: this.underlyingChainId, - beneficiary: '', - privateKey: '', - contracts: [], - blockConfirmations: 1, - }; - for (const contract of this.opts.enabledContracts) { - if (contract.contract == 'OpenVAnchor') { - return this.getVAnchorChainConfig(opts); - } - } - return chainInfo; - } - - public async writeConfig( - path: string, - opts: ExportedConfigOptions - ): Promise { - const config = await this.exportConfig(opts); - // don't mind my typescript typing here XD - type ConvertedLinkedAnchor = ConvertToKebabCase; - type ConvertedContract = Omit< - ConvertToKebabCase, - | 'events-watcher' - | 'proposal-signing-backend' - | 'withdraw-config' - | 'linked-anchors' - > & { - 'events-watcher': ConvertToKebabCase; - 'proposal-signing-backend'?: ConvertToKebabCase; - 'withdraw-config'?: ConvertToKebabCase; - 'linked-anchors'?: ConvertedLinkedAnchor[]; - }; - type ConvertedConfig = Omit< - ConvertToKebabCase, - 'contracts' - > & { - contracts: ConvertedContract[]; - }; - type FullConfigFile = { - evm: { - // chainId as the chain identifier - [key: number]: ConvertedConfig; - }; - features?: ConvertToKebabCase; - }; - - const convertedConfig: ConvertedConfig = { - name: config.name, - enabled: config.enabled, - 'block-confirmations': config.blockConfirmations, - 'http-endpoint': config.httpEndpoint, - 'ws-endpoint': config.wsEndpoint, - 'chain-id': config.chainId, - beneficiary: config.beneficiary, - 'private-key': config.privateKey, - contracts: config.contracts.map((contract) => ({ - contract: contract.contract, - address: contract.address, - 'deployed-at': contract.deployedAt, - 'proposal-signing-backend': - contract.proposalSigningBackend?.type === 'Mocked' - ? { - type: 'Mocked', - 'private-key': contract.proposalSigningBackend?.privateKey, - } - : contract.proposalSigningBackend?.type === 'DKGNode' - ? { - type: 'DKGNode', - node: contract.proposalSigningBackend?.node, - } - : undefined, - 'withdraw-config': contract.withdrawConfig - ? { - 'withdraw-fee-percentage': - contract.withdrawConfig?.withdrawFeePercentage, - 'withdraw-gaslimit': contract.withdrawConfig?.withdrawGaslimit, - } - : undefined, - 'events-watcher': { - enabled: contract.eventsWatcher.enabled, - 'polling-interval': contract.eventsWatcher.pollingInterval, - 'print-progress-interval': - contract.eventsWatcher.printProgressInterval, - }, - 'linked-anchors': contract?.linkedAnchors?.map((anchor: LinkedAnchor) => - anchor.type === 'Evm' - ? { - 'chain-id': anchor.chainId, - type: 'Evm', - address: anchor.address, - } - : anchor.type === 'Substrate' - ? { - type: 'Substrate', - 'chain-id': anchor.chainId, - 'tree-id': anchor.treeId, - pallet: anchor.pallet, - } - : { - type: 'Raw', - 'resource-id': anchor.resourceId, - } - ), - })), - }; - const fullConfigFile: FullConfigFile = { - evm: { - [this.underlyingChainId]: convertedConfig, - }, - features: { - 'data-query': opts.features?.dataQuery ?? true, - 'governance-relay': opts.features?.governanceRelay ?? true, - 'private-tx-relay': opts.features?.privateTxRelay ?? true, - }, - }; - const configString = JSON.stringify(fullConfigFile, null, 2); - fs.writeFileSync(path, configString); - } -} - -export type FullChainInfo = ChainInfo & { - httpEndpoint: string; - wsEndpoint: string; - privateKey: string; -}; diff --git a/tests/lib/substrateNodeBase.ts b/tests/lib/substrateNodeBase.ts index 21f5d26b9..b9b9774d4 100644 --- a/tests/lib/substrateNodeBase.ts +++ b/tests/lib/substrateNodeBase.ts @@ -15,23 +15,13 @@ * */ -import fs from 'fs'; -import getPort, { portNumbers } from 'get-port'; +import { options, rpcProperties } from '@webb-tools/api'; import { ChildProcess, execSync } from 'child_process'; + import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; -import { - EventsWatcher, - FeaturesConfig, - LinkedAnchor, - NodeInfo, - Pallet, - ProposalSigningBackend, - SubstrateLinkedAnchor, -} from './webbRelayer.js'; -import { ConvertToKebabCase } from './tsHacks.js'; -import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { options } from '@webb-tools/api'; +import { SubmittableExtrinsic } from '@polkadot/api/types'; +import '@webb-tools/tangle-substrate-types'; export type DockerMode = { mode: 'docker'; forcePullImage: boolean; @@ -57,7 +47,6 @@ export type LocalNodeOpts = { usageMode: UsageMode; enableLogging?: boolean; isManual?: boolean; // for manual connection to the substrate node using 9944 - enabledPallets?: Pallet[]; }; export type SubstrateEvent = { @@ -65,25 +54,11 @@ export type SubstrateEvent = { method: string; }; -export type ExportedConfigOptions = { - suri: string; - proposalSigningBackend?: ProposalSigningBackend; - linkedAnchors?: LinkedAnchor[]; - features?: FeaturesConfig; - chainId: number; -}; - -// Default Events watcher for the pallets. -export const defaultEventsWatcherValue: EventsWatcher = { - enabled: true, - pollingInterval: 3000, -}; - export abstract class SubstrateNodeBase { #api: ApiPromise | null = null; constructor( protected readonly opts: LocalNodeOpts, - private readonly proc?: ChildProcess + protected readonly proc?: ChildProcess ) {} public get name(): string { @@ -93,11 +68,15 @@ export abstract class SubstrateNodeBase { public static async makePorts( opts: LocalNodeOpts ): Promise<{ ws: number; http: number; p2p: number }> { + // Dynamic import used for commonjs compatibility + const getPort = await import('get-port'); + const { portNumbers } = getPort; + return opts.ports === 'auto' ? { - ws: await getPort({ port: portNumbers(9944, 9999) }), - http: await getPort({ port: portNumbers(9933, 9999) }), - p2p: await getPort({ port: portNumbers(30333, 30399) }), + http: await getPort.default({ port: portNumbers(9933, 9999) }), + p2p: await getPort.default({ port: portNumbers(30333, 30399) }), + ws: await getPort.default({ port: portNumbers(9944, 9999) }), } : (opts.ports as { ws: number; http: number; p2p: number }); } @@ -105,6 +84,7 @@ export abstract class SubstrateNodeBase { public async api(): Promise { const ports = this.opts.ports as { ws: number; http: number; p2p: number }; const host = '127.0.0.1'; + if (this.opts.isManual) { return await createApiPromise(`ws://${host}:${ports.ws}`); } @@ -112,29 +92,32 @@ export abstract class SubstrateNodeBase { if (this.#api) { return this.#api; } + this.#api = await createApiPromise(`ws://${host}:${ports.ws}`); + return this.#api; } public async stop(): Promise { await this.#api?.disconnect(); this.#api = null; - if (this.proc) this.proc.kill('SIGINT'); + + if (this.proc) { + this.proc.kill('SIGINT'); + } } public async waitForEvent(typedEvent: TypedEvent): Promise { const api = await this.api(); - return new Promise((resolve, _reject) => { - // Subscribe to system events via storage - api.query.system!.events((events: any[]) => { - // Loop through the Vec - events.forEach((record: any) => { + + return new Promise((resolve) => { + api.query.system.events((events) => { + events.forEach((record) => { const { event } = record; if ( event.section === typedEvent.section && event.method === typedEvent.method ) { - // Resolve the promise resolve(); } }); @@ -146,8 +129,9 @@ export abstract class SubstrateNodeBase { tx: SubmittableExtrinsic<'promise'> ): Promise { const api = await this.api(); + return new Promise((resolve, reject) => { - tx.send(({ status, dispatchError }) => { + tx.send(({ dispatchError, status }) => { // status would still be set, but in the case of error we can shortcut // to just check it (so an error would indicate InBlock or Finalized) if (dispatchError) { @@ -155,16 +139,18 @@ export abstract class SubstrateNodeBase { // for module errors, we have the section indexed, lookup const decoded = api.registry.findMetaError(dispatchError.asModule); const { docs, name, section } = decoded; - reject(`${section}.${name}: ${docs.join(' ')}`); + + reject(new Error(`${section}.${name}: ${docs.join(' ')}`)); } else { // Other, CannotLookup, BadOrigin, no extra info reject(dispatchError.toString()); } } + if (status.isFinalized && !dispatchError) { resolve(status.asFinalized.toString()); } - }); + }).catch((e) => reject(e)); }); } @@ -173,13 +159,11 @@ export abstract class SubstrateNodeBase { ): Promise { const api = await this.api(); const keyring = new Keyring({ type: 'sr25519' }); - const sudoKey = keyring.addFromUri(`//Alice`); - const sudoCall = api.tx.sudo!.sudo!(tx.toU8a()); + const sudoKey = keyring.addFromUri('//Alice'); + const sudoCall = api.tx.sudo.sudo(tx); return new Promise((resolve, reject) => { - sudoCall.signAndSend( - sudoKey, - { nonce: -1 }, - ({ status, dispatchError }) => { + sudoCall + .signAndSend(sudoKey, { nonce: -1 }, ({ dispatchError, status }) => { // status would still be set, but in the case of error we can shortcut // to just check it (so an error would indicate InBlock or Finalized) if (dispatchError) { @@ -189,125 +173,33 @@ export abstract class SubstrateNodeBase { dispatchError.asModule ); const { docs, name, section } = decoded; - reject(`${section}.${name}: ${docs.join(' ')}`); + + reject(new Error(`${section}.${name}: ${docs.join(' ')}`)); } else { // Other, CannotLookup, BadOrigin, no extra info reject(dispatchError.toString()); } } + if (status.isFinalized && !dispatchError) { resolve(status.asFinalized.toString()); } - } - ); + }) + .catch((e) => reject(e)); }); } - abstract exportConfig(opts: ExportedConfigOptions): Promise; - - public async writeConfig( - path: string, - opts: ExportedConfigOptions - ): Promise { - const config = await this.exportConfig(opts); - // don't mind my typescript typing here XD - type ConvertedLinkedAnchor = ConvertToKebabCase; - type ConvertedPallet = Omit< - ConvertToKebabCase, - 'events-watcher' | 'proposal-signing-backend' | 'linked-anchors' - > & { - 'events-watcher': ConvertToKebabCase; - 'proposal-signing-backend'?: ConvertToKebabCase; - 'linked-anchors'?: ConvertedLinkedAnchor[]; - }; - type ConvertedConfig = Omit< - ConvertToKebabCase, - 'pallets' - > & { - pallets: ConvertedPallet[]; - }; - type FullConfigFile = { - substrate: { - [key: string]: ConvertedConfig; - }; - features?: ConvertToKebabCase; - }; - const convertedConfig: ConvertedConfig = { - name: config.name, - enabled: config.enabled, - 'http-endpoint': config.httpEndpoint, - 'ws-endpoint': config.wsEndpoint, - 'chain-id': config.chainId, - runtime: config.runtime, - suri: config.suri, - pallets: config.pallets.map((c: Pallet) => { - const convertedPallet: ConvertedPallet = { - pallet: c.pallet, - 'events-watcher': { - enabled: c.eventsWatcher.enabled, - 'polling-interval': c.eventsWatcher.pollingInterval, - }, - 'proposal-signing-backend': - c.proposalSigningBackend?.type === 'Mocked' - ? { - type: 'Mocked', - 'private-key': c.proposalSigningBackend?.privateKey, - } - : c.proposalSigningBackend?.type === 'DKGNode' - ? { - type: 'DKGNode', - node: c.proposalSigningBackend?.node, - } - : undefined, - - 'linked-anchors': c.linkedAnchors?.map((anchor: LinkedAnchor) => - anchor.type === 'Evm' - ? { - 'chain-id': anchor.chainId, - type: 'Evm', - address: anchor.address, - } - : anchor.type === 'Substrate' - ? { - type: 'Substrate', - 'chain-id': anchor.chainId, - 'tree-id': anchor.treeId, - pallet: anchor.pallet, - } - : { - type: 'Raw', - 'resource-id': anchor.resourceId, - } - ), - }; - return convertedPallet; - }), - }; - const fullConfigFile: FullConfigFile = { - substrate: { - [this.opts.name]: convertedConfig, - }, - features: { - 'data-query': opts.features?.dataQuery ?? true, - 'governance-relay': opts.features?.governanceRelay ?? true, - 'private-tx-relay': opts.features?.privateTxRelay ?? true, - }, - }; - const configString = JSON.stringify(fullConfigFile, null, 2); - fs.writeFileSync(path, configString); - console.log(configString); - } - - protected static checkIfDkgImageExists(image: string): boolean { + protected static checkIfImageExists(image: string): boolean { const result = execSync('docker images', { encoding: 'utf8' }); + return result.includes(image); } - protected static pullDkgImage(opts: { - frocePull: boolean; + protected static pullImage(opts: { + forcePull: boolean; image: string; }): void { - if (!this.checkIfDkgImageExists(opts.image) || opts.frocePull) { + if (!this.checkIfImageExists(opts.image) || opts.forcePull) { execSync(`docker pull ${opts.image}`, { encoding: 'utf8', }); @@ -315,66 +207,11 @@ export abstract class SubstrateNodeBase { } } -async function createApiPromise(endpoint: string) { +export async function createApiPromise(endpoint: string) { return ApiPromise.create( options({ provider: new WsProvider(endpoint) as any, - rpc: { - mt: { - getLeaves: { - description: 'Query for the tree leaves', - params: [ - { - name: 'tree_id', - type: 'u32', - isOptional: false, - }, - { - name: 'from', - type: 'u32', - isOptional: false, - }, - { - name: 'to', - type: 'u32', - isOptional: false, - }, - { - name: 'at', - type: 'Hash', - isOptional: true, - }, - ], - type: 'Vec<[u8; 32]>', - }, - }, - lt: { - getNeighborRoots: { - description: 'Query for the neighbor roots', - params: [ - { - name: 'tree_id', - type: 'u32', - isOptional: false, - }, - { - name: 'at', - type: 'Hash', - isOptional: true, - }, - ], - type: 'Vec<[u8; 32]>', - }, - }, - }, + rpc: rpcProperties, }) ); } - -export type FullNodeInfo = NodeInfo & { - name: string; - httpEndpoint: string; - wsEndpoint: string; - suri: string; - chainId: number; -}; diff --git a/tests/lib/utils.ts b/tests/lib/utils.ts index 6f77b734a..aac45b890 100644 --- a/tests/lib/utils.ts +++ b/tests/lib/utils.ts @@ -1,4 +1,7 @@ +import { Keyring } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; import { Keypair, Note, Utxo } from '@webb-tools/sdk-core'; +import { BigNumber } from 'ethers'; import { EventsWatcher } from './webbRelayer'; // Default Events watcher for the pallets. @@ -6,7 +9,7 @@ export const defaultEventsWatcherValue: EventsWatcher = { enabled: true, pollingInterval: 3000, printProgressInterval: 7000, - syncBlocksFrom: 1 + syncBlocksFrom: 1, }; // Pad hexString with zero to make it of length 64. @@ -60,3 +63,9 @@ export async function generateVAnchorNote( return note; } + +export function createAccount(accountId: string): KeyringPair { + const keyring = new Keyring({ type: 'sr25519' }); + const account = keyring.addFromUri(accountId); + return account; +} diff --git a/tests/lib/webbRelayer.ts b/tests/lib/webbRelayer.ts index a09572e20..f146f6834 100644 --- a/tests/lib/webbRelayer.ts +++ b/tests/lib/webbRelayer.ts @@ -22,16 +22,19 @@ import { IVariableAnchorExtData, IVariableAnchorPublicInputs, } from '@webb-tools/interfaces'; -import { ChildProcess, spawn, execSync } from 'child_process'; +import { ChildProcess, spawn } from 'child_process'; import { EventEmitter } from 'events'; import JSONStream from 'JSONStream'; -import { BigNumber, BigNumberish } from 'ethers'; +import { BigNumber } from 'ethers'; import { ConvertToKebabCase } from './tsHacks'; import { padHexString } from '../lib/utils.js'; +import * as BN from 'bn.js'; export type CommonConfig = { features?: FeaturesConfig; + evmEtherscan?: EvmEtherscanConfig; port: number; + assets?: { [key: string]: UnlistedAssetConfig }; }; /** @@ -58,25 +61,16 @@ export class WebbRelayer { readonly #logs: RawEvent[] = []; readonly #eventEmitter = new EventEmitter(); constructor(private readonly opts: WebbRelayerOptions) { - // read the files and contents - fs.readdir(opts.configDir, (err, files) => { - if (err) { - console.log('error while reading directory'); - } - - files.map((fileName) => { - const fileContents = fs.readFileSync( - path.resolve(opts.configDir, fileName), - { encoding: 'ascii' } - ); - console.log(`file: ${fileName} \n contents: \n ${fileContents} \n\n`); - }); - }); - // Write the folder-wide configuration for this relayer instance type WrittenCommonConfig = { features?: ConvertToKebabCase; + 'evm-etherscan'?: { + [key: string]: ConvertToKebabCase; + }; port: number; + assets?: { + [key: string]: ConvertToKebabCase; + }; }; const commonConfigFile: WrittenCommonConfig = { features: { @@ -84,13 +78,19 @@ export class WebbRelayer { 'governance-relay': opts.commonConfig.features?.governanceRelay ?? true, 'private-tx-relay': opts.commonConfig.features?.privateTxRelay ?? true, }, + 'evm-etherscan': Object.fromEntries( + Object.entries(opts.commonConfig.evmEtherscan ?? {}).map( + ([key, { chainId, apiKey }]) => [ + key, + { ...{ 'chain-id': chainId, 'api-key': apiKey } }, + ] + ) + ), port: opts.commonConfig.port, }; const configString = JSON.stringify(commonConfigFile, null, 2); fs.writeFileSync(path.join(opts.configDir, 'main.json'), configString); - console.log('config string: ', configString); - // Startup the relayer const verbosity = opts.verbosity ?? 3; const levels = ['error', 'warn', 'info', 'debug', 'trace']; @@ -102,7 +102,7 @@ export class WebbRelayer { '--bin', 'webb-relayer', '--features', - 'integration-tests,cli', + 'integration-tests,cli,native-tls/vendored', '--', '-c', opts.configDir, @@ -111,9 +111,10 @@ export class WebbRelayer { ], { env: { - ...process.env, - WEBB_PORT: `${opts.commonConfig.port}`, RUST_LOG: `webb_probe=${logLevel}`, + WEBB_PORT: `${opts.commonConfig.port}`, + // allow us to override the env + ...process.env, }, } ); @@ -219,13 +220,19 @@ export class WebbRelayer { const response = await fetch(endpoint); return response; } - // API to fetch metrics for particular resource - public async getFeeInfo( + + public async getEvmFeeInfo( chainId: number, vanchor: string, gas_amount: BigNumber ) { - const endpoint = `http://127.0.0.1:${this.opts.commonConfig.port}/api/v1/fee_info/${chainId}/${vanchor}/${gas_amount}`; + const endpoint = `http://127.0.0.1:${this.opts.commonConfig.port}/api/v1/fee_info/evm/${chainId}/${vanchor}/${gas_amount}`; + const response = await fetch(endpoint); + return response; + } + + public async getSubstrateFeeInfo(chainId: number, partialFee: BN) { + const endpoint = `http://127.0.0.1:${this.opts.commonConfig.port}/api/v1/fee_info/substrate/${chainId}/${partialFee}`; const response = await fetch(endpoint); return response; } @@ -344,39 +351,6 @@ export class WebbRelayer { return txHashOrReject(ws, cmd); } - public async substrateMixerWithdraw(inputs: { - chainId: number; - id: number; - proof: number[]; - root: number[]; - nullifierHash: number[]; - recipient: string; - relayer: string; - fee: number; - refund: number; - }): Promise<`0x${string}`> { - const wsEndpoint = `ws://127.0.0.1:${this.opts.commonConfig.port}/ws`; - // create a new websocket connection to the relayer. - const ws = new WebSocket(wsEndpoint); - await new Promise((resolve) => ws.once('open', resolve)); - const cmd = { - substrate: { - mixer: { - chainId: inputs.chainId, - id: inputs.id, - proof: inputs.proof, - root: inputs.root, - nullifierHash: inputs.nullifierHash, - recipient: inputs.recipient, - relayer: inputs.relayer, - fee: inputs.fee, - refund: inputs.refund, - }, - }, - }; - return substrateTxHashOrReject(ws, cmd); - } - public async substrateVAnchorWithdraw( chainId: number, id: number, @@ -398,7 +372,7 @@ export class WebbRelayer { }, }; - console.log(cmd); + console.log(JSON.stringify(cmd)); return substrateTxHashOrReject(ws, cmd); } } @@ -555,7 +529,8 @@ type EventKind = | 'leaves_store' | 'signing_backend' | 'signature_bridge' - | 'encrypted_outputs_store'; + | 'encrypted_outputs_store' + | 'retry'; type EventTarget = 'webb_probe'; @@ -567,11 +542,11 @@ export type EventSelector = { export type SubstrateVAnchorExtData = { recipient: string; relayer: string; - extAmount: number; - fee: number; + extAmount: string; + fee: string; encryptedOutput1: number[]; encryptedOutput2: number[]; - refund: number; + refund: string; token: number; }; @@ -590,6 +565,30 @@ export interface FeaturesConfig { governanceRelay?: boolean; privateTxRelay?: boolean; } + +export interface SmartAnchorUpdatesConfig { + enabled?: boolean; + minTimeDelay?: number; + maxTimeDelay?: number; + initialTimeDelay?: number; + timeDelayWindowSize?: number; +} + +export interface EtherscanApiConfig { + chainId: number; + apiKey: string; +} + +export interface UnlistedAssetConfig { + name: string; + decimals: number; + price: number; +} + +export interface EvmEtherscanConfig { + [key: string]: EtherscanApiConfig; +} + export interface WithdrawConfig { withdrawFeePercentage: number; withdrawGaslimit: `0x${string}`; @@ -601,7 +600,7 @@ export interface WebbRelayerInfo { } export interface LeavesCacheResponse { - leaves: [string]; + leaves: [`0x${string}`]; lastQueriedBlock: string; } @@ -633,11 +632,18 @@ export interface ChainInfo { blockConfirmations: number; } -export interface FeeInfo { - estimatedFee: BigNumber; - gasPrice: BigNumber; - refundExchangeRate: BigNumber; - maxRefund: BigNumber; +export interface EvmFeeInfo { + estimatedFee: string; + gasPrice: string; + refundExchangeRate: string; + maxRefund: string; + timestamp: string; +} + +export interface SubstrateFeeInfo { + estimatedFee: string; + refundExchangeRate: string; + maxRefund: string; timestamp: string; } @@ -650,6 +656,7 @@ export interface Contract { withdrawConfig?: WithdrawConfig; proposalSigningBackend?: ProposalSigningBackend; linkedAnchors?: LinkedAnchor[]; + smartAnchorUpdates?: SmartAnchorUpdatesConfig; } export interface EventsWatcher { @@ -687,7 +694,6 @@ export interface Substrate { export interface NodeInfo { enabled: boolean; - runtime: RuntimeKind; pallets: Pallet[]; } @@ -712,10 +718,8 @@ type ContractKind = | 'Anchor' | 'SignatureBridge' | 'GovernanceBravoDelegate' - | 'VAnchor' - | 'OpenVAnchor'; + | 'VAnchor'; -type RuntimeKind = 'DKG' | 'WebbProtocol'; type PalletKind = | 'DKG' | 'DKGProposals' @@ -726,7 +730,7 @@ type PalletKind = export type DKGProposalSigningBackend = { type: 'DKGNode'; - node: string; + chainId: number; }; /** DKG Node name in the config */ export type MockedProposalSigningBackend = { type: 'Mocked'; diff --git a/tests/package.json b/tests/package.json index 1772ac0c6..6cdc70d20 100644 --- a/tests/package.json +++ b/tests/package.json @@ -9,18 +9,21 @@ "type": "module", "scripts": { "lint": "eslint . --ext .ts", - "test": "mocha", + "test": "mocha test test/*/*.test.*", + "test-evm": "mocha test test/evm/*.test.*", + "test-substrate": "mocha test test/substrate/*.test.*", + "sim": "mocha test sim/*.sim.*", "format": "prettier -w '**/*.ts'", "ts-check": "npx tsc --noEmit" }, "engines": { - "node": "16.x.x" + "node": "18.x.x" }, "resolutions": { - "@webb-tools/api": "0.1.4-117", - "@webb-tools/api-derive": "0.1.4-117", - "@webb-tools/sdk-core": "0.1.4-117", - "@webb-tools/test-utils": "0.1.4-117" + "@webb-tools/api": "0.1.4-125", + "@webb-tools/api-derive": "0.1.4-125", + "@webb-tools/sdk-core": "0.1.4-125", + "@webb-tools/test-utils": "0.1.4-125" }, "dependencies": { "@babel/core": "^7.17.8", @@ -29,26 +32,29 @@ "@babel/plugin-transform-modules-commonjs": "^7.17.7", "@babel/preset-env": "^7.16.11", "@openzeppelin/contracts": "^4.5.0", - "@polkadot/api": "9.2.4", - "@polkadot/util": "10.1.6", + "@polkadot/api": "10.3.2", + "@polkadot/util": "11.1.3", + "@polkadot/keyring": "11.1.3", "@truffle/hdwallet-provider": "^2.0.3", - "@webb-tools/anchors": "0.5.19", - "@webb-tools/vbridge": "0.5.19", - "@webb-tools/protocol-solidity": "0.5.19", - "@webb-tools/tokens": "0.5.19", - "@webb-tools/utils": "0.5.7", - "@webb-tools/api": "0.1.4-117", - "@webb-tools/api-derive": "0.1.4-117", - "@webb-tools/sdk-core": "0.1.4-117", - "@webb-tools/test-utils": "0.1.4-117", - "@webb-tools/protocol-substrate-types": "0.0.3", - "@webb-tools/dkg-substrate-types": "0.0.1", + "@webb-tools/anchors": "0.5.38", + "@webb-tools/api": "0.1.4-125", + "@webb-tools/api-derive": "0.1.4-125", + "@webb-tools/evm-test-utils": "0.5.38", + "@webb-tools/protocol-solidity": "0.5.38", + "@webb-tools/sdk-core": "0.1.4-125", + "@webb-tools/tangle-substrate-types": "0.0.1", + "@webb-tools/test-utils": "0.1.4-125", + "@webb-tools/tokens": "0.5.38", + "@webb-tools/utils": "0.5.30", + "@webb-tools/vbridge": "0.5.38", "JSONStream": "^1.3.5", "async-retry": "^1.3.3", "dotenv": "^16.0.0", "ecpair": "^2.0.1", "ethers": "5.7.0", "ganache": "7.4.1", + "chacha20": "^0.1.4", + "babyjubjub": "^1.0.4", "get-port": "6.1.2", "keccak": "^3.0.2", "node-cron": "^3.0.0", @@ -79,12 +85,12 @@ "eslint-plugin-promise": "^6.0.0", "eslint_d": "^11.1.1", "is-ci": "^3.0.1", - "mocha": "^9.2.2", + "mocha": "^10.2.0", "prettier": "^2.6.2", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", "tsconfig-paths": "^3.12.0", - "tslib": "^2.4.0", + "tslib": "^2.5.0", "typescript": "^4.6.2" } } diff --git a/tests/sim/smartAnchorUpdates.sim.ts b/tests/sim/smartAnchorUpdates.sim.ts new file mode 100644 index 000000000..aba45d732 --- /dev/null +++ b/tests/sim/smartAnchorUpdates.sim.ts @@ -0,0 +1,396 @@ +/* + * Copyright 2022 Webb Technologies Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Smart Anchor Updates Simulation + * Summary: + * + * This test simulates the following scenario: + * 1. Busy Bridge Traffic: + * a. User A deposits X ETH N times on Chain A targeting Chain B + * Print the following metrics: + * 1. Total Number of Transactions sent by the relayer. + * 2. Total Gas Used by the relayer. + * + * 2. Busy Bridge Traffic with Smart Anchor Updates: + * a. User A deposits X ETH N times on Chain A targeting Chain B + * Print the following metrics: + * 1. Total Number of Transactions sent by the relayer. + * 2. Total Gas Used by the relayer. + * + * At the end of the simulation, print the following metrics: + * 1. Improvement of Number of Transactions sent by the relayer From Scenario 1 to Scenario 2 + * 2. Improvement of Total Gas Used by the relayer From Scenario 1 to Scenario 2 + */ +import Chai, { expect } from 'chai'; +import ChaiAsPromised from 'chai-as-promised'; +import { VBridge } from '@webb-tools/protocol-solidity'; +import { TokenConfig } from '@webb-tools/vbridge/dist/VBridge'; +import { CircomUtxo, Keypair } from '@webb-tools/sdk-core'; +import dotenv from 'dotenv'; +import { BigNumber, ethers } from 'ethers'; +import temp from 'temp'; +import { LocalChain } from '../lib/localTestnet.js'; +import { + defaultWithdrawConfigValue, + EnabledContracts, + LinkedAnchor, + WebbRelayer, +} from '../lib/webbRelayer.js'; +import { hexToU8a, u8aToHex } from '@polkadot/util'; + +dotenv.config({ path: '../.env' }); +Chai.use(ChaiAsPromised); + +type SimulationMetrics = { + // Total Gas Used by the relayer across all chains. + totalGasUsed: string; + // Average Gas Used by the relayer for each chain. + avgGasUsed: string; + // Total Number of Transactions sent by the relayer. + // across all chains. + totalRelayerTxCount: number; + // Average Number of Transactions sent by the relayer. + // for each chain. + avgRelayerTxCount: number; + // Total Number of milliseconds it took to relay all roots. + // across all chains. + totalWaitTimeToRelayAllRoots: number; +}; + +describe('Smart Anchor Updates Simulation', function () { + // Disable mocha time-out because these tests take a long time. + this.timeout(0); + this.slow(Number.MAX_SAFE_INTEGER); + + const NUM_CHAINS = 7; + const NUM_DEPOSITS = 50; + + const simulationMetrics: SimulationMetrics[] = []; + let chains: LocalChain[]; + let signatureVBridge: VBridge.VBridge; + let senderWallet: ethers.Wallet; + let relayerWallet: ethers.Wallet; + + let webbRelayer: WebbRelayer; + + this.beforeEach(async () => { + const tmpDirPath = temp.mkdirSync(); + const govPk = u8aToHex(ethers.utils.randomBytes(32)); + const relayerPk = u8aToHex(ethers.utils.randomBytes(32)); + relayerWallet = new ethers.Wallet(relayerPk); + + senderWallet = ethers.Wallet.createRandom(); + const deployerWallet = ethers.Wallet.createRandom(); + + const enabledContracts: EnabledContracts[] = [ + { + contract: 'VAnchor', + }, + ]; + const populatedAccounts = [ + { + secretKey: relayerPk, + balance: ethers.utils.parseEther('10').toHexString(), + }, + { + secretKey: senderWallet.privateKey, + balance: ethers.utils.parseEther('10').toHexString(), + }, + { + secretKey: deployerWallet.privateKey, + balance: ethers.utils.parseEther('1').toHexString(), + }, + ]; + + chains = await Promise.all( + Array(NUM_CHAINS) + .fill(0) + .map((_, i) => + LocalChain.init({ + port: 5000 + i, + chainId: 5000 + i, + name: `chain${i}`, + populatedAccounts, + enabledContracts, + evmOptions: { + miner: { + defaultGasPrice: ethers.utils + .parseUnits('75', 'gwei') + .toHexString(), + }, + }, + }) + ) + ); + + const wrappedTokens: TokenConfig[] = []; + const unwrappedTokens: string[] = []; + + for (const chain of chains) { + const wrappedToken = await chain.deployToken('Wrapped Ethereum', 'WETH'); + wrappedTokens.push(wrappedToken); + unwrappedTokens.push('0x0000000000000000000000000000000000000000'); + } + + const deployerWallets = chains.map((chain) => { + return deployerWallet.connect(chain.provider()); + }); + const gov = new ethers.Wallet(govPk); + const govConfig = chains.reduce((acc, chain) => { + acc[chain.chainId] = gov.address; + return acc; + }, {} as Record); + + signatureVBridge = await LocalChain.deployManySignatureVBridge( + chains, + wrappedTokens, + unwrappedTokens, + deployerWallets, + govConfig + ); + + const linkedAnchors: LinkedAnchor[] = []; + for (let i = 0; i < chains.length; i++) { + const vanchor = signatureVBridge.getVAnchor(chains[i]!.chainId); + const rid = await vanchor.createResourceId(); + linkedAnchors.push({ type: 'Raw', resourceId: rid }); + } + + for (const chain of chains) { + const vanchor = signatureVBridge.getVAnchor(chain.chainId); + const rid = await vanchor.createResourceId(); + const myLinkedAnchors = linkedAnchors.filter((linkedAnchor) => { + if (linkedAnchor.type === 'Raw') { + return linkedAnchor.resourceId !== rid; + } else { + throw new Error('Not implemented'); + } + }); + const path = `${tmpDirPath}/${chain.name}.json`; + await chain.writeConfig(path, { + signatureVBridge, + withdrawConfig: defaultWithdrawConfigValue, + relayerWallet: relayerWallet, + linkedAnchors: myLinkedAnchors, + smartAnchorUpdates: { + enabled: simulationMetrics.length > 0, + minTimeDelay: 10, + maxTimeDelay: 90, + initialTimeDelay: 10, + }, + proposalSigningBackend: { + type: 'Mocked', + privateKey: govPk, + }, + }); + } + + for (const chain of chains) { + const gov = new ethers.Wallet(govPk); + // Sanity check. + const governorAddress = gov.address; + const currentGovernor = await signatureVBridge + .getVBridgeSide(chain.chainId) + .contract.governor(); + expect(currentGovernor).to.eq(governorAddress); + } + + webbRelayer = new WebbRelayer({ + commonConfig: { + features: { dataQuery: true, governanceRelay: true }, + port: 9955, + }, + tmp: true, + configDir: tmpDirPath, + showLogs: false, + verbosity: 3, + }); + await webbRelayer.waitUntilReady(); + }); + + it('Busy Bridge Traffic', async () => { + const localChain1 = chains[0]!; + const localChain2 = chains[1]!; + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); + const senderWallet1 = senderWallet.connect(localChain1.provider()); + vanchor1.setSigner(senderWallet1); + + const tokenAddress = '0x0000000000000000000000000000000000000000'; + + const depositUtxos = await Promise.all( + Array(NUM_DEPOSITS) + .fill(0) + .map(() => + CircomUtxo.generateUtxo({ + curve: 'Bn254', + backend: 'Circom', + amount: ethers.utils.parseEther('0.01').toHexString(), + originChainId: localChain1.chainId.toString(), + chainId: localChain2.chainId.toString(), + keypair: new Keypair(), + }) + ) + ); + for (const utxo of depositUtxos) { + const leaves = vanchor1.tree + .elements() + .map((el) => hexToU8a(el.toHexString())); + + await vanchor1.transact([], [utxo], 0, 0, '0', '0', tokenAddress, { + [localChain1.chainId]: leaves, + }); + } + const p1 = performance.now(); + await allRootsGotRelayed(signatureVBridge, chains); + const p2 = performance.now(); + const diff = p2 - p1; + const metrics = await collectMetrics(chains, relayerWallet); + metrics.totalWaitTimeToRelayAllRoots = diff; + simulationMetrics.push(metrics); + }); + + it('Busy Bridge Traffic With Smart Anchor Updates', async () => { + const localChain1 = chains[0]!; + const localChain2 = chains[1]!; + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); + const senderWallet1 = senderWallet.connect(localChain1.provider()); + vanchor1.setSigner(senderWallet1); + + const tokenAddress = '0x0000000000000000000000000000000000000000'; + + const depositUtxos = await Promise.all( + Array(NUM_DEPOSITS) + .fill(0) + .map(() => + CircomUtxo.generateUtxo({ + curve: 'Bn254', + backend: 'Circom', + amount: ethers.utils.parseEther('0.01').toHexString(), + originChainId: localChain1.chainId.toString(), + chainId: localChain2.chainId.toString(), + keypair: new Keypair(), + }) + ) + ); + for (const utxo of depositUtxos) { + const leaves = vanchor1.tree + .elements() + .map((el) => hexToU8a(el.toHexString())); + + await vanchor1.transact([], [utxo], 0, 0, '0', '0', tokenAddress, { + [localChain1.chainId]: leaves, + }); + } + + const p1 = performance.now(); + await allRootsGotRelayed(signatureVBridge, chains); + const p2 = performance.now(); + const diff = p2 - p1; + const metrics = await collectMetrics(chains, relayerWallet); + metrics.totalWaitTimeToRelayAllRoots = diff; + simulationMetrics.push(metrics); + }); + + this.afterEach(async () => { + await webbRelayer?.stop(); + for (const chain of chains) { + await chain.stop(); + } + }); + + this.afterAll(async () => { + console.log(`Simulation Metrics for ${NUM_CHAINS} chains`); + console.log(`For ${NUM_DEPOSITS} deposits:`); + console.table(simulationMetrics); + console.log('Diff (across all chains):'); + const v1 = simulationMetrics[0]!; + const v2 = simulationMetrics[1]!; + + const totalGasSaved = ethers.utils + .parseEther(v1.totalGasUsed) + .sub(ethers.utils.parseEther(v2.totalGasUsed)); + + const totalGasSavedPercentStr = ethers.utils.formatEther(totalGasSaved); + console.log(`Total gas saved: ${totalGasSavedPercentStr}`); + const totalTxSaved = v1.totalRelayerTxCount - v2.totalRelayerTxCount; + console.log(`Total txs saved: ${totalTxSaved}`); + const totalTxSavedPercent = (totalTxSaved / v1.totalRelayerTxCount) * 100; + console.log(`Total txs saved percent: ${totalTxSavedPercent}%`); + }); +}); + +async function allRootsGotRelayed( + signatureVBridge: VBridge.VBridge, + chains: LocalChain[] +) { + const allKnown: boolean[] = chains.map(() => false); + while (true) { + const allIsKnown = allKnown.every((v) => v); + if (allIsKnown) break; + + for (let i = 0; i < chains.length; i++) { + if (allKnown[i]) continue; + const chainA = chains[i]!; + const vanchorA = signatureVBridge.getVAnchor(chainA.chainId); + const expectedRoot = await vanchorA.contract.getLastRoot(); + const chainAPassed: boolean[] = Array(chains.length).fill(false); + for (let j = 0; j < chains.length; j++) { + if (i === j) { + chainAPassed[j] = true; + continue; + } + const chainB = chains[j]!; + const vanchorB = signatureVBridge.getVAnchor(chainB.chainId); + + const neigborRoots = await vanchorB.contract.getLatestNeighborRoots(); + const isKnownNeighborRoot = neigborRoots.some( + (root: BigNumber) => root.toHexString() === expectedRoot.toHexString() + ); + chainAPassed[j] = isKnownNeighborRoot; + } + allKnown[i] = chainAPassed.every((x) => x === true); + } + } +} +async function collectMetrics( + chains: LocalChain[], + relayerWallet: ethers.Wallet +): Promise { + let totalTransactions = 0; + let mTotalGasUsed = BigNumber.from(0); + for (const chain of chains) { + const wallet = relayerWallet.connect(chain.provider()); + const nonce = await wallet.getTransactionCount(); + totalTransactions += nonce; + const balanceAtStart = await wallet.getBalance(0); + const balanceNow = await wallet.getBalance(); + const gasUsed = balanceAtStart.sub(balanceNow); + mTotalGasUsed = mTotalGasUsed.add(gasUsed); + } + const totalRelayerTxCount = totalTransactions; + const avgRelayerTxCount = Math.round(totalTransactions / chains.length); + const totalGasUsed = ethers.utils.formatEther(mTotalGasUsed); + const avgGasUsed = ethers.utils.formatEther(mTotalGasUsed.div(chains.length)); + return { + totalRelayerTxCount, + avgRelayerTxCount, + totalGasUsed, + avgGasUsed, + totalWaitTimeToRelayAllRoots: 0, + }; +} diff --git a/tests/solidity-fixtures.dvc b/tests/solidity-fixtures.dvc index 43c07b7a0..f4f55f3eb 100644 --- a/tests/solidity-fixtures.dvc +++ b/tests/solidity-fixtures.dvc @@ -4,9 +4,9 @@ deps: - path: solidity-fixtures repo: url: https://github.com/webb-tools/solidity-fixtures - rev_lock: 8b3958d0141edb08860c5857a355c5fcf2097760 + rev_lock: 97cb9f8e2a11ef5e67c4d451c496fe15e089ca72 outs: -- md5: 708fc45dffdc8b8806f1c4d9786349d3.dir - size: 469101102 - nfiles: 61 +- md5: 5a60727de10eb4fc2486f04a16d9eb5d.dir + size: 6627655556 + nfiles: 230 path: solidity-fixtures diff --git a/tests/test/bridgeRegistry.test.ts b/tests/test/bridgeRegistry.test.ts new file mode 100644 index 000000000..45d9dd4ef --- /dev/null +++ b/tests/test/bridgeRegistry.test.ts @@ -0,0 +1,484 @@ +/* + * Copyright 2022 Webb Technologies Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// This is evm to substrate cross transaction relayer tests. +// In this test we will deposit on evm vanchor system +// and withdraw through substrate vanchor system. + +import '@webb-tools/tangle-substrate-types'; +import getPort, { portNumbers } from 'get-port'; +import temp from 'temp'; +import path from 'path'; +import isCi from 'is-ci'; +import retry from 'async-retry'; +import { ethers } from 'ethers'; +import { WebbRelayer, Pallet, EnabledContracts } from '../lib/webbRelayer.js'; +import { LocalTangle } from '../lib/localTangle.js'; +import { ApiPromise } from '@polkadot/api'; +import { u8aToHex, hexToU8a } from '@polkadot/util'; + +import { + calculateTypedChainId, + ChainType, + ProposalHeader, + CircomUtxo, + AnchorUpdateProposal, + ResourceId, +} from '@webb-tools/sdk-core'; + +import { createSubstrateResourceId } from '../lib/webbProposals.js'; +import { LocalChain } from '../lib/localTestnet.js'; +import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; +import { expect } from 'chai'; +import { defaultEventsWatcherValue } from '../lib/utils.js'; +import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; +import { KeyringPair } from '@polkadot/keyring/types.js'; +import { ethAddressFromUncompressedPublicKey } from '../lib/ethHelperFunctions.js'; +import { MintableToken } from '@webb-tools/tokens'; + +// These are for testing bridge registry pallet integration test for substrate chains. +// In this test relayer will directly fetch linked anchor config from bridge registry pallet. + +describe.skip('Bridge Registry Pallet Integration Test <=> Substrate', function () { + const tmpDirPath = temp.mkdirSync(); + // Evm node + let localChain1: LocalChain; + + // Tangle nodes + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; + + let webbRelayer: WebbRelayer; + let wallet1: ethers.Wallet; + let relayerExternalWallet: ethers.Wallet; + let relayerNativeWallet: KeyringPair; + let signatureVBridge: VBridge.VBridge; + + // Substrate vanchor treeId + let treeId: number; + const PK1 = u8aToHex(ethers.utils.randomBytes(32)); + const relayerPK = u8aToHex(ethers.utils.randomBytes(32)); + + before(async () => { + const usageMode: UsageMode = isCi + ? { mode: 'host', nodePath: 'tangle-standalone' } + : { + mode: 'host', + nodePath: path.resolve( + '../../tangle/target/release/tangle-standalone' + ), + }; + + const enabledPallets: Pallet[] = [ + { + pallet: 'DKGProposalHandler', + eventsWatcher: defaultEventsWatcherValue, + }, + { + pallet: 'DKG', + eventsWatcher: defaultEventsWatcherValue, + }, + { + pallet: 'VAnchorBn254', + eventsWatcher: defaultEventsWatcherValue, + }, + { + pallet: 'SignatureBridge', + eventsWatcher: defaultEventsWatcherValue, + }, + ]; + + // Step 1. We initialize DKG nodes. + aliceNode = await LocalTangle.start({ + name: 'dkg-alice', + authority: 'alice', + usageMode, + ports: 'auto', + enableLogging: false, + }); + + charlieNode = await LocalTangle.start({ + name: 'dkg-charlie', + authority: 'charlie', + usageMode, + ports: 'auto', + enableLogging: false, + }); + + // Wait until we are ready and connected. + const api = await aliceNode.api(); + await api.isReady; + console.log('Tangle node is ready'); + const substrateChainId = await aliceNode.getChainId(); + // We need to wait until the public key is on chain. + await aliceNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySignatureChanged', + }); + + // Create vanchor on Substrate chain with height 30 and maxEdges = 1 + const createVAnchorCall = api.tx.vAnchorBn254.create(1, 30, 0); + await aliceNode.sudoExecuteTransaction(createVAnchorCall); + const nextTreeId = await api.query.merkleTreeBn254.nextTreeId(); + treeId = nextTreeId.toNumber() - 1; + const substrateResourceId = createSubstrateResourceId( + substrateChainId, + treeId, + '0x2A' + ); + await substrateSetup( + aliceNode, + api, + treeId, + substrateChainId, + substrateResourceId + ); + console.log('substrate node ready'); + + // Step 3. We initialize Evm chain node. + const localChain1Port = await getPort({ + port: portNumbers(3333, 4444), + }); + + const enabledContracts: EnabledContracts[] = [ + { + contract: 'VAnchor', + }, + + { + contract: 'SignatureBridge', + }, + ]; + localChain1 = await LocalChain.init({ + port: localChain1Port, + chainId: localChain1Port, + name: 'Hermes', + populatedAccounts: [ + { + secretKey: PK1, + balance: ethers.utils + .parseEther('100000000000000000000000') + .toHexString(), + }, + { + secretKey: relayerPK, + balance: ethers.utils + .parseEther('100000000000000000000000') + .toHexString(), + }, + ], + enabledContracts: enabledContracts, + }); + wallet1 = new ethers.Wallet(PK1, localChain1.provider()); + // Deploy the token. + const localToken = await localChain1.deployToken('Webb Token', 'WEBB'); + const unwrappedToken = await MintableToken.createToken( + 'Webb Token', + 'WEBB', + wallet1 + ); + + signatureVBridge = await localChain1.deployVBridge( + localToken, + unwrappedToken, + wallet1, + wallet1 + ); + // Get the anchor on localchain1. + const vanchor = signatureVBridge.getVAnchor(localChain1.chainId); + await vanchor.setSigner(wallet1); + const evmResourceId = await vanchor.createResourceId(); + // relayer external wallet + relayerExternalWallet = new ethers.Wallet( + relayerPK, + localChain1.provider() + ); + console.log('Local evm chain ready'); + + await dkgSetup( + aliceNode, + api, + substrateChainId, + localChain1.underlyingChainId, + substrateResourceId.toString(), + evmResourceId, + relayerExternalWallet, + relayerNativeWallet + ); + // Step 4. We transfer ownership to DKG. + // Fetch current active governor from dkg node + const dkgPublicKey = await aliceNode.fetchDkgPublicKey(); + expect(dkgPublicKey).to.not.equal('0x'); + await transferOwnershipSubstrate(aliceNode, api, dkgPublicKey); + await transferOwnershipEvm(signatureVBridge, dkgPublicKey); + + // Step 5. We will send anchor update proposal to register bride and its resources on DKG node. + await sendAnchorUpdateProposal( + aliceNode, + api, + evmResourceId, + substrateResourceId.toString() + ); + + // Save tangle node chain configs. + await aliceNode.writeConfig(`${tmpDirPath}/${aliceNode.name}.json`, { + suri: '//Charlie', + chainId: substrateChainId, + proposalSigningBackend: { type: 'DKGNode', chainId: substrateChainId }, + enabledPallets, + }); + // Save evm node chain configs. + await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { + signatureVBridge, + proposalSigningBackend: { type: 'DKGNode', chainId: substrateChainId }, + privateKey: relayerPK, + }); + + // Now start the relayer. + const relayerPort = await getPort({ port: portNumbers(8000, 8888) }); + webbRelayer = new WebbRelayer({ + commonConfig: { + port: relayerPort, + }, + tmp: true, + configDir: tmpDirPath, + showLogs: true, + }); + await webbRelayer.waitUntilReady(); + }); + + it('should relay roots to linked anchors', async () => { + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); + await vanchor1.setSigner(wallet1); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const tokenAddress = signatureVBridge.getWebbTokenAddress( + localChain1.chainId + )!; + const token = await Tokens.MintableToken.tokenFromAddress( + tokenAddress, + wallet1 + ); + + const tx = await token.approveSpending( + vanchor1.contract.address, + ethers.utils.parseEther('1000') + ); + await tx.wait(); + + // Mint 1000 * 10^18 tokens to wallet1 + await token.mintTokens(wallet1.address, ethers.utils.parseEther('1000')); + const webbBalance = await token.getBalance(wallet1.address); + expect(webbBalance.toBigInt() > ethers.utils.parseEther('1').toBigInt()).to + .be.true; + + const substrateChainId = await aliceNode.getChainId(); + const typedTargetChainId = calculateTypedChainId( + ChainType.Substrate, + substrateChainId + ); + const typedSourceChainId = localChain1.chainId; + + // Step 5. We make a deposit amount on evm chain + const publicAmount = currencyToUnitI128(10); + const depositUtxo = await CircomUtxo.generateUtxo({ + curve: 'Bn254', + backend: 'Circom', + amount: publicAmount.toString(), + originChainId: typedSourceChainId.toString(), + chainId: typedTargetChainId.toString(), + }); + + const leaves = vanchor1.tree + .elements() + .map((el) => hexToU8a(el.toHexString())); + + await vanchor1.transact([], [depositUtxo], 0, 0, '0', '0', tokenAddress, { + [typedSourceChainId]: leaves, + }); + console.log('Deposit made'); + + // now we wait for the proposal to be signed by DKG backend and then send data to signature bridge. + await webbRelayer.waitForEvent({ + kind: 'signing_backend', + event: { + backend: 'DKG', + }, + }); + + // now we wait for proposals to be verified and executed by signature bridge through transaction queue. + await webbRelayer.waitForEvent({ + kind: 'tx_queue', + event: { + ty: 'SUBSTRATE', + chain_id: substrateChainId.toString(), + finalized: true, + }, + }); + }); + + after(async () => { + await localChain1?.stop(); + await aliceNode?.stop(); + await charlieNode?.stop(); + await webbRelayer?.stop(); + }); +}); + +// Helper methods, we can move them somewhere if we end up using them again. + +async function transferOwnershipSubstrate( + aliceNode: LocalTangle, + api: ApiPromise, + dkgPublicKey: `0x${string}` +) { + // force set maintainer + const refreshNonce = 0; + const setMaintainerCall = api.tx.signatureBridge.forceSetMaintainer( + refreshNonce, + dkgPublicKey + ); + await aliceNode.sudoExecuteTransaction(setMaintainerCall); +} + +async function transferOwnershipEvm( + signatureVBridge: VBridge.VBridge, + dkgPublicKey: `0x${string}` +) { + const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey); + // verify the governor address is a valid ethereum address. + expect(ethers.utils.isAddress(governorAddress)).to.be.true; + // transfer ownership to the DKG. + const sides = signatureVBridge.vBridgeSides.values(); + for (const signatureSide of sides) { + // now we transferOwnership, forcefully. + const tx = await signatureSide.transferOwnership(governorAddress, 1); + await retry( + async () => { + await tx.wait(); + }, + { + retries: 3, + minTimeout: 500, + onRetry: (error) => { + console.error('transferOwnership retry', error.name, error.message); + }, + } + ); + // check that the new governor is the same as the one we just set. + const currentGovernor = await signatureSide.contract.governor(); + expect(currentGovernor).to.eq(governorAddress); + } +} + +// Setup substrate chain, we will whitelist chains and register resourceId +async function substrateSetup( + aliceNode: LocalTangle, + api: ApiPromise, + treeId: number, + substrateChainId: number, + substrateResourceId: ResourceId +) { + // Whitelist chain on substrate node + const whitelistChainCall = + api.tx.signatureBridge.whitelistChain(substrateChainId); + await aliceNode.sudoExecuteTransaction(whitelistChainCall); + // Set resource on signature bridge + const setResourceCall = api.tx.signatureBridge.setResource( + substrateResourceId.toU8a() + ); + await aliceNode.sudoExecuteTransaction(setResourceCall); + // set resource on vanchor-handler + const forceSetResource = api.tx.vAnchorHandlerBn254.forceSetResource!( + substrateResourceId.toU8a(), + treeId + ); + await aliceNode.sudoExecuteTransaction(forceSetResource); +} + +async function dkgSetup( + dkgNode: LocalTangle, + dkgApi: ApiPromise, + substrateChainId: number, + evmChainId: number, + substrateResourceId: string, + evmResourceId: string, + relayerExternalWallet: ethers.Wallet, + relayerNativeWallet: KeyringPair +) { + // Whitelist chain in dkg node + const whitelistSubstrateChainDkgCall = dkgApi.tx.dkgProposals.whitelistChain({ + Substrate: substrateChainId, + }); + await dkgNode.sudoExecuteTransaction(whitelistSubstrateChainDkgCall); + + const whitelistEVMChainDkgCall = dkgApi.tx.dkgProposals.whitelistChain({ + Evm: evmChainId, + }); + await dkgNode.sudoExecuteTransaction(whitelistEVMChainDkgCall); + + // Set resources + const setResource1Call = dkgApi.tx.dkgProposals.setResource( + evmResourceId, + '0x' + ); + await dkgNode.sudoExecuteTransaction(setResource1Call); + const setResource2Call = dkgApi.tx.dkgProposals.setResource( + substrateResourceId, + '0x' + ); + await dkgNode.sudoExecuteTransaction(setResource2Call); + + // Add Proposer + const addProposerCall = dkgApi.tx.dkgProposals.addProposer( + relayerNativeWallet.address, + relayerExternalWallet.address + ); + await dkgNode.sudoExecuteTransaction(addProposerCall); +} + +async function sendAnchorUpdateProposal( + dkgNode: LocalTangle, + dkgApi: ApiPromise, + evmResourceId: string, + substrateResourceId: string +) { + // send dummy proposal to register bridge + const sourceResourceId = ResourceId.fromBytes(hexToU8a(evmResourceId)); + const targetResourceId = ResourceId.fromBytes(hexToU8a(substrateResourceId)); + const functionSignature = hexToU8a('0x00000002', 32); + const merkleRoot = u8aToHex(ethers.utils.randomBytes(32)); + const header = new ProposalHeader(targetResourceId, functionSignature, 0); + const anchorUpdateProposal = new AnchorUpdateProposal( + header, + merkleRoot, + sourceResourceId + ); + const kind = dkgApi.createType( + 'WebbProposalsProposalProposalKind', + 'AnchorUpdate' + ); + const prop = dkgApi.createType('WebbProposalsProposal', { + Unsigned: { + kind, + data: u8aToHex(anchorUpdateProposal.toU8a()), + }, + }); + + const submitUnsignedProposalCall = + dkgApi.tx.dkgProposalHandler.forceSubmitUnsignedProposal(prop.toU8a()); + await dkgNode.sudoExecuteTransaction(submitUnsignedProposalCall); + console.log('Dummy proposal sent'); +} diff --git a/tests/test/evm/RelayerTxTransfer.test.ts b/tests/test/evm/RelayerTxTransfer.test.ts new file mode 100644 index 000000000..c86060882 --- /dev/null +++ b/tests/test/evm/RelayerTxTransfer.test.ts @@ -0,0 +1,355 @@ +/* + * Copyright 2022 Webb Technologies Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// This our basic EVM Vanchor Transaction Relayer Tests. +// These are for testing the basic relayer functionality. which is just relay transactions for us. + +import { expect } from 'chai'; +import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; +import { + CircomUtxo, + Keypair, + parseTypedChainId, + toFixedHex, + Utxo, +} from '@webb-tools/sdk-core'; +import dotenv from 'dotenv'; +import { BigNumber, ethers } from 'ethers'; +import temp from 'temp'; +import { LocalChain } from '../../lib/localTestnet.js'; +import { + defaultWithdrawConfigValue, + EnabledContracts, + EvmFeeInfo, + EvmEtherscanConfig, + WebbRelayer, +} from '../../lib/webbRelayer.js'; +import getPort, { portNumbers } from 'get-port'; +import { u8aToHex, hexToU8a } from '@polkadot/util'; +import { MintableToken } from '@webb-tools/tokens'; +import { formatEther, parseEther } from 'ethers/lib/utils.js'; + +dotenv.config({ path: '../.env' }); +// This test is meant to prove that utxo transfer flows are possible, and the receiver +// can query on-chain data to construct and spend a utxo generated by the sender. +describe('Relayer transfer assets', function () { + const tmpDirPath = temp.mkdirSync(); + let localChain1: LocalChain; + let signatureVBridge: VBridge.VBridge; + let govWallet1: ethers.Wallet; + let relayerWallet1: ethers.Wallet; + + let webbRelayer: WebbRelayer; + + before(async () => { + const govPk = u8aToHex(ethers.utils.randomBytes(32)); + const relayerPk = u8aToHex(ethers.utils.randomBytes(32)); + // first we need to start local evm node. + const localChain1Port = await getPort({ + port: portNumbers(3333, 4444), + }); + + const enabledContracts: EnabledContracts[] = [ + { + contract: 'VAnchor', + }, + ]; + parseTypedChainId; + localChain1 = await LocalChain.init({ + port: localChain1Port, + chainId: localChain1Port, + name: 'Hermes', + populatedAccounts: [ + { + secretKey: govPk, + balance: ethers.utils + .parseEther('100000000000000000000000') + .toHexString(), + }, + { + secretKey: relayerPk, + balance: ethers.utils + .parseEther('100000000000000000000000') + .toHexString(), + }, + ], + enabledContracts: enabledContracts, + }); + + govWallet1 = new ethers.Wallet(govPk, localChain1.provider()); + relayerWallet1 = new ethers.Wallet(relayerPk, localChain1.provider()); + + // Deploy the token. + const wrappedToken1 = await localChain1.deployToken( + 'Wrapped Ethereum', + 'WETH' + ); + const unwrappedToken1 = await MintableToken.createToken( + 'Webb Token', + 'WEBB', + govWallet1 + ); + + signatureVBridge = await localChain1.deploySignatureVBridge( + localChain1, + wrappedToken1, + wrappedToken1, + govWallet1, + govWallet1, + unwrappedToken1, + unwrappedToken1 + ); + + // save the chain configs. + await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { + signatureVBridge, + withdrawConfig: defaultWithdrawConfigValue, + relayerWallet: relayerWallet1, + }); + + // get the vanhor on localchain1 + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); + await vanchor1.setSigner(govWallet1); + // get token + const tokenAddress = signatureVBridge.getWebbTokenAddress( + localChain1.chainId + )!; + const token = await Tokens.MintableToken.tokenFromAddress( + tokenAddress, + govWallet1 + ); + + // aprove token spending for vanchor + const tx = await token.approveSpending( + vanchor1.contract.address, + ethers.utils.parseEther('1000') + ); + await tx.wait(); + + // mint tokens on wallet + await token.mintTokens(govWallet1.address, ethers.utils.parseEther('1000')); + + // Set governor + const governorAddress = govWallet1.address; + const currentGovernor = await signatureVBridge + .getVBridgeSide(localChain1.chainId) + .contract.governor(); + expect(currentGovernor).to.eq(governorAddress); + + // now start the relayer + const relayerPort = await getPort({ port: portNumbers(9955, 9999) }); + + const evmEtherscan: EvmEtherscanConfig = { + ['mainnet']: { + chainId: localChain1.underlyingChainId, + apiKey: process.env.ETHERSCAN_API_KEY!, + }, + }; + + webbRelayer = new WebbRelayer({ + commonConfig: { + features: { dataQuery: false, governanceRelay: false }, + port: relayerPort, + evmEtherscan, + }, + tmp: true, + configDir: tmpDirPath, + showLogs: true, + verbosity: 3, + }); + await webbRelayer.waitUntilReady(); + }); + + it('should be able to transfer Utxo', async () => { + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); + await vanchor1.setSigner(govWallet1); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const tokenAddress = signatureVBridge.getWebbTokenAddress( + localChain1.chainId + )!; + + const token = await Tokens.MintableToken.tokenFromAddress( + tokenAddress, + govWallet1 + ); + + // Step 1. Register recipient account(Bob) + const bobKey = u8aToHex(ethers.utils.randomBytes(32)); + const bobWallet = new ethers.Wallet(bobKey, localChain1.provider()); + const bobKeypair = new Keypair(); + const tx = await vanchor1.contract.register({ + owner: govWallet1.address, + keyData: bobKeypair.toString(), + }); + + let receipt = await tx.wait(); + // Step 2. Sender queries on chain data for keypair information of recipient + // In this test, simply take the data from the previous transaction receipt. + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const registeredKeydata: string = receipt.events[0].args.key; + const bobPublicKeypair = Keypair.fromString(registeredKeydata); + + // Step 3. Generate a UTXO that is only spendable by recipient(Bob) + const transferUtxo = await CircomUtxo.generateUtxo({ + curve: 'Bn254', + backend: 'Circom', + amount: ethers.utils.parseEther('10').toString(), + originChainId: localChain1.chainId.toString(), + chainId: localChain1.chainId.toString(), + keypair: bobPublicKeypair, + }); + + // insert utxo into tree + receipt = (await vanchor1.transact( + [], + [transferUtxo], + 0, + 0, + '0', + relayerWallet1.address, + tokenAddress, + {} + )) as ethers.ContractReceipt; + + // Step 4. Recipient (Bob) queries encrypted commitments on chain. + // In this test, simply take the data from the previous transaction receipt. + const encryptedCommitments: string[] = + receipt.events + ?.filter((event) => event.event === 'NewCommitment') + .sort((a, b) => a.args?.index - b.args?.index) + .map((e) => e.args?.encryptedOutput) ?? []; + + // Attempt to decrypt the encrypted commitments with bob's keypair + const utxos = await Promise.all( + encryptedCommitments.map(async (enc, index) => { + try { + const decryptedUtxo = await CircomUtxo.decrypt(bobKeypair, enc); + // In order to properly calculate the nullifier, an index is required. + decryptedUtxo.setIndex(index); + decryptedUtxo.setOriginChainId(localChain1.chainId.toString()); + const alreadySpent = await vanchor1.contract.isSpent( + toFixedHex('0x' + decryptedUtxo.nullifier) + ); + if (!alreadySpent) { + return decryptedUtxo; + } else { + throw new Error('Passed Utxo detected as alreadySpent'); + } + } catch (e) { + return undefined; + } + }) + ); + + const spendableUtxos = utxos.filter( + (utxo): utxo is Utxo => utxo !== undefined + ); + const bobBalanceBefore = await token.getBalance(bobWallet.address); + console.log('Balance before ', bobBalanceBefore); + + // Step 5. Get Estimated Fee + const dummyUtxo = await CircomUtxo.generateUtxo({ + curve: 'Bn254', + backend: 'Circom', + amount: '0', + chainId: localChain1.chainId.toString(), + keypair: bobKeypair, + }); + // Inputs should be 2 or 16 based upon circuit,so pad it with dummy utxos. + spendableUtxos.push(dummyUtxo); + // fetch the inserted leaves + const leaves = vanchor1.tree + .elements() + .map((leaf) => hexToU8a(leaf.toHexString())); + const outputData = await vanchor1.setupTransaction( + spendableUtxos, + [dummyUtxo, dummyUtxo], + 0, + 0, + bobWallet.address, + relayerWallet1.address, + tokenAddress, + { + [localChain1.chainId.toString()]: leaves, + } + ); + + const gas_amount = await vanchor1.contract.estimateGas.transact( + outputData.publicInputs.proof, + '0x0000000000000000000000000000000000000000000000000000000000000000', + outputData.extData, + outputData.publicInputs, + outputData.extData + ); + + const feeInfoResponse = await webbRelayer.getEvmFeeInfo( + localChain1.underlyingChainId, + vanchor1.getAddress(), + gas_amount + ); + expect(feeInfoResponse.status).equal(200); + const feeInfo = await (feeInfoResponse.json() as Promise); + console.log(feeInfo); + const maxRefund = Number(formatEther(feeInfo.maxRefund)); + const refundExchangeRate = Number(formatEther(feeInfo.refundExchangeRate)); + const refundAmount = BigNumber.from( + parseEther((maxRefund * refundExchangeRate).toString()) + ); + const totalFee = refundAmount.add(feeInfo.estimatedFee); + + // Step 6. Use relayer to make transfer + const { extData, publicInputs } = await vanchor1.setupTransaction( + spendableUtxos, + [dummyUtxo, dummyUtxo], + totalFee, + refundAmount, + bobWallet.address, + relayerWallet1.address, + tokenAddress, + { + [localChain1.chainId.toString()]: leaves, + } + ); + + await webbRelayer.vanchorWithdraw( + localChain1.underlyingChainId, + vanchor1.getAddress(), + publicInputs, + extData + ); + // now we wait for relayer to execute private transaction. + await webbRelayer.waitForEvent({ + kind: 'private_tx', + event: { + ty: 'EVM', + chain_id: localChain1.underlyingChainId.toString(), + finalized: true, + }, + }); + // Step 7. Check recipient(Bob) balance. This account is used to receive funds after withdrawal + const bobBalanceAfter = await token.getBalance(bobWallet.address); + console.log('Balance after ', bobBalanceAfter); + expect(bobBalanceAfter.toBigInt() > bobBalanceBefore.toBigInt()).to.be.true; + }); + + after(async () => { + await localChain1?.stop(); + await webbRelayer?.stop(); + }); +}); diff --git a/tests/test/evm/evmToSubstrateTransaction.test.ts b/tests/test/evm/evmToSubstrateTransaction.test.ts index 686582412..ccf895fae 100644 --- a/tests/test/evm/evmToSubstrateTransaction.test.ts +++ b/tests/test/evm/evmToSubstrateTransaction.test.ts @@ -18,22 +18,21 @@ // In this test we will deposit on evm vanchor system // and withdraw through substrate vanchor system. -import '@webb-tools/protocol-substrate-types'; import getPort, { portNumbers } from 'get-port'; import temp from 'temp'; import path from 'path'; import fs from 'fs'; import isCi from 'is-ci'; import child from 'child_process'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { WebbRelayer, Pallet, EnabledContracts, } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { ApiPromise, Keyring } from '@polkadot/api'; +import { ApiPromise } from '@polkadot/api'; import { u8aToHex, hexToU8a } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { naclEncrypt, randomAsU8a } from '@polkadot/util-crypto'; @@ -60,29 +59,30 @@ import { LocalChain } from '../../lib/localTestnet.js'; import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; import { expect } from 'chai'; import { + createAccount, defaultEventsWatcherValue, generateVAnchorNote, } from '../../lib/utils.js'; import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; import { VAnchor } from '@webb-tools/anchors'; +import { MintableToken } from '@webb-tools/tokens'; const { ecdsaSign } = pkg; describe('Cross chain transaction <<>> Mocked Backend', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; let wallet1: ethers.Wallet; let signatureVBridge: VBridge.VBridge; // Mnemonic seed phrase to created governor key used for signing proposals. - const filePath = path.join(path.resolve('../'), 'sample_seed.txt'); - const mnemonic = fs.readFileSync(filePath); + // DO NOT USE IN PRODUCTION + const mnemonic = + 'point shiver hurt flight fun online hub antenna engine pave chef fantasy front interest poem accident catch load frequent praise elite pet remove used'; const governorWallet = ethers.Wallet.fromMnemonic(mnemonic.toString()); const GOV = governorWallet.privateKey; - // File path to secrets containing mnemonic seed phrase - const secretFile = `file:${filePath}`; const PK1 = u8aToHex(ethers.utils.randomBytes(32)); // slice 0x04 from public key const uncompressedKey = governorWallet @@ -96,7 +96,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { : { mode: 'host', nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -110,7 +110,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { }, ]; - aliceNode = await LocalProtocolSubstrate.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, @@ -118,9 +118,9 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { enableLogging: false, }); - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', + charlieNode = await LocalTangle.start({ + name: 'substrate-charlie', + authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, @@ -162,11 +162,19 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { }); wallet1 = new ethers.Wallet(PK1, localChain1.provider()); + // Deploy the token. - const localToken1 = await localChain1.deployToken('Webb Token', 'WEBB'); + const localToken = await localChain1.deployToken('Webb Token', 'WEBB'); + + const unwrappedToken = await MintableToken.createToken( + 'Webb Token', + 'WEBB', + wallet1 + ); signatureVBridge = await localChain1.deployVBridge( - localToken1, + localToken, + unwrappedToken, wallet1, governorWallet ); @@ -181,7 +189,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { const substrateResourceId = createSubstrateResourceId( substrateChainId, 6, - '0x2C' + '0x2A' ); // save the substrate chain configs @@ -196,7 +204,10 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { // save the evm chain configs. await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { signatureVBridge, - proposalSigningBackend: { type: 'Mocked', privateKey: secretFile }, + proposalSigningBackend: { + type: 'Mocked', + privateKey: governorWallet.privateKey, + }, linkedAnchors: [ { type: 'Raw', resourceId: substrateResourceId.toString() }, ], @@ -207,7 +218,9 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { // 2. We need to whitelist chain Id // force set maintainer + const refreshNonce = 0; const setMaintainerCall = api.tx.signatureBridge.forceSetMaintainer( + refreshNonce, `0x${uncompressedKey}` ); await aliceNode.sudoExecuteTransaction(setMaintainerCall); @@ -324,6 +337,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { typedTargetChainId, 0 ); + webbRelayer.clearLogs(); // substrate vanchor withdraw await vanchorWithdraw( vanchor1, @@ -357,7 +371,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { after(async () => { await localChain1?.stop(); await aliceNode?.stop(); - await bobNode?.stop(); + await charlieNode?.stop(); await webbRelayer?.stop(); }); }); @@ -372,7 +386,7 @@ async function setResourceIdProposal( ): Promise> { const functionSignature = hexToU8a('0x00000002', 32); const nonce = 1; - const palletIndex = '0x2C'; + const palletIndex = '0x2A'; const callIndex = '0x02'; // set resource ID const resourceId = createSubstrateResourceId(chainId, treeId, palletIndex); @@ -412,7 +426,7 @@ async function vanchorWithdraw( depositNote: Note, treeId: number, api: ApiPromise, - aliceNode: LocalProtocolSubstrate + aliceNode: LocalTangle ) { const account = createAccount('//Dave'); const typedSourceChainId = depositNote.note.sourceChainId; @@ -436,7 +450,7 @@ async function vanchorWithdraw( const pk_hex = fs.readFileSync(pkPath).toString('hex'); const pk = hexToU8a(pk_hex); - let note1 = depositNote; + const note1 = depositNote; const note2 = await note1.getDefaultUtxoNote(); const publicAmount = currencyToUnitI128(publicAmountUint); const notes = [note1, note2]; @@ -457,7 +471,7 @@ async function vanchorWithdraw( // Configure a new proving manager with direct call const provingManager = new ArkworksProvingManager(null); const leavesMap = {}; - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. const address = account.address; const extAmount = currencyToUnitI128(10); const fee = 0; @@ -509,11 +523,11 @@ async function vanchorWithdraw( fee, refund: String(refund), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toNumber(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; - let vanchorProofData = { + const vanchorProofData = { proof: `0x${data.proof}`, publicAmount: data.publicAmount, roots: rootsSet, @@ -523,7 +537,7 @@ async function vanchorWithdraw( }; // now we call the vanchor transact to withdraw on substrate - let transactCall = api.tx.vAnchorBn254!.transact!( + const transactCall = api.tx.vAnchorBn254.transact( treeId, vanchorProofData, extData @@ -531,10 +545,3 @@ async function vanchorWithdraw( const txSigned = await transactCall.signAsync(account); await aliceNode.executeTransaction(txSigned); } - -function createAccount(accountId: string): any { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} diff --git a/tests/test/evm/governorUpdates.test.ts b/tests/test/evm/governorUpdates.test.ts index 76971e621..3a0cca497 100644 --- a/tests/test/evm/governorUpdates.test.ts +++ b/tests/test/evm/governorUpdates.test.ts @@ -16,23 +16,22 @@ */ // A simple test for Updating the Signature Bridge Governor when DKG Rotates. - +import '@webb-tools/tangle-substrate-types'; import Chai, { expect } from 'chai'; import ChaiAsPromised from 'chai-as-promised'; import { VBridge, Tokens } from '@webb-tools/protocol-solidity'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import temp from 'temp'; import retry from 'async-retry'; import { LocalChain } from '../../lib/localTestnet.js'; import { sleep } from '../../lib/sleep.js'; -import { timeout } from '../../lib/timeout.js'; import { Pallet, WebbRelayer, EnabledContracts, } from '../../lib/webbRelayer.js'; import getPort, { portNumbers } from 'get-port'; -import { LocalDkg } from '../../lib/localDkg.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import isCi from 'is-ci'; import path from 'path'; import { ethAddressFromUncompressedPublicKey } from '../../lib/ethHelperFunctions.js'; @@ -44,7 +43,8 @@ import { MintableToken } from '@webb-tools/tokens'; // to support chai-as-promised Chai.use(ChaiAsPromised); -// FIXME: this test is skipped since there is an issue with manual DKG Refresh. +// FIXME: This test is currently broken. It needs to be fixed. +// The node hangs at 30 blocks and does not proceed further. describe.skip('SignatureBridge Governor Updates', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; @@ -54,9 +54,8 @@ describe.skip('SignatureBridge Governor Updates', function () { let wallet2: ethers.Wallet; // dkg nodes - let aliceNode: LocalDkg; - let bobNode: LocalDkg; - let charlieNode: LocalDkg; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; @@ -64,11 +63,11 @@ describe.skip('SignatureBridge Governor Updates', function () { const PK1 = u8aToHex(ethers.utils.randomBytes(32)); const PK2 = u8aToHex(ethers.utils.randomBytes(32)); const usageMode: UsageMode = isCi - ? { mode: 'host', nodePath: 'dkg-standalone-node' } + ? { mode: 'docker', forcePullImage: false } : { mode: 'host', nodePath: path.resolve( - '../../dkg-substrate/target/release/dkg-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -81,29 +80,28 @@ describe.skip('SignatureBridge Governor Updates', function () { eventsWatcher: defaultEventsWatcherValue, }, ]; - aliceNode = await LocalDkg.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, ports: 'auto', }); - bobNode = await LocalDkg.start({ - name: 'substrate-bob', - authority: 'bob', - usageMode, - ports: 'auto', - }); - - charlieNode = await LocalDkg.start({ + charlieNode = await LocalTangle.start({ name: 'substrate-charlie', authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, }); - + // Wait until we are ready and connected + const api = await charlieNode.api(); + await api.isReady; + console.log( + 'tangle node ready waiting for dkg public key to be set onchain' + ); const chainId = await charlieNode.getChainId(); + await charlieNode.writeConfig(`${tmpDirPath}/${charlieNode.name}.json`, { suri: '//Charlie', chainId: chainId, @@ -189,16 +187,16 @@ describe.skip('SignatureBridge Governor Updates', function () { // save the chain configs. await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, + proposalSigningBackend: { type: 'DKGNode', chainId }, }); await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, + proposalSigningBackend: { type: 'DKGNode', chainId }, }); // fetch the dkg public key. const dkgPublicKey = await charlieNode.fetchDkgPublicKey(); - expect(dkgPublicKey).to.not.be.null; - const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey!); + expect(dkgPublicKey).to.not.equal('0x'); + const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey); // verify the governor address is a valid ethereum address. expect(ethers.utils.isAddress(governorAddress)).to.be.true; // transfer ownership to the DKG. @@ -223,7 +221,7 @@ describe.skip('SignatureBridge Governor Updates', function () { expect(currentGovernor).to.eq(governorAddress); } // get the anhor on localchain1 - const anchor = signatureBridge.getVAnchor(localChain1.chainId)!; + const anchor = signatureBridge.getVAnchor(localChain1.chainId); await anchor.setSigner(wallet1); // approve token spending const tokenAddress = signatureBridge.getWebbTokenAddress( @@ -241,7 +239,7 @@ describe.skip('SignatureBridge Governor Updates', function () { await token.mintTokens(wallet1.address, ethers.utils.parseEther('1000')); // do the same but on localchain2 - const anchor2 = signatureBridge.getVAnchor(localChain2.chainId)!; + const anchor2 = signatureBridge.getVAnchor(localChain2.chainId); await anchor2.setSigner(wallet2); const tokenAddress2 = signatureBridge.getWebbTokenAddress( localChain2.chainId @@ -258,12 +256,11 @@ describe.skip('SignatureBridge Governor Updates', function () { await tx.wait(); await token2.mintTokens(wallet2.address, ethers.utils.parseEther('1000')); - const api = await charlieNode.api(); const resourceId1 = await anchor.createResourceId(); const resourceId2 = await anchor2.createResourceId(); const call = (resourceId: string) => - api.tx.dkgProposals!.setResource!(resourceId, '0x00'); + api.tx.dkgProposals.setResource(resourceId, '0x00'); // register the resource on DKG node. for (const rid of [resourceId1, resourceId2]) { await charlieNode.sudoExecuteTransaction(call(rid)); @@ -276,22 +273,13 @@ describe.skip('SignatureBridge Governor Updates', function () { }, tmp: true, configDir: tmpDirPath, - showLogs: false, + showLogs: true, verbosity: 3, }); await webbRelayer.waitUntilReady(); }); it('ownership should be transfered when the DKG rotates', async () => { - // now we just need to force the DKG to rotate/refresh. - const api = await charlieNode.api(); - const forceIncrementNonce = api.tx.dkg.manualIncrementNonce(); - const forceRefresh = api.tx.dkg.manualRefresh(); - await timeout( - charlieNode.sudoExecuteTransaction(forceIncrementNonce), - 30_000 - ); - await timeout(charlieNode.sudoExecuteTransaction(forceRefresh), 60_000); // now we just need for the relayer to pick up the new DKG events. // and update both chains' signature bridge governor. await Promise.all([ @@ -316,8 +304,8 @@ describe.skip('SignatureBridge Governor Updates', function () { await sleep(1000); // now we need to check that the ownership was transfered. const dkgPublicKey = await charlieNode.fetchDkgPublicKey(); - expect(dkgPublicKey).to.not.be.null; - const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey!); + expect(dkgPublicKey).to.not.equal('0x'); + const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey); const sides = signatureBridge.vBridgeSides.values(); for (const signatureSide of sides) { const contract = signatureSide.contract; @@ -328,7 +316,6 @@ describe.skip('SignatureBridge Governor Updates', function () { after(async () => { await aliceNode?.stop(); - await bobNode?.stop(); await charlieNode?.stop(); await localChain1?.stop(); await localChain2?.stop(); diff --git a/tests/test/evm/invalidProviders.test.ts b/tests/test/evm/invalidProviders.test.ts new file mode 100644 index 000000000..8da5866c0 --- /dev/null +++ b/tests/test/evm/invalidProviders.test.ts @@ -0,0 +1,133 @@ +import http from 'node:http'; +import fs from 'node:fs'; +import getPort from 'get-port'; +import temp from 'temp'; +import Chai, { expect } from 'chai'; +import ChaiAsPromised from 'chai-as-promised'; + +import { WebbRelayer } from '../../lib/webbRelayer.js'; + +// to support chai-as-promised +Chai.use(ChaiAsPromised); + +/** + * An Invalid Provider that always returns an error when queried. + * It is used to test the error handling of the EVM. + */ +abstract class InvalidProvider { + private readonly server: http.Server; + + constructor(public readonly port: number) { + this.server = http + .createServer((request, response) => this.handle(request, response)) + .listen(port); + } + + public async destroy(): Promise { + await new Promise((resolve, reject) => { + this.server.close((error) => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + } + + public get url(): string { + return `http://localhost:${this.port}`; + } + + protected abstract handle( + request: http.IncomingMessage, + response: http.ServerResponse + ): void; +} + +class RateLimitedProvider extends InvalidProvider { + public get errorMessage(): string { + return '

429 Too Many Requests

You have sent too many requests in a given amount of time.'; + } + + protected handle( + _request: http.IncomingMessage, + response: http.ServerResponse + ): void { + response.writeHead(429, { + 'Content-Type': 'text/html', + 'X-Content-Type-Options': 'nosniff', + }); + response.end(this.errorMessage); + } +} + +describe('Invalid EVM Providers', () => { + describe('Rate Limited Provider', function () { + const tmpDirPath = temp.mkdirSync(); + let provider: RateLimitedProvider; + let webbRelayer: WebbRelayer; + + before(async function () { + this.timeout(0); + provider = new RateLimitedProvider(await getPort()); + + const config = { + evm: { + 1: { + name: 'rate-limited-provider', + enabled: true, + 'chain-id': 1, + 'http-endpoint': provider.url, + 'ws-endpoint': 'ws://localhost:8546', + 'block-confirmations': 0, + contracts: [ + { + contract: 'VAnchor', + address: '0x0000000000000000000000000000000000000000', + 'deployed-at': 0, + 'events-watcher': { + enabled: true, + 'polling-interval': 100, + 'print-progress-interval': 1000, + 'linked-anchors': [], + }, + }, + ], + }, + }, + }; + + const configString = JSON.stringify(config, null, 2); + fs.writeFileSync( + `${tmpDirPath}/rate-limited-provider.json`, + configString + ); + // now start the relayer + const relayerPort = await getPort(); + webbRelayer = new WebbRelayer({ + commonConfig: { + port: relayerPort, + }, + tmp: true, + configDir: tmpDirPath, + showLogs: true, + verbosity: 3, + }); + }); + + it('should keep retrying with the rate limited provider', async function () { + await webbRelayer.waitForEvent({ + kind: 'retry', + event: { + should_retry: true, + }, + }); + }); + + after(async () => { + await webbRelayer.stop(); + await provider.destroy(); + }); + }); +}); diff --git a/tests/test/evm/openVAnchorGovernance.test.ts b/tests/test/evm/openVAnchorGovernance.test.ts deleted file mode 100644 index 5b5c7174c..000000000 --- a/tests/test/evm/openVAnchorGovernance.test.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2022 Webb Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { BigNumber } from 'ethers'; -import { expect } from 'chai'; -import { MintableToken } from '@webb-tools/tokens'; -import { ethers } from 'ethers'; -import temp from 'temp'; -import { LocalChain } from '../../lib/localTestnetOpenVBridge.js'; -import { EnabledContracts, WebbRelayer } from '../../lib/webbRelayer.js'; -import getPort, { portNumbers } from 'get-port'; -import { u8aToHex, hexToU8a } from '@polkadot/util'; -import { OpenVBridge } from '@webb-tools/vbridge'; - -describe('Open VAnchor Governance Relayer', function () { - const tmpDirPath = temp.mkdirSync(); - let localChain1: LocalChain; - let localChain2: LocalChain; - let signatureVBridge: OpenVBridge; - let wallet1: ethers.Wallet; - let wallet2: ethers.Wallet; - let localToken1: MintableToken; - let localToken2: MintableToken; - - let webbRelayer: WebbRelayer; - - before(async () => { - const PK1 = u8aToHex(ethers.utils.randomBytes(32)); - const PK2 = u8aToHex(ethers.utils.randomBytes(32)); - const GOV = u8aToHex(ethers.utils.randomBytes(32)); - const localChain1Port = await getPort({ - port: portNumbers(3333, 4444), - }); - - const enabledContracts: EnabledContracts[] = [ - { - contract: 'OpenVAnchor', - }, - ]; - - localChain1 = await LocalChain.init({ - port: localChain1Port, - chainId: 5001, - name: 'Hermes', - populatedAccounts: [ - { - secretKey: PK1, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - { - secretKey: GOV, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - ], - enabledContracts: enabledContracts, - }); - - const localChain2Port = await getPort({ - port: portNumbers(3333, 4444), - }); - - localChain2 = await LocalChain.init({ - port: localChain2Port, - chainId: 5002, - name: 'Athena', - populatedAccounts: [ - { - secretKey: PK2, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - { - secretKey: GOV, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - ], - enabledContracts: enabledContracts, - }); - - wallet1 = new ethers.Wallet(PK1, localChain1.provider()); - wallet2 = new ethers.Wallet(PK2, localChain2.provider()); - // Deploy the token. - localToken1 = await localChain1.deployToken('Webb Token', 'WEBB', wallet1); - localToken2 = await localChain2.deployToken('Webb Token', 'WEBB', wallet2); - - const govWallet = new ethers.Wallet(GOV); - signatureVBridge = await localChain1.deploySignatureVBridge( - localChain2, - localToken1, - localToken2, - wallet1, - wallet2, - { - [localChain1.chainId]: govWallet.address, - [localChain2.chainId]: govWallet.address, - } - ); - - const governorAddress = govWallet.address; - const sides = signatureVBridge.vBridgeSides.values(); - for (const signatureSide of sides) { - // check that the new governor is the same as the one we just set. - const currentGovernor = await signatureSide.contract.governor(); - expect(currentGovernor).to.eq(governorAddress); - } - // get the anhor on localchain1 - const openVAnchor = signatureVBridge.getVAnchor(localChain1.chainId); - await openVAnchor.setSigner(wallet1); - // approve token spending - const token = await MintableToken.tokenFromAddress( - localToken1.contract.address, - wallet1 - ); - await token.mintTokens(wallet1.address, '1000000'); - - // do the same but on localchain2 - const openVAnchor2 = signatureVBridge.getVAnchor(localChain2.chainId); - await openVAnchor2.setSigner(wallet2); - const token2 = await MintableToken.tokenFromAddress( - localToken2.contract.address, - wallet2 - ); - await token2.mintTokens(wallet2.address, '1000000'); - const resourceId1 = await openVAnchor.createResourceId(); - const resourceId2 = await openVAnchor2.createResourceId(); - // save the chain configs. - await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { - signatureVBridge, - proposalSigningBackend: { type: 'Mocked', privateKey: GOV }, - linkedAnchors: [{ type: 'Raw', resourceId: resourceId2 }], - }); - await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { - signatureVBridge, - proposalSigningBackend: { type: 'Mocked', privateKey: GOV }, - linkedAnchors: [{ type: 'Raw', resourceId: resourceId1 }], - }); - // now start the relayer - const relayerPort = await getPort({ port: portNumbers(9955, 9999) }); - webbRelayer = new WebbRelayer({ - tmp: true, - commonConfig: { - port: relayerPort, - }, - configDir: tmpDirPath, - showLogs: false, - verbosity: 4, - }); - await webbRelayer.waitUntilReady(); - }); - - it('should handle AnchorUpdateProposal when a deposit happens', async () => { - // we will use chain1 as an example here. - const openVAnchor1 = signatureVBridge.getVAnchor(localChain1.chainId); - const openVAnchor2 = signatureVBridge.getVAnchor(localChain2.chainId); - await openVAnchor2.setSigner(wallet2); - const wrappedTokenAddress = await signatureVBridge.getWebbTokenAddress( - localChain1.chainId - )!; - console.log(wrappedTokenAddress, await openVAnchor1.contract.token()); - const token = await MintableToken.tokenFromAddress( - localToken1.contract.address, - wallet1 - ); - // Approve the wrapped token to spend the wrapping token. - let tx = await token.contract.approve(wrappedTokenAddress, 1000000, { - from: wallet1.address, - }); - await tx.wait(); - - const depositAmount = 100; - const destChainId = localChain2.chainId; - const recipient = await wallet1.getAddress(); - const delegatedCalldata = '0x00'; - const blinding = BigNumber.from(1010101010); - await openVAnchor1.setSigner(wallet1); - tx = await openVAnchor1.contract.wrapAndDeposit( - destChainId, - depositAmount, - recipient, - delegatedCalldata, - blinding, - BigNumber.from(1010101010), - token.contract.address - ); - await tx.wait(); - - tx = await openVAnchor1.contract.wrapAndDeposit( - destChainId, - depositAmount, - recipient, - delegatedCalldata, - blinding, - BigNumber.from(1010101011), - token.contract.address - ); - await tx.wait(); - - // wait until the signature bridge recives the execute call. - await webbRelayer.waitForEvent({ - kind: 'signature_bridge', - event: { chain_id: localChain2.underlyingChainId.toString() }, - }); - // now we wait for the tx queue on that chain to execute the transaction. - await webbRelayer.waitForEvent({ - kind: 'tx_queue', - event: { - ty: 'EVM', - chain_id: localChain2.underlyingChainId.toString(), - finalized: true, - }, - }); - // all is good, last thing is to check for the roots. - const srcChainRoot = await openVAnchor1.contract.getLastRoot(); - const neigborRoots = await openVAnchor2.contract.getLatestNeighborRoots(); - const edges = await openVAnchor2.contract.getLatestNeighborEdges(); - const isKnownNeighborRoot = neigborRoots.some( - (root: BigNumber) => root.toHexString() === srcChainRoot.toHexString() - ); - if (!isKnownNeighborRoot) { - console.log({ - srcChainRoot, - neigborRoots, - edges, - isKnownNeighborRoot, - }); - } - expect(isKnownNeighborRoot).to.be.true; - }); - - after(async () => { - await localChain1?.stop(); - await localChain2?.stop(); - await webbRelayer?.stop(); - }); -}); diff --git a/tests/test/proposals.test.ts b/tests/test/evm/proposals.test.ts similarity index 75% rename from tests/test/proposals.test.ts rename to tests/test/evm/proposals.test.ts index cb7c3356a..b7fc95676 100644 --- a/tests/test/proposals.test.ts +++ b/tests/test/evm/proposals.test.ts @@ -16,8 +16,7 @@ */ // Testing different kind of proposals between DKG <=> Relayer <=> Signature Bridge. - -import '@webb-tools/protocol-substrate-types'; +import '@webb-tools/tangle-substrate-types'; import Chai, { expect } from 'chai'; import ChaiAsPromised from 'chai-as-promised'; import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; @@ -25,14 +24,18 @@ import { u8aToHex } from '@polkadot/util'; import { ethers } from 'ethers'; import temp from 'temp'; import retry from 'async-retry'; -import { LocalChain } from '../lib/localTestnet.js'; -import { Pallet, WebbRelayer, EnabledContracts } from '../lib/webbRelayer.js'; +import { LocalChain } from '../../lib/localTestnet.js'; +import { + Pallet, + WebbRelayer, + EnabledContracts, +} from '../../lib/webbRelayer.js'; import getPort, { portNumbers } from 'get-port'; -import { LocalDkg } from '../lib/localDkg.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import isCi from 'is-ci'; import path from 'path'; -import { ethAddressFromUncompressedPublicKey } from '../lib/ethHelperFunctions.js'; -import { sleep } from '../lib/sleep.js'; +import { ethAddressFromUncompressedPublicKey } from '../../lib/ethHelperFunctions.js'; +import { sleep } from '../../lib/sleep.js'; import { ProposalHeader, ResourceId, @@ -43,40 +46,37 @@ import { import { hexToU8a } from '@polkadot/util'; import { ChainType } from '@webb-tools/sdk-core'; import { UsageMode } from '@webb-tools/test-utils'; -import { defaultEventsWatcherValue } from '../lib/utils.js'; +import { defaultEventsWatcherValue } from '../../lib/utils.js'; import { MintableToken } from '@webb-tools/tokens'; // to support chai-as-promised Chai.use(ChaiAsPromised); -describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { - // 8 minutes - this.timeout(8 * 60 * 1000); +describe('Proposals (DKG <=> Relayer <=> SigBridge)', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; let localChain2: LocalChain; let signatureBridge: VBridge.VBridge; let wallet1: ethers.Wallet; let wallet2: ethers.Wallet; + let relayerWallet1: ethers.Wallet; // dkg nodes - let aliceNode: LocalDkg; - let bobNode: LocalDkg; - let charlieNode: LocalDkg; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; before(async function () { - // Only run these tests in CI - if (!isCi) this.skip(); const PK1 = u8aToHex(ethers.utils.randomBytes(32)); const PK2 = u8aToHex(ethers.utils.randomBytes(32)); + const relayerPk = u8aToHex(ethers.utils.randomBytes(32)); const usageMode: UsageMode = isCi - ? { mode: 'host', nodePath: 'dkg-standalone-node' } + ? { mode: 'docker', forcePullImage: false } : { mode: 'host', nodePath: path.resolve( - '../../dkg-substrate/target/release/dkg-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -84,22 +84,20 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { pallet: 'DKGProposalHandler', eventsWatcher: defaultEventsWatcherValue, }, + { + pallet: 'DKG', + eventsWatcher: defaultEventsWatcherValue, + }, ]; - aliceNode = await LocalDkg.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, ports: 'auto', + enableLogging: false, }); - bobNode = await LocalDkg.start({ - name: 'substrate-bob', - authority: 'bob', - usageMode, - ports: 'auto', - }); - - charlieNode = await LocalDkg.start({ + charlieNode = await LocalTangle.start({ name: 'substrate-charlie', authority: 'charlie', usageMode, @@ -107,8 +105,13 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { enableLogging: false, }); - // get chainId + const api = await charlieNode.api(); + await api.isReady; + console.log( + 'tangle node ready waiting for dkg public key to be set onchain' + ); const chainId = await charlieNode.getChainId(); + await charlieNode.writeConfig(`${tmpDirPath}/${charlieNode.name}.json`, { suri: '//Charlie', chainId: chainId, @@ -118,7 +121,7 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { // we need to wait until the public key is on chain. await charlieNode.waitForEvent({ section: 'dkg', - method: 'PublicKeySubmitted', + method: 'PublicKeySignatureChanged', }); // next we need to start local evm node. @@ -128,7 +131,7 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { const enabledContracts: EnabledContracts[] = [ { - contract: 'Anchor', + contract: 'VAnchor', }, ]; localChain1 = await LocalChain.init({ @@ -140,6 +143,10 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { secretKey: PK1, balance: ethers.utils.parseEther('1000').toHexString(), }, + { + secretKey: relayerPk, + balance: ethers.utils.parseEther('1000').toHexString(), + }, ], enabledContracts: enabledContracts, }); @@ -157,12 +164,17 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { secretKey: PK2, balance: ethers.utils.parseEther('1000').toHexString(), }, + { + secretKey: relayerPk, + balance: ethers.utils.parseEther('1000').toHexString(), + }, ], enabledContracts: enabledContracts, }); wallet1 = new ethers.Wallet(PK1, localChain1.provider()); wallet2 = new ethers.Wallet(PK2, localChain2.provider()); + relayerWallet1 = new ethers.Wallet(relayerPk, localChain1.provider()); // Deploy the token. const wrappedToken1 = await localChain1.deployToken( 'Wrapped Ethereum', @@ -195,16 +207,18 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { // save the chain configs. await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, + proposalSigningBackend: { type: 'DKGNode', chainId }, + relayerWallet: relayerWallet1, }); await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, + proposalSigningBackend: { type: 'DKGNode', chainId }, + relayerWallet: relayerWallet1, }); // fetch the dkg public key. const dkgPublicKey = await charlieNode.fetchDkgPublicKey(); - expect(dkgPublicKey).to.not.be.null; - const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey!); + expect(dkgPublicKey).to.not.equal('0x'); + const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey); // verify the governor address is a valid ethereum address. expect(ethers.utils.isAddress(governorAddress)).to.be.true; // transfer ownership to the DKG. @@ -264,7 +278,6 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { await tx.wait(); await token2.mintTokens(wallet2.address, ethers.utils.parseEther('1000')); - const api = await charlieNode.api(); const resourceId1 = await anchor.createResourceId(); const resourceId2 = await anchor2.createResourceId(); const governedTokenAddress = anchor.token!; @@ -285,10 +298,11 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { webbRelayer = new WebbRelayer({ tmp: true, commonConfig: { + features: { privateTxRelay: false }, port: relayerPort, }, configDir: tmpDirPath, - showLogs: false, + showLogs: true, verbosity: 3, }); await webbRelayer.waitUntilReady(); @@ -311,18 +325,18 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { const resourceId = ResourceId.newFromContractAddress( governedTokenAddress, ChainType.EVM, - await governedToken.signer.getChainId() + localChain1.underlyingChainId ); const functionSignature = hexToU8a( governedToken.contract.interface.getSighash( - governedToken.contract.interface.functions['add(address,uint256)'] + governedToken.contract.interface.functions['add(address,uint32)'] ) ); const nonce = await governedToken.contract.proposalNonce(); const proposalHeader = new ProposalHeader( resourceId, functionSignature, - nonce.toNumber() + nonce.add(1).toNumber() ); const tokenAddProposal = new TokenAddProposal( proposalHeader, @@ -332,15 +346,20 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { kind: 'TokenAdd', data: u8aToHex(tokenAddProposal.toU8a()), }); + // now we wait for the proposal to be signed. charlieNode.waitForEvent({ section: 'dkgProposalHandler', method: 'ProposalSigned', }); + // now we wait for the proposal to be executed by the relayer then by the Signature Bridge. await webbRelayer.waitForEvent({ kind: 'signature_bridge', - event: { chain_id: localChain1.underlyingChainId.toString() }, + event: { + chain_id: localChain1.underlyingChainId.toString(), + call: 'execute_proposal_with_signature', + }, }); // now we wait for the tx queue on that chain to execute the transaction. await webbRelayer.waitForEvent({ @@ -351,14 +370,39 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { finalized: true, }, }); - await sleep(1000); + await sleep(5000); // now we check that the token was added. const tokens = await governedToken.contract.getTokens(); expect(tokens.includes(testToken.contract.address)).to.eq(true); }); - it('should handle TokenRemoveProposal', async () => { - // get the anhor on localchain1 + it.skip('should handle TokenRemoveProposal', async () => { + // we need to wait until the public key is changed. + await charlieNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySignatureChanged', + }); + // wait until the signature bridge receives the transfer ownership call. + await webbRelayer.waitForEvent({ + kind: 'signature_bridge', + event: { + chain_id: localChain2.underlyingChainId.toString(), + call: 'transfer_ownership_with_signature_pub_key', + }, + }); + + // now we wait for the tx queue on that chain to execute the transfer ownership transaction. + await webbRelayer.waitForEvent({ + kind: 'tx_queue', + event: { + ty: 'EVM', + chain_id: localChain2.underlyingChainId.toString(), + finalized: true, + }, + }); + + webbRelayer.clearLogs(); + // get the anchor on localchain1 const anchor = signatureBridge.getVAnchor(localChain1.chainId); const governedTokenAddress = anchor.token!; const governedToken = Tokens.FungibleTokenWrapper.connect( @@ -369,40 +413,47 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { const tokenToRemove = currentTokens[0]; expect(tokenToRemove).to.not.be.undefined; // but first, remove all realyer old events (as in reset the event listener) - webbRelayer.clearLogs(); + const resourceId = ResourceId.newFromContractAddress( governedTokenAddress, ChainType.EVM, - await governedToken.signer.getChainId() + localChain1.underlyingChainId ); const functionSignature = hexToU8a( governedToken.contract.interface.getSighash( - governedToken.contract.interface.functions['remove(address,uint256)'] + governedToken.contract.interface.functions['remove(address,uint32)'] ) ); const nonce = await governedToken.contract.proposalNonce(); const proposalHeader = new ProposalHeader( resourceId, functionSignature, - nonce.toNumber() + nonce.add(1).toNumber() ); - const tokenAddProposal = new TokenRemoveProposal( + const tokenRemoveProposal = new TokenRemoveProposal( proposalHeader, tokenToRemove! ); + await forceSubmitUnsignedProposal(charlieNode, { kind: 'TokenRemove', - data: u8aToHex(tokenAddProposal.toU8a()), + data: u8aToHex(tokenRemoveProposal.toU8a()), }); + console.log(' TokenRemove Proposal submitted'); + // now we wait for the proposal to be signed. charlieNode.waitForEvent({ section: 'dkgProposalHandler', method: 'ProposalSigned', }); + // now we wait for the proposal to be executed by the relayer then by the Signature Bridge. await webbRelayer.waitForEvent({ kind: 'signature_bridge', - event: { chain_id: localChain1.underlyingChainId.toString() }, + event: { + chain_id: localChain1.underlyingChainId.toString(), + call: 'execute_proposal_with_signature', + }, }); // now we wait for the tx queue on that chain to execute the transaction. await webbRelayer.waitForEvent({ @@ -413,13 +464,38 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { finalized: true, }, }); - await sleep(1000); + await sleep(5000); // now we check that the token was removed. const tokens = await governedToken.contract.getTokens(); expect(tokens.includes(tokenToRemove!)).to.eq(false); }); it('should handle WrappingFeeUpdateProposal', async () => { + // we need to wait until the public key is changed. + await charlieNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySignatureChanged', + }); + // wait until the signature bridge receives the transfer ownership call. + await webbRelayer.waitForEvent({ + kind: 'signature_bridge', + event: { + chain_id: localChain2.underlyingChainId.toString(), + call: 'transfer_ownership_with_signature_pub_key', + }, + }); + + // now we wait for the tx queue on that chain to execute the transfer ownership transaction. + await webbRelayer.waitForEvent({ + kind: 'tx_queue', + event: { + ty: 'EVM', + chain_id: localChain2.underlyingChainId.toString(), + finalized: true, + }, + }); + + webbRelayer.clearLogs(); // get the anhor on localchain1 const anchor = signatureBridge.getVAnchor(localChain1.chainId); const governedTokenAddress = anchor.token!; @@ -435,15 +511,15 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { const nonce = await governedToken.contract.proposalNonce(); const functionSignature = hexToU8a( governedToken.contract.interface.getSighash( - governedToken.contract.interface.functions['setFee(uint8,uint256)'] + governedToken.contract.interface.functions['setFee(uint16,uint32)'] ) ); const proposalHeader = new ProposalHeader( resourceId, functionSignature, - nonce.toNumber() + nonce.add(1).toNumber() ); - webbRelayer.clearLogs(); + const newFee = ethers.utils.hexValue(50); const wrappingFeeProposalPayload = new WrappingFeeUpdateProposal( proposalHeader, @@ -453,15 +529,21 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { kind: 'WrappingFeeUpdate', data: u8aToHex(wrappingFeeProposalPayload.toU8a()), }); + console.log('WrappingFeeUpdate Proposal submitted'); + // now we wait for the proposal to be signed. charlieNode.waitForEvent({ section: 'dkgProposalHandler', method: 'ProposalSigned', }); + // now we wait for the proposal to be executed by the relayer then by the Signature Bridge. await webbRelayer.waitForEvent({ kind: 'signature_bridge', - event: { chain_id: localChain1.underlyingChainId.toString() }, + event: { + chain_id: localChain1.underlyingChainId.toString(), + call: 'execute_proposal_with_signature', + }, }); // now we wait for the tx queue on that chain to execute the transaction. await webbRelayer.waitForEvent({ @@ -472,14 +554,13 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { finalized: true, }, }); - await sleep(1000); + await sleep(10000); const fee = await governedToken.contract.getFee(); expect(newFee).to.eq(ethers.utils.hexValue(fee)); }); after(async () => { await aliceNode?.stop(); - await bobNode?.stop(); await charlieNode?.stop(); await localChain1?.stop(); await localChain2?.stop(); @@ -490,19 +571,16 @@ describe.skip('Proposals (DKG <=> Relayer <=> SigBridge)', function () { type WebbProposalKind = 'TokenAdd' | 'TokenRemove' | 'WrappingFeeUpdate'; async function forceSubmitUnsignedProposal( - node: LocalDkg, + node: LocalTangle, opts: { kind: WebbProposalKind; data: `0x${string}`; } ) { const api = await node.api(); - const kind = api.createType( - 'DkgRuntimePrimitivesProposalProposalKind', - opts.kind - ); + const kind = api.createType('WebbProposalsProposalProposalKind', opts.kind); const proposal = api - .createType('DkgRuntimePrimitivesProposal', { + .createType('WebbProposalsProposal', { Unsigned: { kind, data: opts.data, diff --git a/tests/test/signatureBridge.test.ts b/tests/test/evm/signatureBridge.test.ts similarity index 56% rename from tests/test/signatureBridge.test.ts rename to tests/test/evm/signatureBridge.test.ts index 609c94f5c..9d7397054 100644 --- a/tests/test/signatureBridge.test.ts +++ b/tests/test/evm/signatureBridge.test.ts @@ -23,48 +23,51 @@ import { hexToU8a, u8aToHex } from '@polkadot/util'; import { BigNumber, ethers } from 'ethers'; import temp from 'temp'; import retry from 'async-retry'; -import { LocalChain } from '../lib/localTestnet.js'; -import { Pallet, WebbRelayer, EnabledContracts } from '../lib/webbRelayer.js'; +import { LocalChain } from '../../lib/localTestnet.js'; +import { + Pallet, + WebbRelayer, + EnabledContracts, +} from '../../lib/webbRelayer.js'; import getPort, { portNumbers } from 'get-port'; -import { LocalDkg } from '../lib/localDkg.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import isCi from 'is-ci'; import path from 'path'; -import { ethAddressFromUncompressedPublicKey } from '../lib/ethHelperFunctions.js'; +import { ethAddressFromUncompressedPublicKey } from '../../lib/ethHelperFunctions.js'; import { CircomUtxo, Keypair } from '@webb-tools/sdk-core'; import { UsageMode } from '@webb-tools/test-utils'; -import { defaultEventsWatcherValue } from '../lib/utils.js'; +import { defaultEventsWatcherValue } from '../../lib/utils.js'; import { MintableToken } from '@webb-tools/tokens'; // to support chai-as-promised Chai.use(ChaiAsPromised); -describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { - this.timeout(5 * 60 * 1000); +describe('Signature Bridge <> DKG Proposal Signing Backend', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; let localChain2: LocalChain; let signatureBridge: VBridge.VBridge; let wallet1: ethers.Wallet; let wallet2: ethers.Wallet; + let relayerWallet: ethers.Wallet; // dkg nodes - let aliceNode: LocalDkg; - let bobNode: LocalDkg; - let charlieNode: LocalDkg; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; before(async function () { - // skip this test if we are locally running, only run on the CI - if (!isCi) this.skip(); const PK1 = u8aToHex(ethers.utils.randomBytes(32)); const PK2 = u8aToHex(ethers.utils.randomBytes(32)); + const relayerPk = u8aToHex(ethers.utils.randomBytes(32)); + const usageMode: UsageMode = isCi - ? { mode: 'host', nodePath: 'dkg-standalone-node' } + ? { mode: 'docker', forcePullImage: false } : { mode: 'host', nodePath: path.resolve( - '../../dkg-substrate/target/release/dkg-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -72,29 +75,31 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { pallet: 'DKGProposalHandler', eventsWatcher: defaultEventsWatcherValue, }, + { + pallet: 'DKG', + eventsWatcher: defaultEventsWatcherValue, + }, ]; - aliceNode = await LocalDkg.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, ports: 'auto', + enableLogging: false, }); - bobNode = await LocalDkg.start({ - name: 'substrate-bob', - authority: 'bob', - usageMode, - ports: 'auto', - }); - - charlieNode = await LocalDkg.start({ + charlieNode = await LocalTangle.start({ name: 'substrate-charlie', authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, }); - // get chainId + const api = await charlieNode.api(); + await api.isReady; + console.log( + 'tangle node ready waiting for dkg public key to be set onchain' + ); const chainId = await charlieNode.getChainId(); await charlieNode.writeConfig(`${tmpDirPath}/${charlieNode.name}.json`, { suri: '//Charlie', @@ -105,7 +110,7 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { // we need to wait until the public key is on chain. await charlieNode.waitForEvent({ section: 'dkg', - method: 'PublicKeySubmitted', + method: 'PublicKeySignatureChanged', }); // next we need to start local evm node. @@ -115,7 +120,7 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { const enabledContracts: EnabledContracts[] = [ { - contract: 'Anchor', + contract: 'VAnchor', }, ]; @@ -128,6 +133,10 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { secretKey: PK1, balance: ethers.utils.parseEther('1000').toHexString(), }, + { + secretKey: relayerPk, + balance: ethers.utils.parseEther('1000').toHexString(), + }, ], enabledContracts: enabledContracts, }); @@ -145,12 +154,18 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { secretKey: PK2, balance: ethers.utils.parseEther('1000').toHexString(), }, + { + secretKey: relayerPk, + balance: ethers.utils.parseEther('1000').toHexString(), + }, ], enabledContracts: enabledContracts, }); wallet1 = new ethers.Wallet(PK1, localChain1.provider()); wallet2 = new ethers.Wallet(PK2, localChain2.provider()); + relayerWallet = new ethers.Wallet(relayerPk, localChain1.provider()); + // Deploy the token. const wrappedToken1 = await localChain1.deployToken( 'Wrapped Ethereum', @@ -180,19 +195,11 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { unwrappedToken1, unwrappedToken2 ); - // save the chain configs. - await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { - signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, - }); - await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { - signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'DKGNode', node: charlieNode.name }, - }); + // fetch the dkg public key. const dkgPublicKey = await charlieNode.fetchDkgPublicKey(); - expect(dkgPublicKey).to.not.be.null; - const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey!); + expect(dkgPublicKey).to.not.equal('0x'); + const governorAddress = ethAddressFromUncompressedPublicKey(dkgPublicKey); // verify the governor address is a valid ethereum address. expect(ethers.utils.isAddress(governorAddress)).to.be.true; // transfer ownership to the DKG. @@ -216,6 +223,7 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { const currentGovernor = await signatureSide.contract.governor(); expect(currentGovernor).to.eq(governorAddress); } + // get the anhor on localchain1 const anchor = signatureBridge.getVAnchor(localChain1.chainId); await anchor.setSigner(wallet1); @@ -252,77 +260,70 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { await tx.wait(); await token2.mintTokens(wallet2.address, ethers.utils.parseEther('1000')); - const api = await charlieNode.api(); const resourceId1 = await anchor.createResourceId(); const resourceId2 = await anchor2.createResourceId(); - const call = (resourceId: string) => + const setResourceIdCall = (resourceId: string) => api.tx.dkgProposals.setResource(resourceId, '0x00'); - // register the resource on DKG node. + // register the resource on tangle node. for (const rid of [resourceId1, resourceId2]) { - await charlieNode.sudoExecuteTransaction(call(rid)); + await charlieNode.sudoExecuteTransaction(setResourceIdCall(rid)); + } + // Whitelist chainId on tangle node + const whitelistChainIdCall = (chain_id: number) => + api.tx.dkgProposals.whitelistChain({ Evm: chain_id }); + for (const chain_id of [ + localChain1.underlyingChainId, + localChain2.underlyingChainId, + ]) { + await charlieNode.sudoExecuteTransaction(whitelistChainIdCall(chain_id)); } + + // save the chain configs. + await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { + signatureVBridge: signatureBridge, + proposalSigningBackend: { type: 'DKGNode', chainId }, + linkedAnchors: [{ type: 'Raw', resourceId: resourceId2 }], + relayerWallet, + }); + await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { + signatureVBridge: signatureBridge, + proposalSigningBackend: { type: 'DKGNode', chainId }, + linkedAnchors: [{ type: 'Raw', resourceId: resourceId1 }], + relayerWallet, + }); + // now start the relayer const relayerPort = await getPort({ port: portNumbers(9955, 9999) }); webbRelayer = new WebbRelayer({ commonConfig: { + features: { governanceRelay: true }, port: relayerPort, }, tmp: true, configDir: tmpDirPath, - showLogs: false, + showLogs: true, verbosity: 3, }); await webbRelayer.waitUntilReady(); }); it('should handle AnchorUpdateProposal when a deposit happens using DKG proposal backend', async () => { - // we will use chain1 as an example here. - const anchor1 = signatureBridge.getVAnchor(localChain1.chainId); - const anchor2 = signatureBridge.getVAnchor(localChain2.chainId); - await anchor1.setSigner(wallet1); - const tokenAddress = signatureBridge.getWebbTokenAddress( - localChain1.chainId - )!; - const token = await Tokens.MintableToken.tokenFromAddress( - tokenAddress, - wallet1 - ); - - await token.mintTokens(wallet1.address, ethers.utils.parseEther('1000')); - const webbBalance = await token.getBalance(wallet1.address); - expect(webbBalance.toBigInt() > ethers.utils.parseEther('1').toBigInt()).to - .be.true; - - // Create a deposit utxo - const randomKeypair = new Keypair(); - const depositUtxo = await CircomUtxo.generateUtxo({ - curve: 'Bn254', - backend: 'Circom', - amount: (1e2).toString(), - originChainId: localChain1.chainId.toString(), - chainId: localChain2.chainId.toString(), - keypair: randomKeypair, + // we need to wait until the public key is changed. + await charlieNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySignatureChanged', }); - - // Make the deposit transaction - await signatureBridge.transact( - [], - [depositUtxo], - 0, - 0, - '0', - '0', - tokenAddress, - wallet1 - ); - - // wait until the signature bridge recives the execute call. + // wait until the signature bridge receives the transfer ownership call. await webbRelayer.waitForEvent({ kind: 'signature_bridge', - event: { chain_id: localChain2.underlyingChainId.toString() }, + event: { + chain_id: localChain2.underlyingChainId.toString(), + call: 'transfer_ownership_with_signature_pub_key', + }, }); - // now we wait for the tx queue on that chain to execute the transaction. + + // now we wait for the tx queue on that chain to execute the transfer ownership transaction. await webbRelayer.waitForEvent({ kind: 'tx_queue', event: { @@ -331,202 +332,8 @@ describe.skip('Signature Bridge <> DKG Proposal Signing Backend', function () { finalized: true, }, }); - // all is good, last thing is to check for the roots. - const srcChainRoot = await anchor1.contract.getLastRoot(); - const neigborRoots = await anchor2.contract.getLatestNeighborRoots(); - const edges = await anchor2.contract.getLatestNeighborEdges(); - const isKnownNeighborRoot = neigborRoots.some( - (root: BigNumber) => root.toHexString() === srcChainRoot.toHexString() - ); - if (!isKnownNeighborRoot) { - console.log({ - srcChainRoot, - neigborRoots, - edges, - isKnownNeighborRoot, - }); - } - expect(isKnownNeighborRoot).to.be.true; - }); - after(async () => { - await aliceNode?.stop(); - await bobNode?.stop(); - await charlieNode?.stop(); - await localChain1?.stop(); - await localChain2?.stop(); - await webbRelayer?.stop(); - }); -}); - -describe('Signature Bridge <> Mocked Proposal Signing Backend', function () { - const tmpDirPath = temp.mkdirSync(); - let localChain1: LocalChain; - let localChain2: LocalChain; - let signatureBridge: VBridge.VBridge; - let wallet1: ethers.Wallet; - let wallet2: ethers.Wallet; - let govWallet: ethers.Wallet; - - let webbRelayer: WebbRelayer; - - before(async () => { - const PK1 = u8aToHex(ethers.utils.randomBytes(32)); - const PK2 = u8aToHex(ethers.utils.randomBytes(32)); - const GOV = u8aToHex(ethers.utils.randomBytes(32)); - const localChain1Port = await getPort({ - port: portNumbers(3333, 4444), - }); - - const enabledContracts: EnabledContracts[] = [ - { - contract: 'VAnchor', - }, - { - contract: 'SignatureBridge', - }, - ]; - - localChain1 = await LocalChain.init({ - port: localChain1Port, - chainId: localChain1Port, - name: 'Hermes', - populatedAccounts: [ - { - secretKey: PK1, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - { - secretKey: GOV, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - ], - enabledContracts: enabledContracts, - }); - - const localChain2Port = await getPort({ - port: portNumbers(3333, 4444), - }); - - localChain2 = await LocalChain.init({ - port: localChain2Port, - chainId: localChain2Port, - name: 'Athena', - populatedAccounts: [ - { - secretKey: PK2, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - { - secretKey: GOV, - balance: ethers.utils.parseEther('1000').toHexString(), - }, - ], - enabledContracts: enabledContracts, - }); - - wallet1 = new ethers.Wallet(PK1, localChain1.provider()); - wallet2 = new ethers.Wallet(PK2, localChain2.provider()); - govWallet = new ethers.Wallet(GOV, localChain1.provider()); - // Deploy the token. - const wrappedToken1 = await localChain1.deployToken( - 'Wrapped Ethereum', - 'WETH' - ); - const wrappedToken2 = await localChain2.deployToken( - 'Wrapped Ethereum', - 'WETH' - ); - const unwrappedToken1 = await MintableToken.createToken( - 'Webb Token', - 'WEBB', - wallet1 - ); - const unwrappedToken2 = await MintableToken.createToken( - 'Webb Token', - 'WEBB', - wallet2 - ); - - signatureBridge = await localChain1.deploySignatureVBridge( - localChain2, - wrappedToken1, - wrappedToken2, - wallet1, - wallet2, - unwrappedToken1, - unwrappedToken2, - { - [localChain1.chainId]: govWallet.address, - [localChain2.chainId]: govWallet.address, - } - ); - - // get the anhor on localchain1 - const anchor = signatureBridge.getVAnchor(localChain1.chainId); - await anchor.setSigner(wallet1); - // approve token spending - const tokenAddress = signatureBridge.getWebbTokenAddress( - localChain1.chainId - )!; - const token = await Tokens.MintableToken.tokenFromAddress( - tokenAddress, - wallet1 - ); - let tx = await token.approveSpending( - anchor.contract.address, - ethers.utils.parseEther('1000') - ); - await tx.wait(); - await token.mintTokens(wallet1.address, ethers.utils.parseEther('1000')); - - // do the same but on localchain2 - const anchor2 = signatureBridge.getVAnchor(localChain2.chainId); - await anchor2.setSigner(wallet2); - const tokenAddress2 = signatureBridge.getWebbTokenAddress( - localChain2.chainId - )!; - const token2 = await Tokens.MintableToken.tokenFromAddress( - tokenAddress2, - wallet2 - ); - - tx = await token2.approveSpending( - anchor2.contract.address, - ethers.utils.parseEther('1000') - ); - await tx.wait(); - await token2.mintTokens(wallet2.address, ethers.utils.parseEther('1000')); - - const resourceId1 = await anchor.createResourceId(); - const resourceId2 = await anchor2.createResourceId(); - // save the chain configs. - await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { - signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'Mocked', privateKey: GOV }, - linkedAnchors: [{ type: 'Raw', resourceId: resourceId2 }], - }); - await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { - signatureVBridge: signatureBridge, - proposalSigningBackend: { type: 'Mocked', privateKey: GOV }, - linkedAnchors: [{ type: 'Raw', resourceId: resourceId1 }], - }); - - // now start the relayer - const relayerPort = await getPort({ port: portNumbers(9955, 9999) }); - webbRelayer = new WebbRelayer({ - commonConfig: { - port: relayerPort, - }, - tmp: true, - configDir: tmpDirPath, - showLogs: false, - verbosity: 3, - }); - await webbRelayer.waitUntilReady(); - }); - - it('should handle AnchorUpdateProposal when a deposit happens using mocked proposal backend', async () => { + webbRelayer.clearLogs(); // we will use chain1 as an example here. const anchor1 = signatureBridge.getVAnchor(localChain1.chainId); const anchor2 = signatureBridge.getVAnchor(localChain2.chainId); @@ -538,6 +345,7 @@ describe('Signature Bridge <> Mocked Proposal Signing Backend', function () { tokenAddress, wallet1 ); + await token.mintTokens(wallet1.address, ethers.utils.parseEther('1000')); const webbBalance = await token.getBalance(wallet1.address); expect(webbBalance.toBigInt() > ethers.utils.parseEther('1').toBigInt()).to @@ -555,7 +363,6 @@ describe('Signature Bridge <> Mocked Proposal Signing Backend', function () { }); // Make the deposit transaction - const leaves = anchor1.tree .elements() .map((el) => hexToU8a(el.toHexString())); @@ -564,10 +371,19 @@ describe('Signature Bridge <> Mocked Proposal Signing Backend', function () { [localChain1.chainId]: leaves, }); - // wait until the signature bridge recives the execute call. + // now we wait for the proposal to be signed. + charlieNode.waitForEvent({ + section: 'dkgProposalHandler', + method: 'ProposalSigned', + }); + + // wait until the signature bridge receives the execute call. await webbRelayer.waitForEvent({ kind: 'signature_bridge', - event: { chain_id: localChain2.underlyingChainId.toString() }, + event: { + chain_id: localChain2.underlyingChainId.toString(), + call: 'execute_proposal_with_signature', + }, }); // now we wait for the tx queue on that chain to execute the transaction. await webbRelayer.waitForEvent({ @@ -597,6 +413,8 @@ describe('Signature Bridge <> Mocked Proposal Signing Backend', function () { }); after(async () => { + await aliceNode?.stop(); + await charlieNode?.stop(); await localChain1?.stop(); await localChain2?.stop(); await webbRelayer?.stop(); diff --git a/tests/test/evm/vanchorPrivateTransaction.test.ts b/tests/test/evm/vanchorPrivateTransaction.test.ts index ef8ae7e4f..01b1b7d71 100644 --- a/tests/test/evm/vanchorPrivateTransaction.test.ts +++ b/tests/test/evm/vanchorPrivateTransaction.test.ts @@ -20,14 +20,15 @@ import { expect } from 'chai'; import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; import { CircomUtxo, Keypair, parseTypedChainId } from '@webb-tools/sdk-core'; - +import dotenv from 'dotenv'; import { BigNumber, ethers } from 'ethers'; import temp from 'temp'; import { LocalChain, setupVanchorEvmTx } from '../../lib/localTestnet.js'; import { defaultWithdrawConfigValue, EnabledContracts, - FeeInfo, + EvmFeeInfo, + EvmEtherscanConfig, ResourceMetricResponse, WebbRelayer, } from '../../lib/webbRelayer.js'; @@ -36,6 +37,8 @@ import { u8aToHex, hexToU8a } from '@polkadot/util'; import { MintableToken } from '@webb-tools/tokens'; import { formatEther, parseEther } from 'ethers/lib/utils.js'; +dotenv.config({ path: '../.env' }); + describe('Vanchor Private Tx relaying with mocked governor', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; @@ -213,9 +216,16 @@ describe('Vanchor Private Tx relaying with mocked governor', function () { // now start the relayer const relayerPort = await getPort({ port: portNumbers(9955, 9999) }); + const evmEtherscan: EvmEtherscanConfig = { + ['mainnet']: { + chainId: localChain2.underlyingChainId, + apiKey: process.env.ETHERSCAN_API_KEY!, + }, + }; webbRelayer = new WebbRelayer({ commonConfig: { features: { dataQuery: false, governanceRelay: false }, + evmEtherscan, port: relayerPort, }, tmp: true, @@ -290,13 +300,13 @@ describe('Vanchor Private Tx relaying with mocked governor', function () { dummyOutput.extData ); - const feeInfoResponse = await webbRelayer.getFeeInfo( - localChain2.chainId, + const feeInfoResponse = await webbRelayer.getEvmFeeInfo( + localChain2.underlyingChainId, vanchor2.getAddress(), gas_amount ); expect(feeInfoResponse.status).equal(200); - const feeInfo = await (feeInfoResponse.json() as Promise); + const feeInfo = await (feeInfoResponse.json() as Promise); console.log(feeInfo); const maxRefund = Number(formatEther(feeInfo.maxRefund)); const refundExchangeRate = Number(formatEther(feeInfo.refundExchangeRate)); diff --git a/tests/test/evm/vanchorTransactionRelayer.test.ts b/tests/test/evm/vanchorTransactionRelayer.test.ts index c90faf4d2..4f5aa473f 100644 --- a/tests/test/evm/vanchorTransactionRelayer.test.ts +++ b/tests/test/evm/vanchorTransactionRelayer.test.ts @@ -19,7 +19,7 @@ import { expect } from 'chai'; import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; -import { CircomUtxo } from '@webb-tools/sdk-core'; +import { CircomUtxo, toFixedHex } from '@webb-tools/sdk-core'; import { ethers } from 'ethers'; import temp from 'temp'; import { LocalChain } from '../../lib/localTestnet.js'; @@ -128,14 +128,17 @@ describe('Vanchor Transaction relayer', function () { } ); + const dummyEndpoint = 'http://127.0.0.1:1080'; // save the chain configs. await localChain1.writeConfig(`${tmpDirPath}/${localChain1.name}.json`, { signatureVBridge, proposalSigningBackend: { type: 'Mocked', privateKey: PK1 }, + httpEndpoints: [dummyEndpoint, localChain1.endpoint], }); await localChain2.writeConfig(`${tmpDirPath}/${localChain2.name}.json`, { signatureVBridge, proposalSigningBackend: { type: 'Mocked', privateKey: PK2 }, + httpEndpoints: [dummyEndpoint, localChain2.endpoint], }); // get the vanhor on localchain1 @@ -188,6 +191,9 @@ describe('Vanchor Transaction relayer', function () { }); it('number of deposits made should be equal to number of leaves in cache', async () => { + // sometimes this a flakes, so we retry it. + this.retries(3); + const vanchor1 = signatureVBridge.getVAnchor(localChain1.chainId); const vanchor2 = signatureVBridge.getVAnchor(localChain2.chainId); @@ -211,8 +217,18 @@ describe('Vanchor Transaction relayer', function () { expect(webbBalance.toBigInt() > ethers.utils.parseEther('1').toBigInt()).to .be.true; - // Make 5 deposits - for (let i = 0; i < 5; i++) { + const chainId = localChain1.underlyingChainId.toString(); + const response0 = await webbRelayer.getLeavesEvm( + chainId, + vanchor1.contract.address + ); + expect(response0.status).equal(200); + const alreadyCachedLeaves = + response0.json() as Promise; + const depositsToMake = 5; + const alreadyCachedLeavesCount = (await alreadyCachedLeaves).leaves.length; + // Make 5 deposits (10 leaves) + for (let i = 0; i < depositsToMake; i++) { // Define inputs/outputs utxo for transact function const depositUtxo = await CircomUtxo.generateUtxo({ curve: 'Bn254', @@ -231,20 +247,21 @@ describe('Vanchor Transaction relayer', function () { }); } + const expectedLeavesCount = alreadyCachedLeavesCount + depositsToMake * 2; + // now we wait for all deposits to be saved in LeafStorageCache await webbRelayer.waitForEvent({ kind: 'leaves_store', event: { - leaf_index: '9', + leaf_index: (expectedLeavesCount - 1).toString(), }, }); - const expectedLeaves = vanchor1.tree - .elements() - .map((el) => Array.from(hexToU8a(el.toHexString()))); + const expectedLeaves = vanchor1.tree.elements().map((el) => toFixedHex(el)); + expect(expectedLeaves.length).to.equal(expectedLeavesCount); + // now we call relayer leaf API to check no of leaves stored in LeafStorageCache // are equal to no of deposits made. Each VAnchor deposit generates 2 leaf entries - const chainId = localChain1.underlyingChainId.toString(); const response = await webbRelayer.getLeavesEvm( chainId, vanchor1.contract.address @@ -252,8 +269,9 @@ describe('Vanchor Transaction relayer', function () { expect(response.status).equal(200); const leavesStore = response.json() as Promise; await leavesStore.then((resp) => { - expect(resp.leaves.length).to.equal(10); - expect(resp.leaves).to.deep.equal(expectedLeaves); + const leaves = resp.leaves; + expect(leaves.length).to.equal(expectedLeaves.length); + expect(leaves).to.deep.equal(expectedLeaves); }); // Query a range of leaves. @@ -268,7 +286,10 @@ describe('Vanchor Transaction relayer', function () { const response1 = await webbRelayer.getLeavesEvm( chainId, vanchor1.contract.address, - { start: 20, end: 30 } + { + start: expectedLeavesCount + 2, + end: expectedLeavesCount + 4, + } ); expect(response1.status).equal(200); const leavesStore1 = response1.json() as Promise; @@ -286,8 +307,10 @@ describe('Vanchor Transaction relayer', function () { expect(response2.status).equal(200); const leavesStore2 = response2.json() as Promise; await leavesStore2.then((resp) => { - expect(resp.leaves.length).to.equal(4); - expect(resp.leaves).to.deep.equal(expectedLeaves.slice(1, 5)); + const leaves = resp.leaves; + expect(leaves.length).to.equal(5 - 1); + const expectedLeavesSlice = expectedLeaves.slice(1, 5); + expect(leaves).to.deep.equal(expectedLeavesSlice); }); // 2.1 Querying a range of leaves that are present in the cache (4..7). @@ -300,22 +323,28 @@ describe('Vanchor Transaction relayer', function () { expect(response21.status).equal(200); const leavesStore21 = response21.json() as Promise; await leavesStore21.then((resp) => { - expect(resp.leaves.length).to.equal(3); - expect(resp.leaves).to.deep.equal(expectedLeaves.slice(4, 7)); + const leaves = resp.leaves; + expect(leaves.length).to.equal(7 - 4); + const expectedLeavesSlice = expectedLeaves.slice(4, 7); + expect(leaves).to.deep.equal(expectedLeavesSlice); }); - // 3. Querying a range of leaves that are partially present in the cache (1..12) - // We will query leaves from 1 to 12 + // 3. Querying a range of leaves that are partially present in the cache (1..(alreadyCachedLeavesCount + depositsToMake * 2 + 4))) const response3 = await webbRelayer.getLeavesEvm( chainId, vanchor1.contract.address, - { start: 1, end: 12 } + { start: 1, end: expectedLeavesCount + 4 } ); expect(response3.status).equal(200); const leavesStore3 = response3.json() as Promise; await leavesStore3.then((resp) => { - expect(resp.leaves.length).to.equal(9); - expect(resp.leaves).to.deep.equal(expectedLeaves.slice(1, 12)); + const leaves = resp.leaves; + expect(leaves.length).to.equal(expectedLeavesCount - 1); + const expectedLeavesSlice = expectedLeaves.slice( + 1, + expectedLeavesCount + 4 + ); + expect(leaves).to.deep.equal(expectedLeavesSlice); }); // 4. Querying a range of leaves that is reverse (9..0) @@ -332,8 +361,10 @@ describe('Vanchor Transaction relayer', function () { expect(response4.status).equal(200); const leavesStore4 = response4.json() as Promise; await leavesStore4.then((resp) => { - expect(resp.leaves.length).to.equal(1); - expect(resp.leaves).to.deep.equal(expectedLeaves.slice(9)); + const leaves = resp.leaves; + expect(leaves.length).to.equal(1); + const expectedLeavesSlice = expectedLeaves.slice(9); + expect(leaves).to.deep.equal(expectedLeavesSlice); }); }); diff --git a/tests/test/substrate/governorUpdate.test.ts b/tests/test/substrate/governorUpdate.test.ts new file mode 100644 index 000000000..acf149b03 --- /dev/null +++ b/tests/test/substrate/governorUpdate.test.ts @@ -0,0 +1,167 @@ +/* + * Copyright 2022 Webb Technologies Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// This our basic Substrate VAnchor Transaction Relayer Tests. +// These are for testing the basic relayer functionality. which is just to relay transactions for us. + +import '@webb-tools/tangle-substrate-types'; +import { expect } from 'chai'; +import getPort, { portNumbers } from 'get-port'; +import temp from 'temp'; +import path from 'path'; +import isCi from 'is-ci'; +import { WebbRelayer, Pallet } from '../../lib/webbRelayer.js'; +import { LocalTangle } from '../../lib/localTangle.js'; +import { u8aToHex } from '@polkadot/util'; +import { UsageMode } from '@webb-tools/test-utils'; +import { defaultEventsWatcherValue } from '../../lib/utils.js'; + +describe('Substrate SignatureBridge Governor Update', function () { + const tmpDirPath = temp.mkdirSync(); + // Tangle nodes + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; + + let webbRelayer: WebbRelayer; + + before(async () => { + const usageMode: UsageMode = isCi + ? { mode: 'docker', forcePullImage: false } + : { + mode: 'host', + nodePath: path.resolve( + '../../tangle/target/release/tangle-standalone' + ), + }; + const enabledPallets: Pallet[] = [ + { + pallet: 'SignatureBridge', + eventsWatcher: defaultEventsWatcherValue, + }, + { + pallet: 'DKGProposalHandler', + eventsWatcher: defaultEventsWatcherValue, + }, + { + pallet: 'DKG', + eventsWatcher: defaultEventsWatcherValue, + }, + ]; + + // Step 1. We start tangle nodes. + aliceNode = await LocalTangle.start({ + name: 'substrate-alice', + authority: 'alice', + usageMode, + ports: 'auto', + enableLogging: false, + }); + + charlieNode = await LocalTangle.start({ + name: 'substrate-charlie', + authority: 'charlie', + usageMode, + ports: 'auto', + enableLogging: false, + }); + // Wait until we are ready and connected + const api = await aliceNode.api(); + await api.isReady; + console.log( + 'tangle node ready waiting for dkg public key to be set onchain' + ); + const chainId = await aliceNode.getChainId(); + + // Step 2. We need to wait until the public key is on chain. + await aliceNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySubmitted', + }); + + await aliceNode.writeConfig(`${tmpDirPath}/${aliceNode.name}.json`, { + suri: '//Charlie', + chainId: chainId, + proposalSigningBackend: { type: 'DKGNode', chainId }, + enabledPallets, + }); + + // Step 4. We force set maintainer on signature bridge. + const dkgPublicKey = await aliceNode.fetchDkgPublicKey(); + expect(dkgPublicKey).to.not.equal('0x'); + const refreshNonce = await api.query.dkg.refreshNonce(); + + // force set maintainer + const setMaintainerCall = api.tx.signatureBridge.forceSetMaintainer( + refreshNonce, + dkgPublicKey + ); + await aliceNode.sudoExecuteTransaction(setMaintainerCall); + + // now start the relayer + const relayerPort = await getPort({ port: portNumbers(8000, 8888) }); + webbRelayer = new WebbRelayer({ + commonConfig: { + port: relayerPort, + }, + tmp: true, + configDir: tmpDirPath, + showLogs: true, + }); + await webbRelayer.waitUntilReady(); + }); + + it('ownership should be transferred when the DKG rotates', async () => { + // Now we just need to force the DKG to rotate/refresh. + const api = await aliceNode.api(); + const chainId = await aliceNode.getChainId(); + + await aliceNode.waitForEvent({ + section: 'dkg', + method: 'PublicKeySignatureChanged', + }); + + await webbRelayer.waitForEvent({ + kind: 'signature_bridge', + event: { + call: 'transfer_ownership_with_signature_pub_key', + chain_id: chainId.toString(), + }, + }); + // Now we just need for the relayer to pick up the new DKG events. + await webbRelayer.waitForEvent({ + kind: 'tx_queue', + event: { + ty: 'SUBSTRATE', + chain_id: chainId.toString(), + finalized: true, + }, + }); + + // Now we need to check that the ownership was transfered. + const dkgPublicKey = await aliceNode.fetchDkgPublicKey(); + expect(dkgPublicKey).to.not.equal('0x'); + + const maintainer = await api.query.signatureBridge.maintainer(); + const aliceMainatinerPubKey = u8aToHex(maintainer); + expect(dkgPublicKey).to.eq(aliceMainatinerPubKey); + }); + + after(async () => { + await aliceNode?.stop(); + await charlieNode?.stop(); + await webbRelayer?.stop(); + }); +}); diff --git a/tests/test/substrate/mixerTransactionRelayer.test.ts b/tests/test/substrate/mixerTransactionRelayer.test.ts deleted file mode 100644 index ae8b1defd..000000000 --- a/tests/test/substrate/mixerTransactionRelayer.test.ts +++ /dev/null @@ -1,538 +0,0 @@ -// This our basic Substrate Transaction Relayer Tests. -// These are for testing the basic relayer functionality. which is just relay transactions for us. - -import '@webb-tools/protocol-substrate-types'; -import { expect } from 'chai'; -import getPort, { portNumbers } from 'get-port'; -import temp from 'temp'; -import path from 'path'; -import fs from 'fs'; -import isCi from 'is-ci'; -import child from 'child_process'; -import { WebbRelayer } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; -import { ApiPromise, Keyring } from '@polkadot/api'; -import { u8aToHex, hexToU8a } from '@polkadot/util'; -import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { decodeAddress } from '@polkadot/util-crypto'; -import { - Note, - NoteGenInput, - ProvingManagerSetupInput, - ArkworksProvingManager, -} from '@webb-tools/sdk-core'; -import { UsageMode } from '@webb-tools/test-utils'; - -describe('Substrate Mixer Transaction Relayer', function () { - const tmpDirPath = temp.mkdirSync(); - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; - - let webbRelayer: WebbRelayer; - - before(async () => { - const usageMode: UsageMode = isCi - ? { mode: 'docker', forcePullImage: false } - : { - mode: 'host', - nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' - ), - }; - - aliceNode = await LocalProtocolSubstrate.start({ - name: 'substrate-alice', - authority: 'alice', - usageMode, - ports: 'auto', - }); - - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', - usageMode, - ports: 'auto', - }); - - // Wait until we are ready and connected - const api = await aliceNode.api(); - await api.isReady; - - const chainId = await aliceNode.getChainId(); - - await aliceNode.writeConfig(`${tmpDirPath}/${aliceNode.name}.json`, { - suri: '//Charlie', - chainId: chainId, - }); - - // now start the relayer - const relayerPort = await getPort({ port: portNumbers(8000, 8888) }); - webbRelayer = new WebbRelayer({ - commonConfig: { - port: relayerPort, - }, - tmp: true, - configDir: tmpDirPath, - showLogs: false, - }); - await webbRelayer.waitUntilReady(); - }); - - it('Simple Mixer Transaction', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Dave'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - // get the initial balance - const { nonce, data: balance } = await api.query.system.account( - withdrawalProof.recipient - ); - // get chainId - const chainId = await aliceNode.getChainId(); - const initialBalance = balance.free.toBigInt(); - // now we need to submit the withdrawal transaction. - const txHash = await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: withdrawalProof.recipient, - relayer: withdrawalProof.relayer, - }); - // now we wait for relayer to execute transaction. - await webbRelayer.waitForEvent({ - kind: 'private_tx', - event: { - ty: 'SUBSTRATE', - chain_id: chainId.toString(), - finalized: true, - }, - }); - expect(txHash).to.be.not.null; - - // get the balance after withdrawal is done and see if it increases - const { nonce: nonceAfter, data: balanceAfter } = - await api.query.system.account(withdrawalProof.recipient); - const balanceAfterWithdraw = balanceAfter.free.toBigInt(); - expect(balanceAfterWithdraw > initialBalance); - }); - - it('Should fail to withdraw if recipient address is invalid', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Dave'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const invalidAddress = '5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy'; - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: invalidAddress, - relayer: withdrawalProof.relayer, - }); - } catch (e) { - // Expect an error to be thrown - expect(e).to.not.be.null; - // Runtime Error that indicates invalid withdrawal proof - expect(e).to.contain( - 'Runtime error: RuntimeError(Module { index: 40, error: 1 }' - ); - } - }); - - it('Should fail to withdraw if proof is invalid', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Eve'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const proofBytes = hexToU8a(withdrawalProof.proofBytes); - // flip a bit in the proof, so it is invalid - const flipCount = proofBytes.length / 8; - for (let i = 0; i < flipCount; i++) { - proofBytes[i] |= 0x42; - } - const invalidProofBytes = u8aToHex(proofBytes); - expect(withdrawalProof.proofBytes).to.not.eq(invalidProofBytes); - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(invalidProofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: withdrawalProof.recipient, - relayer: withdrawalProof.relayer, - }); - } catch (e: any) { - // Expect an error to be thrown - expect(e).to.not.be.null; - const errorMessage: string = e.toString(); - - // Runtime Error that indicates VerifyError in pallet-verifier, or InvalidWithdrawProof in pallet-mixer - const correctErrorMessage = - errorMessage.includes('Module { index: 35, error: 1 }') || - errorMessage.includes('Module { index: 40, error: 1 }'); - expect(correctErrorMessage); - } - }); - - it('Should fail to withdraw if fee is not expected', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Ferdie'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const invalidFee = 100; - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: invalidFee, - recipient: withdrawalProof.recipient, - relayer: withdrawalProof.relayer, - }); - } catch (e) { - // Expect an error to be thrown - expect(e).to.not.be.null; - // Runtime Error that indicates invalid withdrawal proof - expect(e).to.match(/InvalidWithdrawProof/gim); - } - }); - - it('Should fail to withdraw with invalid root', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Eve'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const rootBytes = hexToU8a(withdrawalProof.root); - // flip a bit in the proof, so it is invalid - const flipCount = rootBytes.length / 8; - for (let i = 0; i < flipCount; i++) { - rootBytes[i] |= 0x42; - } - const invalidRootBytes = u8aToHex(rootBytes); - expect(withdrawalProof.proofBytes).to.not.eq(invalidRootBytes); - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(invalidRootBytes)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: withdrawalProof.recipient, - relayer: withdrawalProof.relayer, - }); - } catch (e) { - // Expect an error to be thrown - console.log(e); - expect(e).to.not.be.null; - expect(e).to.match(/UnknownRoot/gim); - } - }); - - it('Should fail to withdraw if recipient address is invalid', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Dave'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const invalidAddress = '5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy'; - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(withdrawalProof.nullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: withdrawalProof.recipient, - relayer: invalidAddress, - }); - } catch (e) { - // Expect an error to be thrown - expect(e).to.not.be.null; - // Runtime Error that indicates invalid withdrawal proof - expect(e).to.match(/InvalidWithdrawProof/gim); - } - }); - - it('Should fail to withdraw with invalid nullifier hash', async () => { - const api = await aliceNode.api(); - const account = createAccount('//Eve'); - const note = await makeDeposit(api, aliceNode, account); - const withdrawalProof = await initWithdrawal( - api, - webbRelayer, - account, - note - ); - - const nullifierHash = hexToU8a(withdrawalProof.root); - // flip a bit in the proof, so it is invalid - const flipCount = nullifierHash.length / 8; - for (let i = 0; i < flipCount; i++) { - nullifierHash[i] = 0x42; - } - const invalidNullifierHash = u8aToHex(nullifierHash); - expect(withdrawalProof.nullifierHash).to.not.eq(invalidNullifierHash); - // get chainId - const chainId = await aliceNode.getChainId(); - // now we need to submit the withdrawal transaction. - try { - // try to withdraw with invalid address - await webbRelayer.substrateMixerWithdraw({ - chainId: chainId, - id: withdrawalProof.id, - proof: Array.from(hexToU8a(withdrawalProof.proofBytes)), - root: Array.from(hexToU8a(withdrawalProof.root)), - nullifierHash: Array.from(hexToU8a(invalidNullifierHash)), - refund: withdrawalProof.refund, - fee: withdrawalProof.fee, - recipient: withdrawalProof.recipient, - relayer: withdrawalProof.relayer, - }); - } catch (e) { - // Expect an error to be thrown - expect(e).to.not.be.null; - // Runtime Error that indicates invalid withdrawal proof - expect(e).to.match(/VerifyError/gim); - } - }); - - after(async () => { - await aliceNode?.stop(); - await bobNode?.stop(); - await webbRelayer?.stop(); - }); -}); - -// Helper methods, we can move them somewhere if we end up using them again. - -async function createMixerDepositTx(api: ApiPromise): Promise<{ - tx: SubmittableExtrinsic<'promise'>; - note: Note; -}> { - const noteInput: NoteGenInput = { - protocol: 'mixer', - version: 'v2', - sourceChain: '5', - targetChain: '5', - sourceIdentifyingData: '3', - targetIdentifyingData: '3', - tokenSymbol: 'WEBB', - amount: '1', - denomination: '18', - backend: 'Arkworks', - hashFunction: 'Poseidon', - curve: 'Bn254', - width: '3', - exponentiation: '5', - }; - const note = await Note.generateNote(noteInput); - const treeId = 0; - const leaf = note.getLeaf(); - const tx = api.tx.mixerBn254.deposit(treeId, leaf); - return { tx, note }; -} - -type WithdrawalOpts = { - relayer: string; - recipient: string; - fee?: number; - refund?: number; -}; - -type WithdrawalProof = { - id: number; - proofBytes: string; - root: string; - nullifierHash: string; - recipient: string; - relayer: string; - fee: number; - refund: number; -}; - -async function createMixerWithdrawProof( - api: ApiPromise, - note: any, - opts: WithdrawalOpts -): Promise { - try { - const recipientAddressHex = u8aToHex(decodeAddress(opts.recipient)).replace( - '0x', - '' - ); - const relayerAddressHex = u8aToHex(decodeAddress(opts.relayer)).replace( - '0x', - '' - ); - const treeId = 0; - const leafCount: number = - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await api.derive.merkleTreeBn254.getLeafCountForTree(treeId); - const treeLeaves: Uint8Array[] = - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - await api.derive.merkleTreeBn254.getLeavesForTree( - treeId, - 0, - leafCount - 1 - ); - const provingManager = new ArkworksProvingManager(null); - const leafHex = u8aToHex(note.getLeaf()); - const leafIndex = treeLeaves.findIndex((l) => u8aToHex(l) === leafHex); - expect(leafIndex).to.be.greaterThan(-1); - const gitRoot = child - .execSync('git rev-parse --show-toplevel') - .toString() - .trim(); - const provingKeyPath = path.join( - gitRoot, - 'tests', - 'substrate-fixtures', - 'mixer', - 'bn254', - 'x5', - 'proving_key_uncompressed.bin' - ); - const provingKey = fs.readFileSync(provingKeyPath); - - const proofInput: ProvingManagerSetupInput<'mixer'> = { - note: note.serialize(), - relayer: relayerAddressHex, - recipient: recipientAddressHex, - leaves: treeLeaves, - leafIndex, - fee: opts.fee === undefined ? 0 : opts.fee, - refund: opts.refund === undefined ? 0 : opts.refund, - provingKey, - }; - const zkProof = await provingManager.prove('mixer', proofInput); - return { - id: treeId, - proofBytes: `0x${zkProof.proof}`, - root: `0x${zkProof.root}`, - nullifierHash: `0x${zkProof.nullifierHash}`, - recipient: opts.recipient, - relayer: opts.relayer, - fee: opts.fee === undefined ? 0 : opts.fee, - refund: opts.refund === undefined ? 0 : opts.refund, - }; - } catch (error: any) { - console.error(error.error_message); - console.error(error.code); - throw error; - } -} - -function createAccount(accountId: string) { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} - -async function makeDeposit( - api: any, - aliceNode: any, - account: any -): Promise { - const { tx, note } = await createMixerDepositTx(api); - - // send the deposit transaction. - const txSigned = await tx.signAsync(account); - await aliceNode.executeTransaction(txSigned); - - return note; -} - -async function initWithdrawal( - api: any, - webbRelayer: any, - account: any, - note: any -): Promise { - // next we need to prepare the withdrawal transaction. - // create correct proof with right address - const withdrawalProof = await createMixerWithdrawProof(api, note, { - recipient: account.address, - relayer: account.address, - }); - // ping the relayer! - await webbRelayer.ping(); - - return withdrawalProof; -} diff --git a/tests/test/substrate/signatureBridgeVanchor.test.ts b/tests/test/substrate/signatureBridgeVanchor.test.ts index 26bd25618..eb09f757b 100644 --- a/tests/test/substrate/signatureBridgeVanchor.test.ts +++ b/tests/test/substrate/signatureBridgeVanchor.test.ts @@ -17,7 +17,6 @@ // This is Substrate VAnchor Transaction Relayer Tests. // In this test relayer on vanchor deposit will create and relay proposals to signature bridge pallet for execution -import '@webb-tools/protocol-substrate-types'; import getPort, { portNumbers } from 'get-port'; import temp from 'temp'; import path from 'path'; @@ -30,11 +29,10 @@ import { Pallet, RelayerMetricResponse, } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { BigNumber } from 'ethers'; -import { ApiPromise, Keyring } from '@polkadot/api'; +import { ApiPromise } from '@polkadot/api'; import { u8aToHex, hexToU8a } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { naclEncrypt, randomAsU8a } from '@polkadot/util-crypto'; @@ -58,17 +56,18 @@ import { import pkg from 'secp256k1'; import { makeSubstrateTargetSystem } from '../../lib/webbProposals.js'; import { + createAccount, defaultEventsWatcherValue, generateVAnchorNote, } from '../../lib/utils.js'; -import { UsageMode } from '@webb-tools/test-utils'; +import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; import { expect } from 'chai'; const { ecdsaSign } = pkg; describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Backend', function () { const tmpDirPath = temp.mkdirSync(); - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; // Governer key @@ -79,7 +78,6 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac ._signingKey() .publicKey.toString() .slice(4); - const typedSourceChainId = calculateTypedChainId(ChainType.Substrate, 1080); before(async () => { const usageMode: UsageMode = isCi @@ -87,7 +85,7 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac : { mode: 'host', nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -101,7 +99,7 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac }, ]; - aliceNode = await LocalProtocolSubstrate.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, @@ -109,9 +107,9 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac enableLogging: false, }); - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', + charlieNode = await LocalTangle.start({ + name: 'substrate-charlie', + authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, @@ -131,14 +129,16 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac }); // force set maintainer + const refreshNonce = 0; const setMaintainerCall = api.tx.signatureBridge.forceSetMaintainer( + refreshNonce, `0x${uncompressedKey}` ); await aliceNode.sudoExecuteTransaction(setMaintainerCall); - + const typedChainId = calculateTypedChainId(ChainType.Substrate, chainId); //whitelist chain const whitelistChainCall = - api.tx.signatureBridge.whitelistChain(typedSourceChainId); + api.tx.signatureBridge.whitelistChain(typedChainId); await aliceNode.sudoExecuteTransaction(whitelistChainCall); // now start the relayer @@ -178,7 +178,7 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac await aliceNode.executeTransaction(txSigned); // vanchor deposit - await vanchorDeposit(treeId, api, aliceNode); + await vanchorDeposit(treeId, chainId, api, aliceNode); // now we wait for the proposal to be signed by mocked backend and then send data to signature bridge await webbRelayer.waitForEvent({ @@ -202,7 +202,7 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac // check metrics gathered const responseMetricsGathered = await webbRelayer.getMetricsGathered(); expect(responseMetricsGathered.status).equal(200); - let metricsGathered = + const metricsGathered = responseMetricsGathered.json() as Promise; metricsGathered.then((resp) => { console.log(resp.metrics); @@ -212,7 +212,7 @@ describe('Substrate Signature Bridge Relaying On Vanchor Deposit <<>> Mocked Bac after(async () => { await aliceNode?.stop(); - await bobNode?.stop(); + await charlieNode?.stop(); await webbRelayer?.stop(); }); }); @@ -227,7 +227,7 @@ async function setResourceIdProposal( ): Promise> { const functionSignature = hexToU8a('0x00000002', 32); const nonce = 1; - const palletIndex = '0x2C'; + const palletIndex = '0x2A'; const callIndex = '0x02'; const substrateTargetSystem = makeSubstrateTargetSystem(treeId, palletIndex); // set resource ID @@ -267,12 +267,12 @@ async function setResourceIdProposal( async function vanchorDeposit( treeId: number, + chainId: number, api: ApiPromise, - aliceNode: LocalProtocolSubstrate + aliceNode: LocalTangle ) { const account = createAccount('//Dave'); - const chainId = '2199023256632'; - const outputChainId = BigInt(chainId); + const typedChainId = calculateTypedChainId(ChainType.Substrate, chainId); const secret = randomAsU8a(); const gitRoot = child .execSync('git rev-parse --show-toplevel') @@ -294,12 +294,7 @@ async function vanchorDeposit( const pk = hexToU8a(pk_hex); // Creating two empty vanchor notes - const note1 = await generateVAnchorNote( - 0, - Number(outputChainId.toString()), - Number(outputChainId.toString()), - 0 - ); + const note1 = await generateVAnchorNote(0, typedChainId, typedChainId, 0); const note2 = await note1.getDefaultUtxoNote(); const notes = [note1, note2]; const publicAmount = currencyToUnitI128(10); @@ -308,13 +303,13 @@ async function vanchorDeposit( curve: 'Bn254', backend: 'Arkworks', amount: publicAmount.toString(), - chainId, + chainId: typedChainId.toString(), }); const output2 = await Utxo.generateUtxo({ curve: 'Bn254', backend: 'Arkworks', amount: '0', - chainId, + chainId: typedChainId.toString(), }); // Configure a new proving manager with direct call @@ -325,9 +320,9 @@ async function vanchorDeposit( const extAmount = currencyToUnitI128(10); const fee = 0; const refund = 0; - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. // Empty leaves - leavesMap[outputChainId.toString()] = []; + leavesMap[typedChainId.toString()] = []; const tree = await api.query.merkleTreeBn254.trees(treeId); const root = tree.unwrap().root.toHex(); const rootsSet = [hexToU8a(root), hexToU8a(root)]; @@ -336,11 +331,11 @@ async function vanchorDeposit( const { encrypted: comEnc2 } = naclEncrypt(output2.commitment, secret); const leafId: LeafIdentifier = { index: 0, - typedChainId: Number(outputChainId.toString()), + typedChainId, }; const setup: ProvingManagerSetupInput<'vanchor'> = { - chainId: outputChainId.toString(), + chainId: typedChainId.toString(), leafIds: [leafId, leafId], inputUtxos: notes.map((n) => new Utxo(n.note.getUtxo())), leavesMap: leavesMap, @@ -364,7 +359,7 @@ async function vanchorDeposit( fee, refund: String(refund), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toNumber(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; @@ -387,15 +382,3 @@ async function vanchorDeposit( const txSigned = await transactCall.signAsync(account); await aliceNode.executeTransaction(txSigned); } - -function currencyToUnitI128(currencyAmount: number) { - const bn = BigNumber.from(currencyAmount); - return bn.mul(1_000_000_000_000); -} - -function createAccount(accountId: string) { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} diff --git a/tests/test/substrate/substrateToEvmTransaction.test.ts b/tests/test/substrate/substrateToEvmTransaction.test.ts index ae2257bd3..9892c6495 100644 --- a/tests/test/substrate/substrateToEvmTransaction.test.ts +++ b/tests/test/substrate/substrateToEvmTransaction.test.ts @@ -18,7 +18,6 @@ // In this test we will deposit on substrate vanchor system // and withdraw through evm vanchor system. -import '@webb-tools/protocol-substrate-types'; import getPort, { portNumbers } from 'get-port'; import temp from 'temp'; import path from 'path'; @@ -31,10 +30,9 @@ import { Pallet, EnabledContracts, } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; +import { LocalTangle } from '../../lib/localTangle.js'; import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { BigNumber } from 'ethers'; -import { ApiPromise, Keyring } from '@polkadot/api'; +import { ApiPromise } from '@polkadot/api'; import { u8aToHex, hexToU8a, assert } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { naclEncrypt, randomAsU8a } from '@polkadot/util-crypto'; @@ -53,6 +51,7 @@ import { } from '@webb-tools/sdk-core'; import { + createAccount, defaultEventsWatcherValue, generateVAnchorNote, } from '../../lib/utils.js'; @@ -65,14 +64,15 @@ import { createSubstrateResourceId } from '../../lib/webbProposals.js'; import { LocalChain } from '../../lib/localTestnet.js'; import { Tokens, VBridge } from '@webb-tools/protocol-solidity'; import { expect } from 'chai'; -import { UsageMode } from '@webb-tools/test-utils'; +import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; +import { MintableToken } from '@webb-tools/tokens'; const { ecdsaSign } = pkg; describe('Cross chain transaction <<>> Mocked Backend', function () { const tmpDirPath = temp.mkdirSync(); let localChain1: LocalChain; - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; let wallet1: ethers.Wallet; let signatureVBridge: VBridge.VBridge; @@ -94,7 +94,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { : { mode: 'host', nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -108,7 +108,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { }, ]; - aliceNode = await LocalProtocolSubstrate.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, @@ -116,9 +116,9 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { enableLogging: false, }); - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', + charlieNode = await LocalTangle.start({ + name: 'substrate-charlie', + authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, @@ -157,10 +157,17 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { wallet1 = new ethers.Wallet(PK1, localChain1.provider()); // Deploy the token. - const localToken1 = await localChain1.deployToken('Webb Token', 'WEBB'); + const localToken = await localChain1.deployToken('Webb Token', 'WEBB'); + + const unwrappedToken = await MintableToken.createToken( + 'Webb Token', + 'WEBB', + wallet1 + ); signatureVBridge = await localChain1.deployVBridge( - localToken1, + localToken, + unwrappedToken, wallet1, governorWallet ); @@ -174,7 +181,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { const substrateResourceId = createSubstrateResourceId( substrateChainId, 6, - '0x2C' + '0x2A' ); // save the substrate chain configs await aliceNode.writeConfig(`${tmpDirPath}/${aliceNode.name}.json`, { @@ -199,7 +206,9 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { // 2. We need to whitelist chain Id // force set maintainer + const refreshNonce = 0; const setMaintainerCall = api.tx.signatureBridge.forceSetMaintainer( + refreshNonce, `0x${uncompressedKey}` ); await aliceNode.sudoExecuteTransaction(setMaintainerCall); @@ -257,6 +266,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { substrateChainId ); const typedTargetChainId = localChain1.chainId; + console.log('Set Resource'); // now we set resource through proposal execution const setResourceIdProposalCall = await setResourceIdProposal( api, @@ -266,7 +276,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { ); const txSigned = await setResourceIdProposalCall.signAsync(account); await aliceNode.executeTransaction(txSigned); - + console.log('Resource set'); // dummy Deposit Note. Input note is directed toward source chain const depositNote = await generateVAnchorNote( 0, @@ -311,14 +321,9 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { .map((el) => hexToU8a(el.toHexString())); const publicAmount = (1e13).toString(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore // get leaves for substrate chain - const substrateLeaves = await api.derive.merkleTreeBn254.getLeavesForTree( - treeId, - 0, - 1 - ); + const substrateLeaves: Uint8Array[] = + await api.derive.merkleTreeBn254.getLeavesForTree(treeId, 0, 1); assert(substrateLeaves.length === 2, 'Invalid substrate leaves length'); const index = substrateLeaves.findIndex( (leaf) => data.outputUtxo.commitment.toString() === leaf.toString() @@ -348,7 +353,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { toFixedHex(withdrawUtxo.commitment), 'Invalid commitment' ); - const res = await vanchor1.transact([], [], 0, 0, '0', '0', tokenAddress, { + await vanchor1.transact([], [], 0, 0, '0', '0', tokenAddress, { [localChain1.chainId]: leaves, [typedSourceChainId]: substrateLeaves, }); @@ -374,7 +379,7 @@ describe('Cross chain transaction <<>> Mocked Backend', function () { after(async () => { await localChain1?.stop(); await aliceNode?.stop(); - await bobNode?.stop(); + await charlieNode?.stop(); await webbRelayer?.stop(); }); }); @@ -389,7 +394,7 @@ async function setResourceIdProposal( ): Promise> { const functionSignature = hexToU8a('0x00000002', 32); const nonce = 1; - const palletIndex = '0x2C'; + const palletIndex = '0x2A'; const callIndex = '0x02'; // set resource ID on signature bridge. const resourceId = createSubstrateResourceId(chainId, treeId, palletIndex); @@ -429,7 +434,7 @@ async function vanchorDeposit( publicAmountUint: number, treeId: number, api: ApiPromise, - aliceNode: LocalProtocolSubstrate + aliceNode: LocalTangle ): Promise<{ outputUtxo: Utxo; keyPair: Keypair }> { const account = createAccount('//Dave'); const typedSourceChainId = depositNote.note.sourceChainId; @@ -508,7 +513,7 @@ async function vanchorDeposit( const rootsSet = [hexToU8a(root), hexToU8a(neighborRoots[0])]; const decodedAddress = decodeAddress(address); - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. const { encrypted: comEnc1 } = naclEncrypt(output1.commitment, secret); const { encrypted: comEnc2 } = naclEncrypt(output2.commitment, secret); const LeafId = { @@ -540,7 +545,7 @@ async function vanchorDeposit( fee, refund: String(refund), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toNumber(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; @@ -572,15 +577,3 @@ async function vanchorDeposit( await aliceNode.executeTransaction(txSigned); return { outputUtxo: output1, keyPair: randomKeypair }; } - -function currencyToUnitI128(currencyAmount: number) { - const bn = BigNumber.from(currencyAmount); - return bn.mul(1_000_000_000_000); -} - -function createAccount(accountId: string): any { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} diff --git a/tests/test/substrate/vanchorPrivateTransaction.test.ts b/tests/test/substrate/vanchorPrivateTransaction.test.ts index dcf03b891..62c5b9d48 100644 --- a/tests/test/substrate/vanchorPrivateTransaction.test.ts +++ b/tests/test/substrate/vanchorPrivateTransaction.test.ts @@ -17,7 +17,6 @@ // This our basic Substrate VAnchor Transaction Relayer Tests. // These are for testing the basic relayer functionality. which is just to relay transactions for us. -import '@webb-tools/protocol-substrate-types'; import { assert, expect } from 'chai'; import getPort, { portNumbers } from 'get-port'; import temp from 'temp'; @@ -30,12 +29,12 @@ import { Pallet, SubstrateVAnchorExtData, SubstrateVAnchorProofData, + SubstrateFeeInfo, } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; import { BigNumber, ethers } from 'ethers'; -import { ApiPromise, Keyring } from '@polkadot/api'; -import { u8aToHex, hexToU8a } from '@polkadot/util'; +import { ApiPromise } from '@polkadot/api'; +import { u8aToHex, hexToU8a, BN } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { naclEncrypt, randomAsU8a } from '@polkadot/util-crypto'; @@ -46,17 +45,19 @@ import { calculateTypedChainId, ChainType, } from '@webb-tools/sdk-core'; -import { UsageMode } from '@webb-tools/test-utils'; +import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; import { + createAccount, defaultEventsWatcherValue, generateVAnchorNote, } from '../../lib/utils.js'; +import { LocalTangle } from '../../lib/localTangle.js'; +import { verify_js_proof } from '@webb-tools/wasm-utils/njs/wasm-utils-njs.js'; describe('Substrate VAnchor Private Transaction Relayer Tests', function () { const tmpDirPath = temp.mkdirSync(); - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; - + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; const PK1 = u8aToHex(ethers.utils.randomBytes(32)); @@ -66,7 +67,7 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { : { mode: 'host', nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -76,7 +77,7 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { }, ]; - aliceNode = await LocalProtocolSubstrate.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, @@ -84,13 +85,14 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { enableLogging: false, }); - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', + charlieNode = await LocalTangle.start({ + name: 'dkg-charlie', + authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, }); + // Wait until we are ready and connected const api = await aliceNode.api(); await api.isReady; @@ -112,7 +114,7 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { }, tmp: true, configDir: tmpDirPath, - showLogs: false, + showLogs: true, }); await webbRelayer.waitUntilReady(); }); @@ -137,12 +139,11 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { const data = await vanchorDeposit( typedSourceChainId.toString(), // source chain Id typedSourceChainId.toString(), // target chain Id - 10, // public amount + 1000000000, // public amount treeId, api, aliceNode ); - // now we wait for all deposit to be saved in LeafStorageCache. await webbRelayer.waitForEvent({ kind: 'leaves_store', @@ -156,33 +157,64 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { // Bob's balance after withdrawal const bobBalanceBefore = await api.query.system.account(account.address); + const dummyVanchorData = await vanchorWithdraw( + typedSourceChainId.toString(), + typedSourceChainId.toString(), + account.address, + data.depositUtxos, + treeId, + BigInt(0), + // TODO: need to convert this once there is exchange rate between native + // token and wrapped token + BigInt(0), + api + ); + const token = new DataView(dummyVanchorData.extData.token.buffer, 0); + + const info = await api.tx.vAnchorBn254 + .transact(treeId, dummyVanchorData.proofData, dummyVanchorData.extData) + .paymentInfo(account); + const feeInfoResponse2 = await webbRelayer.getSubstrateFeeInfo( + substrateChainId, + info.partialFee.toBn() + ); + expect(feeInfoResponse2.status).equal(200); + const feeInfo2 = + await (feeInfoResponse2.json() as Promise); + const estimatedFee = BigInt(feeInfo2.estimatedFee); + + const refund = BigInt(0); + const feeTotal = estimatedFee + refund; const vanchorData = await vanchorWithdraw( typedSourceChainId.toString(), typedSourceChainId.toString(), account.address, data.depositUtxos, treeId, + feeTotal, + // TODO: need to convert this once there is exchange rate between native + // token and wrapped token + refund, api ); - // Now we construct payload for substrate private transaction. - // Convert [u8;4] to u32 asset Id - const token = new DataView(vanchorData.extData.token.buffer, 0); - let substrateExtData: SubstrateVAnchorExtData = { + const substrateExtData: SubstrateVAnchorExtData = { recipient: vanchorData.extData.recipient, relayer: vanchorData.extData.relayer, - extAmount: Number(vanchorData.extData.extAmount), - fee: Number(vanchorData.extData.fee), + extAmount: BigNumber.from(vanchorData.extData.extAmount) + .toHexString() + .replace('0x', ''), + fee: BigNumber.from(feeTotal.toString()).toHexString(), encryptedOutput1: Array.from( hexToU8a(vanchorData.extData.encryptedOutput1) ), encryptedOutput2: Array.from( hexToU8a(vanchorData.extData.encryptedOutput2) ), - refund: Number(vanchorData.extData.refund), + refund: BigNumber.from(refund.toString()).toHexString(), token: token.getUint32(0, true), }; - let substrateProofData: SubstrateVAnchorProofData = { + const substrateProofData: SubstrateVAnchorProofData = { proof: Array.from(hexToU8a(vanchorData.proofData.proof)), extDataHash: Array.from(vanchorData.proofData.extDataHash), extensionRoots: vanchorData.proofData.roots.map((root) => @@ -207,6 +239,7 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { ); // now we wait for relayer to execute private transaction. + await webbRelayer.waitForEvent({ kind: 'private_tx', event: { @@ -218,39 +251,32 @@ describe('Substrate VAnchor Private Transaction Relayer Tests', function () { // Bob's balance after withdrawal. const BobBalanceAfter = await api.query.system.account(account.address); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore console.log('balance after : ', BobBalanceAfter.data.free); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore assert(BobBalanceAfter.data.free > bobBalanceBefore.data.free); }); after(async () => { await aliceNode?.stop(); - await bobNode?.stop(); + // await bobNode?.stop(); + await charlieNode?.stop(); await webbRelayer?.stop(); }); }); // Helper methods, we can move them somewhere if we end up using them again. -function currencyToUnitI128(currencyAmount: number) { - const bn = BigNumber.from(currencyAmount); - return bn.mul(1_000_000_000_000); -} - -function createAccount(accountId: string): any { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} - async function vanchorWithdraw( typedTargetChainId: string, typedSourceChainId: string, recipient: string, depositUtxos: [Utxo, Utxo], treeId: number, + fee: bigint, + refund: bigint, api: ApiPromise ): Promise<{ extData: any; proofData: any }> { const secret = randomAsU8a(); @@ -272,9 +298,23 @@ async function vanchorWithdraw( ); const pk_hex = fs.readFileSync(pkPath).toString('hex'); const pk = hexToU8a(pk_hex); + + const vkPath = path.join( + // tests path + gitRoot, + 'tests', + 'substrate-fixtures', + 'vanchor', + 'bn254', + 'x5', + '2-2-2', + 'verifying_key_uncompressed.bin' + ); + const vk_hex = fs.readFileSync(vkPath).toString('hex'); + const vk = hexToU8a(vk_hex); + const leavesMap = {}; // get source chain (evm) leaves. - //@ts-ignore const substrateLeaves = await api.derive.merkleTreeBn254.getLeavesForTree( treeId, 0, @@ -306,17 +346,21 @@ async function vanchorWithdraw( // Configure a new proving manager with direct call const provingManager = new ArkworksProvingManager(null); - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. const address = recipient; - const fee = 0; - const refund = 0; const withdrawAmount = depositUtxos.reduce((acc, utxo) => { - return Number(utxo.amount) + acc; - }, 0); - const extAmount = -withdrawAmount; + return BigInt(utxo.amount) + acc; + }, BigInt(0)); const publicAmount = -withdrawAmount; + const extAmount = -(withdrawAmount - fee); + + console.log({ + fee: fee.toString(), + extAmount: extAmount.toString(), + publicAmount: publicAmount.toString(), + }); const tree = await api.query.merkleTreeBn254.trees(treeId); const root = tree.unwrap().root.toHex(); @@ -343,24 +387,34 @@ async function vanchorWithdraw( output: [output1, output2], encryptedCommitments: [comEnc1, comEnc2], provingKey: pk, - publicAmount: String(publicAmount), + publicAmount: publicAmount.toString(), roots: rootsSet, relayer: decodedAddress, recipient: decodedAddress, extAmount: extAmount.toString(), fee: fee.toString(), - refund: String(refund), + refund: refund.toString(), token: assetId, }; const data = await provingManager.prove('vanchor', setup); + + const isValidProof = verify_js_proof( + data.proof, + data.publicInputs, + u8aToHex(vk).replace('0x', ''), + 'Bn254' + ); + console.log('Is proof valid : ', isValidProof); + expect(isValidProof).to.be.true; + const extData = { relayer: address, recipient: address, - fee, - refund: String(refund), + fee: fee.toString(), + refund: refund.toString(), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toString(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; @@ -382,7 +436,7 @@ async function vanchorDeposit( publicAmountUint: number, treeId: number, api: ApiPromise, - aliceNode: LocalProtocolSubstrate + aliceNode: LocalTangle ): Promise<{ depositUtxos: [Utxo, Utxo] }> { const account = createAccount('//Dave'); const secret = randomAsU8a(); @@ -404,21 +458,6 @@ async function vanchorDeposit( ); const pk_hex = fs.readFileSync(pkPath).toString('hex'); const pk = hexToU8a(pk_hex); - - const vkPath = path.join( - // tests path - gitRoot, - 'tests', - 'substrate-fixtures', - 'vanchor', - 'bn254', - 'x5', - '2-2-2', - 'verifying_key_uncompressed.bin' - ); - const vk_hex = fs.readFileSync(vkPath).toString('hex'); - const vk = hexToU8a(vk_hex); - // dummy Deposit Note. Input note is directed toward source chain const depositNote = await generateVAnchorNote( 0, @@ -450,7 +489,7 @@ async function vanchorDeposit( const leavesMap = {}; const address = account.address; - const extAmount = currencyToUnitI128(10); + const extAmount = currencyToUnitI128(publicAmountUint); const fee = 0; const refund = 0; // Initially leaves will be empty @@ -465,7 +504,7 @@ async function vanchorDeposit( const rootsSet = [hexToU8a(root), hexToU8a(neighborRoots[0])]; const decodedAddress = decodeAddress(address); - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. const { encrypted: comEnc1 } = naclEncrypt(output1.commitment, secret); const { encrypted: comEnc2 } = naclEncrypt(output2.commitment, secret); const LeafId = { @@ -497,7 +536,7 @@ async function vanchorDeposit( fee, refund: String(refund), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toString(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; diff --git a/tests/test/substrate/vanchorTransactionRelayer.test.ts b/tests/test/substrate/vanchorTransactionRelayer.test.ts index fad34ee61..10a292beb 100644 --- a/tests/test/substrate/vanchorTransactionRelayer.test.ts +++ b/tests/test/substrate/vanchorTransactionRelayer.test.ts @@ -17,7 +17,6 @@ // This our basic Substrate VAnchor Transaction Relayer Tests. // These are for testing the basic relayer functionality. which is just to relay transactions for us. -import '@webb-tools/protocol-substrate-types'; import { expect } from 'chai'; import getPort, { portNumbers } from 'get-port'; import temp from 'temp'; @@ -30,10 +29,8 @@ import { Pallet, LeavesCacheResponse, } from '../../lib/webbRelayer.js'; -import { LocalProtocolSubstrate } from '../../lib/localProtocolSubstrate.js'; +import { LocalTangle } from '../../lib/localTangle.js'; -import { BigNumber, ethers } from 'ethers'; -import { Keyring } from '@polkadot/api'; import { u8aToHex, hexToU8a } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { naclEncrypt, randomAsU8a } from '@polkadot/util-crypto'; @@ -44,17 +41,21 @@ import { Utxo, VAnchorProof, LeafIdentifier, + calculateTypedChainId, + ChainType, } from '@webb-tools/sdk-core'; -import { UsageMode } from '@webb-tools/test-utils'; +import { currencyToUnitI128, UsageMode } from '@webb-tools/test-utils'; import { + createAccount, defaultEventsWatcherValue, generateVAnchorNote, } from '../../lib/utils.js'; +import { ethers } from 'ethers'; describe('Substrate VAnchor Transaction Relayer Tests', function () { const tmpDirPath = temp.mkdirSync(); - let aliceNode: LocalProtocolSubstrate; - let bobNode: LocalProtocolSubstrate; + let aliceNode: LocalTangle; + let charlieNode: LocalTangle; let webbRelayer: WebbRelayer; const PK1 = u8aToHex(ethers.utils.randomBytes(32)); @@ -65,7 +66,7 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { : { mode: 'host', nodePath: path.resolve( - '../../protocol-substrate/target/release/webb-standalone-node' + '../../tangle/target/release/tangle-standalone' ), }; const enabledPallets: Pallet[] = [ @@ -75,7 +76,7 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { }, ]; - aliceNode = await LocalProtocolSubstrate.start({ + aliceNode = await LocalTangle.start({ name: 'substrate-alice', authority: 'alice', usageMode, @@ -83,9 +84,9 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { enableLogging: false, }); - bobNode = await LocalProtocolSubstrate.start({ - name: 'substrate-bob', - authority: 'bob', + charlieNode = await LocalTangle.start({ + name: 'substrate-charlie', + authority: 'charlie', usageMode, ports: 'auto', enableLogging: false, @@ -127,8 +128,13 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { const nextTreeId = await api.query.merkleTreeBn254.nextTreeId(); const treeId = nextTreeId.toNumber() - 1; - const chainId = '2199023256632'; - const outputChainId = BigInt(chainId); + // ChainId of the substrate chain + const chainId = await aliceNode.getChainId(); + const typedSourceChainId = calculateTypedChainId( + ChainType.Substrate, + chainId + ); + const outputChainId = BigInt(typedSourceChainId); const secret = randomAsU8a(); const gitRoot = child .execSync('git rev-parse --show-toplevel') @@ -157,20 +163,20 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { 0 ); const note2 = await note1.getDefaultUtxoNote(); - const publicAmount = currencyToUnitI128(10); + const publicAmount = currencyToUnitI128(1000); const notes = [note1, note2]; // Output UTXOs configs const output1 = await Utxo.generateUtxo({ curve: 'Bn254', backend: 'Arkworks', amount: publicAmount.toString(), - chainId, + chainId: chainId.toString(), }); const output2 = await Utxo.generateUtxo({ curve: 'Bn254', backend: 'Arkworks', amount: '0', - chainId, + chainId: chainId.toString(), }); // Configure a new proving manager with direct call @@ -178,7 +184,7 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { const leavesMap = {}; const address = account.address; - const extAmount = currencyToUnitI128(10); + const extAmount = currencyToUnitI128(1000); const fee = 0; const refund = 0; // Empty leaves @@ -189,7 +195,7 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { const decodedAddress = decodeAddress(address); const { encrypted: comEnc1 } = naclEncrypt(output1.commitment, secret); const { encrypted: comEnc2 } = naclEncrypt(output2.commitment, secret); - const assetId = new Uint8Array([ 0, 0, 0, 0 ]); // WEBB native token asset Id. + const assetId = new Uint8Array([0, 0, 0, 0]); // WEBB native token asset Id. const dummyLeafId: LeafIdentifier = { index: 0, typedChainId: Number(outputChainId.toString()), @@ -220,7 +226,7 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { fee, refund: String(refund), token: assetId, - extAmount: extAmount, + extAmount: extAmount.toNumber(), encryptedOutput1: u8aToHex(comEnc1), encryptedOutput2: u8aToHex(comEnc2), }; @@ -267,30 +273,13 @@ describe('Substrate VAnchor Transaction Relayer Tests', function () { '44' // pallet Id ); expect(response.status).equal(200); - const leavesStore = response.json() as Promise; - - leavesStore.then((resp) => { - expect(indexBeforeInsetion + 2).to.equal(resp.leaves.length); - }); + const leavesStore = (await response.json()) as LeavesCacheResponse; + expect(indexBeforeInsetion + 2).to.equal(leavesStore.leaves.length); }); after(async () => { await aliceNode?.stop(); - await bobNode?.stop(); + await charlieNode?.stop(); await webbRelayer?.stop(); }); }); - -// Helper methods, we can move them somewhere if we end up using them again. - -function currencyToUnitI128(currencyAmount: number) { - const bn = BigNumber.from(currencyAmount); - return bn.mul(1_000_000_000_000); -} - -function createAccount(accountId: string): any { - const keyring = new Keyring({ type: 'sr25519' }); - const account = keyring.addFromUri(accountId); - - return account; -} diff --git a/tests/yarn.lock b/tests/yarn.lock index 6af9b7608..7d8d4609c 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -9,12 +9,20 @@ dependencies: "@jridgewell/trace-mapping" "^0.3.0" -"@babel/cli@^7.19.3": - version "7.19.3" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.19.3.tgz#55914ed388e658e0b924b3a95da1296267e278e2" - integrity sha512-643/TybmaCAe101m2tSVHi9UKpETXP9c/Ff4mD2tAwkdP6esKIfaauZFc67vGEM6r9fekbEGid+sZhbEnSe3dg== +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/cli@^7.20.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.21.0.tgz#1868eb70e9824b427fc607610cce8e9e7889e7e1" + integrity sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA== dependencies: - "@jridgewell/trace-mapping" "^0.3.8" + "@jridgewell/trace-mapping" "^0.3.17" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" @@ -46,22 +54,29 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== -"@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3": +"@babel/compat-data@^7.19.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151" integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw== -"@babel/compat-data@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" - integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.13", "@babel/core@^7.19.3": +"@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c" integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ== @@ -103,6 +118,27 @@ json5 "^2.1.2" semver "^6.3.0" +"@babel/core@^7.20.12": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/generator@^7.17.3", "@babel/generator@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" @@ -121,7 +157,7 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/generator@^7.19.3", "@babel/generator@^7.7.2": +"@babel/generator@^7.19.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.3.tgz#d7f4d1300485b4547cb6f94b27d10d237b42bf59" integrity sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ== @@ -130,6 +166,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -180,14 +226,15 @@ browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" - integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/compat-data" "^7.19.0" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6": @@ -216,6 +263,20 @@ "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-create-regexp-features-plugin@^7.16.7": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" @@ -224,7 +285,7 @@ "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^5.0.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": +"@babel/helper-create-regexp-features-plugin@^7.18.6": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== @@ -232,6 +293,14 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" +"@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" + integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" @@ -301,6 +370,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" @@ -336,6 +413,13 @@ dependencies: "@babel/types" "^7.18.9" +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== + dependencies: + "@babel/types" "^7.21.0" + "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" @@ -350,6 +434,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" @@ -378,6 +469,20 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" +"@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" @@ -402,6 +507,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== +"@babel/helper-plugin-utils@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" @@ -411,7 +521,7 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": +"@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== @@ -443,6 +553,18 @@ "@babel/traverse" "^7.19.1" "@babel/types" "^7.19.0" +"@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + "@babel/helper-simple-access@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" @@ -457,6 +579,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" @@ -464,12 +593,12 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.0" "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" @@ -490,6 +619,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" @@ -515,6 +649,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + "@babel/helper-wrap-function@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" @@ -553,6 +692,15 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": version "7.16.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" @@ -591,6 +739,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== +"@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" @@ -614,14 +767,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" - integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" "@babel/plugin-proposal-async-generator-functions@^7.16.8": version "7.16.8" @@ -632,13 +785,13 @@ "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-async-generator-functions@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7" - integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q== +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -667,13 +820,13 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" - integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.16.7": @@ -732,12 +885,12 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" - integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": @@ -783,16 +936,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" -"@babel/plugin-proposal-object-rest-spread@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding@^7.16.7": version "7.16.7" @@ -819,13 +972,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.16.11": @@ -854,14 +1007,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" - integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": @@ -922,12 +1075,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" - integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -943,7 +1096,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== @@ -1013,7 +1166,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-typescript@^7.18.6", "@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== @@ -1027,12 +1180,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== +"@babel/plugin-transform-arrow-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-async-to-generator@^7.16.8": version "7.16.8" @@ -1043,14 +1196,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" - integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-transform-block-scoped-functions@^7.16.7": version "7.16.7" @@ -1073,12 +1226,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-block-scoping@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.16.7": version "7.16.7" @@ -1094,18 +1247,18 @@ "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" - integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" @@ -1116,12 +1269,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== +"@babel/plugin-transform-computed-properties@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.16.7": version "7.17.7" @@ -1130,12 +1284,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-destructuring@^7.18.13": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" - integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.16.7" @@ -1190,12 +1344,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== +"@babel/plugin-transform-for-of@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-function-name@^7.16.7": version "7.16.7" @@ -1252,14 +1406,13 @@ "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-amd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" - integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-modules-commonjs@^7.16.8", "@babel/plugin-transform-modules-commonjs@^7.17.7": version "7.17.7" @@ -1271,15 +1424,14 @@ "@babel/helper-simple-access" "^7.17.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" - integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== +"@babel/plugin-transform-modules-commonjs@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" "@babel/plugin-transform-modules-systemjs@^7.16.7": version "7.17.8" @@ -1292,16 +1444,15 @@ "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" - integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A== +"@babel/plugin-transform-modules-systemjs@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-validator-identifier" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.16.7": version "7.16.7" @@ -1326,13 +1477,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" - integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-new-target@^7.16.7": version "7.16.7" @@ -1371,12 +1522,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-parameters@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-property-literals@^7.16.7": version "7.16.7" @@ -1432,13 +1583,13 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== +"@babel/plugin-transform-regenerator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.16.7": version "7.16.7" @@ -1454,13 +1605,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-runtime@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6" - integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA== +"@babel/plugin-transform-runtime@^7.19.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -1500,13 +1651,13 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" -"@babel/plugin-transform-spread@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" - integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.16.7": version "7.16.7" @@ -1678,38 +1829,38 @@ core-js-compat "^3.20.2" semver "^6.3.0" -"@babel/preset-env@^7.19.3": - version "7.19.3" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.3.tgz#52cd19abaecb3f176a4ff9cc5e15b7bf06bec754" - integrity sha512-ziye1OTc9dGFOAXSWKUqQblYHNlBOaDl8wzqf2iKXJAltYiR3hKHUKmkt+S9PppW7RQpq4fFCrwwpIDj/f5P4w== +"@babel/preset-env@^7.20.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.4.tgz#a952482e634a8dd8271a3fe5459a16eb10739c58" + integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw== dependencies: - "@babel/compat-data" "^7.19.3" - "@babel/helper-compilation-targets" "^7.19.3" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.19.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" "@babel/plugin-proposal-dynamic-import" "^7.18.6" "@babel/plugin-proposal-export-namespace-from" "^7.18.9" "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.18.9" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.18.6" + "@babel/plugin-syntax-import-assertions" "^7.20.0" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1719,40 +1870,40 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.20.7" + "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.18.9" - "@babel/plugin-transform-classes" "^7.19.0" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.18.13" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.20.7" + "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-for-of" "^7.21.0" "@babel/plugin-transform-function-name" "^7.18.9" "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.18.6" - "@babel/plugin-transform-modules-commonjs" "^7.18.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.0" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" "@babel/plugin-transform-new-target" "^7.18.6" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-parameters" "^7.21.3" "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.20.5" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-spread" "^7.20.7" "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" "@babel/plugin-transform-unicode-escapes" "^7.18.10" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.19.3" + "@babel/types" "^7.21.4" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -1822,19 +1973,17 @@ pirates "^4.0.5" source-map-support "^0.5.16" -"@babel/runtime@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== +"@babel/runtime@^7.20.13": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.11" "@babel/runtime@^7.4.4": version "7.20.6" @@ -1868,6 +2017,15 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" +"@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3": version "7.17.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" @@ -1900,7 +2058,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.19.1", "@babel/traverse@^7.19.3", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.19.1", "@babel/traverse@^7.19.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4" integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ== @@ -1916,6 +2074,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.9", "@babel/types@^7.19.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624" @@ -1951,10 +2125,35 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@chainsafe/as-sha256@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.4.1.tgz#cfc0737e25f8c206767bdb6703e7943e5d44513e" + integrity sha512-IqeeGwQihK6Y2EYLFofqs2eY2ep1I2MvQXHzOAI+5iQN51OZlUkrLgyAugu2x86xZewDk5xas7lNczkzFzF62w== + +"@chainsafe/persistent-merkle-tree@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.6.1.tgz#37bde25cf6cbe1660ad84311aa73157dc86ec7f2" + integrity sha512-gcENLemRR13+1MED2NeZBMA7FRS0xQPM7L2vhMqvKkjqtFT4YfjSVADq5U0iLuQLhFUJEMVuA8fbv5v+TN6O9A== + dependencies: + "@chainsafe/as-sha256" "^0.4.1" + "@noble/hashes" "^1.3.0" + +"@chainsafe/ssz@^0.11.1": + version "0.11.1" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.11.1.tgz#d4aec883af2ec5196ae67b96242c467da20b2476" + integrity sha512-cB8dBkgGN6ZoeOKuk+rIRHKN0L5i9JLGeC0Lui71QX0TuLcQKwgbfkUexpyJxnGFatWf8yeJxlOjozMn/OTP0g== + dependencies: + "@chainsafe/as-sha256" "^0.4.1" + "@chainsafe/persistent-merkle-tree" "^0.6.1" "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1963,6 +2162,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dependents/detective-less@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-3.0.2.tgz#c6e46997010fe03a5dc98351a7e99a46d34f5832" + integrity sha512-1YUvQ+e0eeTWAHoN8Uz2x2U37jZs6IGutiIE5LXId7cxfUGhtZjzxE06FdUiuiRrW+UE0vNCdSNPH2lY4dQCOQ== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -2011,6 +2218,18 @@ resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -2041,21 +2260,26 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.4.0" - globals "^13.15.0" + espree "^9.5.1" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.39.0", "@eslint/js@^8.38.0": + version "8.39.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" + integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== + "@ethereum-waffle/chai@^3.4.4": version "3.4.4" resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.4.4.tgz#16c4cc877df31b035d6d92486dfdf983df9138ff" @@ -2117,6 +2341,11 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.4" +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + "@ethereumjs/tx@^3.3.0", "@ethereumjs/tx@^3.3.2": version "3.5.1" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.1.tgz#8d941b83a602b4a89949c879615f7ea9a90e6671" @@ -2125,6 +2354,16 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" +"@ethereumjs/util@^8.0.6": + version "8.0.6" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.6.tgz#f9716ed34235ea05eff8353bc5d483e5a6455989" + integrity sha512-zFLG/gXtF3QUC7iKFn4PT6HCr+DEnlCbwUGKGtXoqjA+64T+e0FuqMjlo4bQIY2ngRzk3EtudKdGYC4g31ehhg== + dependencies: + "@chainsafe/ssz" "^0.11.1" + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" @@ -2728,14 +2967,14 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@humanwhocodes/config-array@^0.10.5": - version "0.10.7" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" - integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" "@humanwhocodes/config-array@^0.5.0": version "0.5.0" @@ -2794,186 +3033,33 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.1.2.tgz#0ae975a70004696f8320490fcaa1a4152f7b62e4" - integrity sha512-ujEBCcYs82BTmRxqfHMQggSlkUZP63AE5YEaTPj7eFyJOzukkTorstOUC7L6nE3w5SYadGVAnTsQ/ZjTGL0qYQ== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: - "@jest/types" "^29.1.2" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.1.2" - jest-util "^29.1.2" - slash "^3.0.0" + "@sinclair/typebox" "^0.25.16" -"@jest/core@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.1.2.tgz#e5ce7a71e7da45156a96fb5eeed11d18b67bd112" - integrity sha512-sCO2Va1gikvQU2ynDN8V4+6wB7iVrD2CvT0zaRst4rglf56yLly0NQ9nuRRAWFeimRf+tCdFsb1Vk1N9LrrMPA== - dependencies: - "@jest/console" "^29.1.2" - "@jest/reporters" "^29.1.2" - "@jest/test-result" "^29.1.2" - "@jest/transform" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.0.0" - jest-config "^29.1.2" - jest-haste-map "^29.1.2" - jest-message-util "^29.1.2" - jest-regex-util "^29.0.0" - jest-resolve "^29.1.2" - jest-resolve-dependencies "^29.1.2" - jest-runner "^29.1.2" - jest-runtime "^29.1.2" - jest-snapshot "^29.1.2" - jest-util "^29.1.2" - jest-validate "^29.1.2" - jest-watcher "^29.1.2" - micromatch "^4.0.4" - pretty-format "^29.1.2" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.1.2.tgz#bb51a43fce9f960ba9a48f0b5b556f30618ebc0a" - integrity sha512-rG7xZ2UeOfvOVzoLIJ0ZmvPl4tBEQ2n73CZJSlzUjPw4or1oSWC0s0Rk0ZX+pIBJ04aVr6hLWFn1DFtrnf8MhQ== - dependencies: - "@jest/fake-timers" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - jest-mock "^29.1.2" - -"@jest/expect-utils@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.1.2.tgz#66dbb514d38f7d21456bc774419c9ae5cca3f88d" - integrity sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg== - dependencies: - jest-get-type "^29.0.0" - -"@jest/expect@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.1.2.tgz#334a86395f621f1ab63ad95b06a588b9114d7b7a" - integrity sha512-FXw/UmaZsyfRyvZw3M6POgSNqwmuOXJuzdNiMWW9LCYo0GRoRDhg+R5iq5higmRTHQY7hx32+j7WHwinRmoILQ== - dependencies: - expect "^29.1.2" - jest-snapshot "^29.1.2" - -"@jest/fake-timers@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.1.2.tgz#f157cdf23b4da48ce46cb00fea28ed1b57fc271a" - integrity sha512-GppaEqS+QQYegedxVMpCe2xCXxxeYwQ7RsNx55zc8f+1q1qevkZGKequfTASI7ejmg9WwI+SJCrHe9X11bLL9Q== - dependencies: - "@jest/types" "^29.1.2" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^29.1.2" - jest-mock "^29.1.2" - jest-util "^29.1.2" - -"@jest/globals@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.1.2.tgz#826ede84bc280ae7f789cb72d325c48cd048b9d3" - integrity sha512-uMgfERpJYoQmykAd0ffyMq8wignN4SvLUG6orJQRe9WAlTRc9cdpCaE/29qurXixYJVZWUqIBXhSk8v5xN1V9g== - dependencies: - "@jest/environment" "^29.1.2" - "@jest/expect" "^29.1.2" - "@jest/types" "^29.1.2" - jest-mock "^29.1.2" - -"@jest/reporters@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.1.2.tgz#5520898ed0a4ecf69d8b671e1dc8465d0acdfa6e" - integrity sha512-X4fiwwyxy9mnfpxL0g9DD0KcTmEIqP0jUdnc2cfa9riHy+I6Gwwp5vOZiwyg0vZxfSDxrOlK9S4+340W4d+DAA== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.1.2" - "@jest/test-result" "^29.1.2" - "@jest/transform" "^29.1.2" - "@jest/types" "^29.1.2" - "@jridgewell/trace-mapping" "^0.3.15" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.1.2" - jest-util "^29.1.2" - jest-worker "^29.1.2" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== - dependencies: - "@sinclair/typebox" "^0.24.1" - -"@jest/source-map@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.0.0.tgz#f8d1518298089f8ae624e442bbb6eb870ee7783c" - integrity sha512-nOr+0EM8GiHf34mq2GcJyz/gYFyLQ2INDhAylrZJ9mMWoW21mLBfZa0BUVPPMxVYrLjeiRe2Z7kWXOGnS0TFhQ== - dependencies: - "@jridgewell/trace-mapping" "^0.3.15" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.1.2.tgz#6a8d006eb2b31ce0287d1fc10d12b8ff8504f3c8" - integrity sha512-jjYYjjumCJjH9hHCoMhA8PCl1OxNeGgAoZ7yuGYILRJX9NjgzTN0pCT5qAoYR4jfOP8htIByvAlz9vfNSSBoVg== - dependencies: - "@jest/console" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.1.2.tgz#10bfd89c08bfdba382eb05cc79c1d23a01238a93" - integrity sha512-fU6dsUqqm8sA+cd85BmeF7Gu9DsXVWFdGn9taxM6xN1cKdcP/ivSgXh5QucFRFz1oZxKv3/9DYYbq0ULly3P/Q== - dependencies: - "@jest/test-result" "^29.1.2" - graceful-fs "^4.2.9" - jest-haste-map "^29.1.2" - slash "^3.0.0" - -"@jest/transform@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.1.2.tgz#20f814696e04f090421f6d505c14bbfe0157062a" - integrity sha512-2uaUuVHTitmkx1tHF+eBjb4p7UuzBG7SXIaA/hNIkaMP6K+gXYGxP38ZcrofzqN0HeZ7A90oqsOa97WU7WZkSw== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.1.2" + "@jest/types" "^29.5.0" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" + convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.1.2" - jest-regex-util "^29.0.0" - jest-util "^29.1.2" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" "@jest/types@^27.5.1": version "27.5.1" @@ -2986,12 +3072,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.1.2": - version "29.1.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.1.2.tgz#7442d32b16bcd7592d9614173078b8c334ec730a" - integrity sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -3040,6 +3126,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== +"@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -3056,7 +3147,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.8": +"@jridgewell/trace-mapping@^0.3.15": version "0.3.16" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz#a7982f16c18cae02be36274365433e5b49d7b23f" integrity sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA== @@ -3064,6 +3155,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" @@ -3077,6 +3176,18 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@metamask/eth-sig-util@^5.0.2": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-5.1.0.tgz#a47f62800ee1917fef976ba67544a0ccd7d1bd6b" + integrity sha512-mlgziIHYlA9pi/XZerChqg4NocdOgBPB9NmxgXWQO2U2hH8RGOJQrz6j/AIKkYxgCMIE2PY000+joOwXfzeTDQ== + dependencies: + "@ethereumjs/util" "^8.0.6" + bn.js "^4.12.0" + ethereum-cryptography "^2.0.0" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" @@ -3087,25 +3198,22 @@ resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== -"@noble/hashes@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== - -"@noble/hashes@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.3.tgz#360afc77610e0a61f3417e497dcf36862e4f8111" - integrity sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A== +"@noble/curves@1.0.0", "@noble/curves@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" + integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== + dependencies: + "@noble/hashes" "1.3.0" -"@noble/secp256k1@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" - integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== +"@noble/hashes@1.3.0", "@noble/hashes@^1.3.0", "@noble/hashes@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== -"@noble/secp256k1@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1" - integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw== +"@noble/secp256k1@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -3120,7 +3228,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -3136,106 +3244,106 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== +"@octokit/auth-token@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c" + integrity sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" -"@octokit/core@^3.5.1": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" - integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.3" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" +"@octokit/core@^4.1.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" + integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg== + dependencies: + "@octokit/auth-token" "^3.0.0" + "@octokit/graphql" "^5.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== +"@octokit/endpoint@^7.0.0": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1" + integrity sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== +"@octokit/graphql@^5.0.0": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" + integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ== dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== +"@octokit/openapi-types@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622" + integrity sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog== -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" - integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== +"@octokit/plugin-paginate-rest@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz#f34b5a7d9416019126042cd7d7b811e006c0d561" + integrity sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw== dependencies: - "@octokit/types" "^6.40.0" + "@octokit/types" "^9.0.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" - integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== +"@octokit/plugin-rest-endpoint-methods@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz#f7ebe18144fd89460f98f35a587b056646e84502" + integrity sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA== dependencies: - "@octokit/types" "^6.39.0" + "@octokit/types" "^9.0.0" deprecation "^2.3.1" -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== +"@octokit/request-error@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== +"@octokit/request@^6.0.0": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4" + integrity sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA== dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" + "@octokit/endpoint" "^7.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^18.0.9": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== +"@octokit/rest@^19.0.5": + version "19.0.7" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.7.tgz#d2e21b4995ab96ae5bfae50b4969da7e04e0bb70" + integrity sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA== dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/core" "^4.1.0" + "@octokit/plugin-paginate-rest" "^6.0.0" "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + "@octokit/plugin-rest-endpoint-methods" "^7.0.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": - version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== +"@octokit/types@^9.0.0": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.2.tgz#1a8d35b1f4a3d2ad386e223f249dd5f7506979c1" + integrity sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ== dependencies: - "@octokit/openapi-types" "^12.11.0" + "@octokit/openapi-types" "^17.0.0" "@open-web3/orml-type-definitions@1.1.4": version "1.1.4" @@ -3266,561 +3374,415 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== -"@polkadot/api-augment@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.2.4.tgz#6d12e6e702828facd40606efbb494d80c1372db4" - integrity sha512-oL8JJS3RClbv+yVdVTjRHoU2d3az/YvD+Ex2UFnMtBCBk22kDnDEoJAstJJjnXW2RT7CsEmCj3ffP0pmJGvF4A== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api-base" "9.2.4" - "@polkadot/rpc-augment" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/types-augment" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/util" "^10.1.6" - -"@polkadot/api-base@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.2.4.tgz#6279da0fb2ce6a044fc03ce28112322908f20193" - integrity sha512-iTSsenaKAWTrkyDkbvBlhx/hzS/DlPPcw+u/Z2EQfnsh6R8qR7Od5JbFKM5Z81mlBX2R3FYm6r2Cxan0Bb+bDA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-core" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/util" "^10.1.6" - rxjs "^7.5.6" - -"@polkadot/api-derive@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.2.4.tgz#2abdf94d69cc4b2596125f630452237cb62c4988" - integrity sha512-16V+l8hNe+TMYbZf6Bq27ZT69HXZC1Fn/mq0IrzdAs4jW2mok5tDfHDL+rCGJeRTq1LXFUOfPZGTMR4bqH2VqA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api" "9.2.4" - "@polkadot/api-augment" "9.2.4" - "@polkadot/api-base" "9.2.4" - "@polkadot/rpc-core" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/util" "^10.1.6" - "@polkadot/util-crypto" "^10.1.6" - rxjs "^7.5.6" - -"@polkadot/api@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.2.4.tgz#501a271d61dd5e1a81d08e0183a4a4bc0a081693" - integrity sha512-mtTnTpix8lvjZnKgB3vnYE9frGPil2QbUd6uD2Vu/q2izRZuuPrMCAQjMf9o8CRJfeC4149jX3VZ5xq9gmYHrg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api-augment" "9.2.4" - "@polkadot/api-base" "9.2.4" - "@polkadot/api-derive" "9.2.4" - "@polkadot/keyring" "^10.1.6" - "@polkadot/rpc-augment" "9.2.4" - "@polkadot/rpc-core" "9.2.4" - "@polkadot/rpc-provider" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/types-augment" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/types-create" "9.2.4" - "@polkadot/types-known" "9.2.4" - "@polkadot/util" "^10.1.6" - "@polkadot/util-crypto" "^10.1.6" - eventemitter3 "^4.0.7" - rxjs "^7.5.6" - -"@polkadot/dev@^0.67.86": - version "0.67.138" - resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.67.138.tgz#9bd0fd8357a5f8cc4736f477bd26b6acdf453ca0" - integrity sha512-C46rgXEqqAlc8xGG3dC0H5PXINPbBcS0umzgpjNuNiNT3hwXHyIGe4pfogCVLD1eW6Z5oqvKBrs1+iH9CN6LSQ== - dependencies: - "@babel/cli" "^7.19.3" - "@babel/core" "^7.19.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.18.6" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-regenerator" "^7.18.6" - "@babel/plugin-transform-runtime" "^7.19.1" - "@babel/preset-env" "^7.19.3" - "@babel/preset-react" "^7.18.6" - "@babel/preset-typescript" "^7.18.6" - "@babel/register" "^7.18.9" - "@babel/runtime" "^7.19.0" - "@rollup/plugin-alias" "^3.1.9" - "@rollup/plugin-commonjs" "^22.0.2" - "@rollup/plugin-dynamic-import-vars" "^1.4.4" - "@rollup/plugin-inject" "^4.0.4" - "@rollup/plugin-json" "^4.1.0" - "@rollup/plugin-node-resolve" "^14.1.0" +"@polkadot/api-augment@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.3.2.tgz#52d4588d715f55dff7c9b0cbabdc04943dd7a493" + integrity sha512-73bzQnjVmOHf/PIzSQru2jqlkVivtIvXoABkq87QIDxsZvUIQPXXIHnVb9C96yJuLE4St22w4y1qI9XSwau96g== + dependencies: + "@polkadot/api-base" "10.3.2" + "@polkadot/rpc-augment" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/types-augment" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/api-base@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-10.3.2.tgz#1b7854d34c733b43c7268fa3ef1cb55be42b458b" + integrity sha512-5UPVDmYrxLCfKmCKwaiKqK1s8x3nHbi5vulUDj31P9f///oWZGwjsXdp2hn4FGoEEi1pKuMUK+dRRMEMrj3gPA== + dependencies: + "@polkadot/rpc-core" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/util" "^11.1.3" + rxjs "^7.8.0" + tslib "^2.5.0" + +"@polkadot/api-derive@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-10.3.2.tgz#3d2fb8e8b0f6b824300cfda267e92bb4f6afc3df" + integrity sha512-vU6ymw/XykrPZseCgO2BgsX9niRfystXAVUltxO9m/aqZdmf3yeFl+77MwQd9zdLsPOmcoWpiEPRRKWQThqgSA== + dependencies: + "@polkadot/api" "10.3.2" + "@polkadot/api-augment" "10.3.2" + "@polkadot/api-base" "10.3.2" + "@polkadot/rpc-core" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/util" "^11.1.3" + "@polkadot/util-crypto" "^11.1.3" + rxjs "^7.8.0" + tslib "^2.5.0" + +"@polkadot/api@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-10.3.2.tgz#59236ca7a8f5058a94ee48b8aa085f50ddd8a6d6" + integrity sha512-7lJI9bHHfiepbQvEdgqrt60jmFCw/6FvSqi9R2S9RbYdz/LD0PVF1GavpyU0tWDUfFg+jUGi2Qw4DLml455BiQ== + dependencies: + "@polkadot/api-augment" "10.3.2" + "@polkadot/api-base" "10.3.2" + "@polkadot/api-derive" "10.3.2" + "@polkadot/keyring" "^11.1.3" + "@polkadot/rpc-augment" "10.3.2" + "@polkadot/rpc-core" "10.3.2" + "@polkadot/rpc-provider" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/types-augment" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/types-create" "10.3.2" + "@polkadot/types-known" "10.3.2" + "@polkadot/util" "^11.1.3" + "@polkadot/util-crypto" "^11.1.3" + eventemitter3 "^5.0.0" + rxjs "^7.8.0" + tslib "^2.5.0" + +"@polkadot/dev-test@^0.72.48": + version "0.72.48" + resolved "https://registry.yarnpkg.com/@polkadot/dev-test/-/dev-test-0.72.48.tgz#922c6a654136f335e9546bf70a835aa661d744c9" + integrity sha512-33Rt+EljS6T7GvGu54e5hm4sKGdFWH77Unpq6x7V8XGjncfuFRyhFL4Brnqws9khqQLBZG+Fvx9ktF+LPuWakw== + dependencies: + jsdom "^21.1.1" + tslib "^2.5.0" + +"@polkadot/dev-ts@^0.72.48": + version "0.72.48" + resolved "https://registry.yarnpkg.com/@polkadot/dev-ts/-/dev-ts-0.72.48.tgz#8603e4a0040d11ee0b727aaa30e99e8fc8865020" + integrity sha512-4jI+qekFfD0DkOUTnUjroJ/ni/SH5TxqFKbvNjKB42iTA9X8aQXSeyUrlkc701Y7qASuVgQ5rXk4myEeEjgS6A== + dependencies: + json5 "^2.2.3" + tslib "^2.5.0" + typescript "^5.0.4" + +"@polkadot/dev@^0.72.42": + version "0.72.48" + resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.72.48.tgz#9473199cdb9384c4e28b8f89055b4d8155329b05" + integrity sha512-4kPPN+U+bq6KFMuaqR24PCenv3ZV59zQ9nDihqkjmSnAkvwskojIYK33Vg9C28XWqyu30gGOzhKycTzuWmgkaw== + dependencies: + "@eslint/js" "^8.38.0" + "@polkadot/dev-test" "^0.72.48" + "@polkadot/dev-ts" "^0.72.48" + "@rollup/plugin-alias" "^5.0.0" + "@rollup/plugin-commonjs" "^24.1.0" + "@rollup/plugin-dynamic-import-vars" "^2.0.3" + "@rollup/plugin-inject" "^5.0.3" + "@rollup/plugin-json" "^6.0.0" + "@rollup/plugin-node-resolve" "^15.0.2" "@rushstack/eslint-patch" "^1.2.0" - "@typescript-eslint/eslint-plugin" "5.39.0" - "@typescript-eslint/parser" "5.39.0" - "@vue/component-compiler-utils" "^3.3.0" - babel-jest "^29.1.2" - babel-plugin-module-extension-resolver "^1.0.0-rc.2" - babel-plugin-module-resolver "^4.1.0" - babel-plugin-styled-components "^2.0.7" - browserslist "^4.21.4" - coveralls "^3.1.1" - eslint "^8.24.0" + "@tsconfig/esm" "^1.0.3" + "@tsconfig/strictest" "^2.0.1" + "@typescript-eslint/eslint-plugin" "^5.59.0" + "@typescript-eslint/parser" "^5.59.0" + eslint "^8.38.0" eslint-config-standard "^17.0.0" - eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-node "^0.3.7" + eslint-plugin-deprecation "^1.4.1" eslint-plugin-header "^3.1.1" - eslint-plugin-import "^2.26.0" - eslint-plugin-import-newlines "^1.2.3" - eslint-plugin-n "^15.3.0" - eslint-plugin-promise "^6.0.1" - eslint-plugin-react "^7.31.8" + eslint-plugin-import "^2.27.5" + eslint-plugin-import-newlines "^1.3.1" + eslint-plugin-jest "^27.2.1" + eslint-plugin-n "^15.7.0" + eslint-plugin-promise "^6.1.1" + eslint-plugin-react "^7.32.2" eslint-plugin-react-hooks "^4.6.0" - eslint-plugin-simple-import-sort "^8.0.0" - eslint-plugin-sort-destructure-keys "^1.4.0" - fs-extra "^10.1.0" - gh-pages "^4.0.0" - gh-release "^6.0.4" - glob "^8.0.3" - glob2base "^0.0.12" - jest "^29.1.2" - jest-cli "^29.1.2" - jest-config "^29.1.2" - jest-environment-jsdom "^29.1.2" - jest-haste-map "^29.1.2" - jest-resolve "^29.1.2" - madge "^5.0.1" - minimatch "^5.1.0" - mkdirp "^1.0.4" - prettier "^2.7.1" - rimraf "^3.0.2" - rollup "^2.79.1" + eslint-plugin-simple-import-sort "^10.0.0" + eslint-plugin-sort-destructure-keys "^1.5.0" + gh-pages "^5.0.0" + gh-release "^7.0.2" + madge "^6.0.0" + rollup "^3.20.7" rollup-plugin-cleanup "^3.2.1" - typescript "^4.8.4" - webpack "^5.74.0" - webpack-cli "^4.10.0" - webpack-dev-server "^4.11.1" + tslib "^2.5.0" + typescript "^5.0.4" + webpack "^5.80.0" + webpack-cli "^5.0.1" + webpack-dev-server "^4.13.3" webpack-merge "^5.8.0" - webpack-subresource-integrity "^5.1.0" - yargs "^17.6.0" - -"@polkadot/keyring@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.7.tgz#d51be1dc5807c961889847d8f0e10e4bbdd19d3f" - integrity sha512-lArwaAS3hDs+HHupDIC4r2mFaAfmNQV2YzwL2wM5zhOqB2RugN03BFrgwNll0y9/Bg8rYDqM3Y5BvVMzgMZ6XA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.7" - "@polkadot/util-crypto" "10.1.7" - -"@polkadot/keyring@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.10.tgz#f9d97ab528e612df4820e0df1837faae51c15904" - integrity sha512-crKYBbwmPcFoTP6mby2+o1QWsjAyi5QlKzU8tXuXOApP6SBuqmDujIuLOKNG2vZoftNdVldsVL0WmKVYtBeuQg== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/util" "10.1.10" - "@polkadot/util-crypto" "10.1.10" - -"@polkadot/networks@10.1.10", "@polkadot/networks@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.10.tgz#421ce200f8b26759edd1d9d9408d934527f5b455" - integrity sha512-Db78t2XnFIZbdSdu1aFuj3/1cNwcSzG/+wNrpCQ9dPhnGPy5S1GVbmU8pyxTftPKdTFc+8RdBr+5bc0d5ijGiA== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/util" "10.1.10" - "@substrate/ss58-registry" "^1.31.0" - -"@polkadot/networks@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.7.tgz#33b38d70409e2daf0990ef18ff150c6718ffb700" - integrity sha512-ol864SZ/GwAF72GQOPRy+Y9r6NtgJJjMBlDLESvV5VK64eEB0MRSSyiOdd7y/4SumR9crrrNimx3ynACFgxZ8A== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.7" - "@substrate/ss58-registry" "^1.28.0" - -"@polkadot/rpc-augment@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.2.4.tgz#372eec7f15aea8f0ba3cb80affb0f216e6971e26" - integrity sha512-Jhw0r1CDr1CgkXdq/9zVNzQe4Yipk+NZ+uBWgBxfk+5ceaVsPjcwDG4A+J7og6I3fiWpz7srY1MC2Q8tikn6BA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-core" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/util" "^10.1.6" - -"@polkadot/rpc-core@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.2.4.tgz#83e5828b53e27c976636bafe4486979df420cd93" - integrity sha512-npGOFC3BIjIxb4+jK4hISlUjsY8ay6GsLD5qhxEeiypk3pDNeHuBN/LjN4Z5Cb9N6e2fDgcLP4HRZG+YTf8ccA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-augment" "9.2.4" - "@polkadot/rpc-provider" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/util" "^10.1.6" - rxjs "^7.5.6" - -"@polkadot/rpc-provider@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.2.4.tgz#c25db404642516ccd4d461325c2dcec08637fba9" - integrity sha512-qVlaDr/Oax764mps89tQpKFohOacFta5Gp72s81OBxFR1Nxq9qE5uaL5AaXzAxEtL+XVMFYKE508pp2IJW52dw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/keyring" "^10.1.6" - "@polkadot/types" "9.2.4" - "@polkadot/types-support" "9.2.4" - "@polkadot/util" "^10.1.6" - "@polkadot/util-crypto" "^10.1.6" - "@polkadot/x-fetch" "^10.1.6" - "@polkadot/x-global" "^10.1.6" - "@polkadot/x-ws" "^10.1.6" - "@substrate/connect" "0.7.11" - eventemitter3 "^4.0.7" - mock-socket "^9.1.5" - nock "^13.2.9" - -"@polkadot/typegen@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/typegen/-/typegen-9.2.4.tgz#766af32c3ab1a6ba4438ef3f5e02b9b56d85e3da" - integrity sha512-13dSv7ucLAaMWJTzYSErPfFfKCZPp8cMp5l8XcmUEk/zc5GuLCII+A4Fj+7FGYXjntUvf+DhV4hgzGMitjM0SQ== - dependencies: - "@babel/core" "^7.18.13" - "@babel/register" "^7.18.9" - "@babel/runtime" "^7.18.9" - "@polkadot/api" "9.2.4" - "@polkadot/api-augment" "9.2.4" - "@polkadot/rpc-augment" "9.2.4" - "@polkadot/rpc-provider" "9.2.4" - "@polkadot/types" "9.2.4" - "@polkadot/types-augment" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/types-create" "9.2.4" - "@polkadot/types-support" "9.2.4" - "@polkadot/util" "^10.1.6" - "@polkadot/util-crypto" "^10.1.6" - "@polkadot/x-ws" "^10.1.6" + webpack-subresource-integrity "^5.2.0-rc.1" + yargs "^17.7.1" + +"@polkadot/keyring@11.1.3", "@polkadot/keyring@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-11.1.3.tgz#8718f14996ecdb389acffc6ecbe7deb8a2d74b5f" + integrity sha512-bzGz1cWDYK7MWhp0630W6KOwTC/wsvKKHBvWxReMT7iQwFHeLn5AemUOveqIPxF+esd/UfdN5aFDHApjYcyZsg== + dependencies: + "@polkadot/util" "11.1.3" + "@polkadot/util-crypto" "11.1.3" + tslib "^2.5.0" + +"@polkadot/networks@11.1.3", "@polkadot/networks@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-11.1.3.tgz#e113c98269328267962c2047dccca4d2790cc8a5" + integrity sha512-goLpX9SswAGGeh1jXB79wHEfWOF5rLIItMHYalujBmhQVxyAqbxP2tzQqPQXDLcnkWbgwkyYGLXaDD72GBqHZw== + dependencies: + "@polkadot/util" "11.1.3" + "@substrate/ss58-registry" "^1.39.0" + tslib "^2.5.0" + +"@polkadot/rpc-augment@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-10.3.2.tgz#486c152e222bf14c2489e1b3ff7ac98d90f6cd93" + integrity sha512-P8pUywOtVc5jSoMMhtZSYqyvLNaf2x/tsDaC0sct1yHg5KkXjTCnOgRTepjObgsQWWW5SjKudhhXr8Yfv2XbWQ== + dependencies: + "@polkadot/rpc-core" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/rpc-core@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-10.3.2.tgz#07f0a483a3e092db1c2231eebe613faf4606cfbb" + integrity sha512-XUQ6QzcAG7NkhenOtV2zhJjrSsQntmJHfwt8Hp5JZdap/bTsRLGl66N4mrK0Lm5535OiDYFEGXxnLZM10IjtRA== + dependencies: + "@polkadot/rpc-augment" "10.3.2" + "@polkadot/rpc-provider" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/util" "^11.1.3" + rxjs "^7.8.0" + tslib "^2.5.0" + +"@polkadot/rpc-provider@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-10.3.2.tgz#2b3347246f9be6f9f360a075eaf862c8fbccf530" + integrity sha512-torI91dgZXPYIjUSU1/YzTK9mK3H/36JZhJFrdrw63PR3Lh5/cdQ8TFJ5gJ2naVXZB/GIOH7GAhm38ByL17wig== + dependencies: + "@polkadot/keyring" "^11.1.3" + "@polkadot/types" "10.3.2" + "@polkadot/types-support" "10.3.2" + "@polkadot/util" "^11.1.3" + "@polkadot/util-crypto" "^11.1.3" + "@polkadot/x-fetch" "^11.1.3" + "@polkadot/x-global" "^11.1.3" + "@polkadot/x-ws" "^11.1.3" + eventemitter3 "^5.0.0" + mock-socket "^9.2.1" + nock "^13.3.0" + tslib "^2.5.0" + optionalDependencies: + "@substrate/connect" "0.7.23" + +"@polkadot/typegen@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/typegen/-/typegen-10.3.2.tgz#fbba8bb39445d54240d5f9a86c620db9e965e4ff" + integrity sha512-ho0oRXRTQfu85nOOHAZ/N1LMPzkn6gaT82PwUi2Rvt7hXCZVK5sYe6s9NSRK6eqlrp9gRXS55rmo+H7WQ7OIVw== + dependencies: + "@polkadot/api" "10.3.2" + "@polkadot/api-augment" "10.3.2" + "@polkadot/rpc-augment" "10.3.2" + "@polkadot/rpc-provider" "10.3.2" + "@polkadot/types" "10.3.2" + "@polkadot/types-augment" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/types-create" "10.3.2" + "@polkadot/types-support" "10.3.2" + "@polkadot/util" "^11.1.3" + "@polkadot/util-crypto" "^11.1.3" + "@polkadot/x-ws" "^11.1.3" handlebars "^4.7.7" - websocket "^1.0.34" - yargs "^17.5.1" - -"@polkadot/types-augment@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.2.4.tgz#dc9307012bf7adfa8329413e716dda351cd7e5f1" - integrity sha512-3Y8I7ZjCBWJwnooegEqBvsbYk9EOAfYhX1g+IxYFL/xTUO4mdNrgmaslHele2M7h//N+xW+iZ5R8IGRYOka1ew== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/types" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/util" "^10.1.6" - -"@polkadot/types-codec@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.2.4.tgz#4ae6bd33fbc7cb41b3b8dc41d49325393fd4f22f" - integrity sha512-3Wy7KOk6qd0v6rUQ2Qq5bz6xAo2KCp1Sn1GYa7VPNcFZfZaBO3Axgg3w3++ckDqLt8bI99Y/wCuAKhzvnMs/WA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "^10.1.6" - "@polkadot/x-bigint" "^10.1.6" - -"@polkadot/types-create@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.2.4.tgz#be920f7ae3404e655afc2cd0b63a6ccebe2ea112" - integrity sha512-RU5r4GlhNFhd3QUzUj9gxfRNlH4LgvHEXTS/K5pprlKTmoJk9HcpzbnkydM8qb1kc3JJWpYgG3Ov82y6HWDjDg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/types-codec" "9.2.4" - "@polkadot/util" "^10.1.6" - -"@polkadot/types-known@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.2.4.tgz#940e18aa18dea885051db5fabddd83a81580ba52" - integrity sha512-AEf0Qj/HjoCZTbIdNHXFQt+bbjaNpVdSV+yWIJNTtpRz6IcFNkddiW+4VRlesJh8M6zNy1S4BUcJlee3OqWJbg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/networks" "^10.1.6" - "@polkadot/types" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/types-create" "9.2.4" - "@polkadot/util" "^10.1.6" - -"@polkadot/types-support@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.2.4.tgz#5446791a876dedc629ff48fd0066ff306d2a2ec7" - integrity sha512-+Ci3dr6vieIybtfNERMeujzhr1P6i0napLm1bFb3QAiZ8sOaeF9i1y/LUsIHSFCb7D4joyJpaRK0WwLSV17WBA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "^10.1.6" - -"@polkadot/types@9.2.4": - version "9.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.2.4.tgz#05818a2514f4d87c3339f6e000174ffccd6e0385" - integrity sha512-AnoTE2ORB0GfDz0JXwMqGiUieIxJSledxmiGTMF3Oj68WLWtg5yEMQZzYnbAELBLhscYdp1MHwYNBli72M3Q2Q== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/keyring" "^10.1.6" - "@polkadot/types-augment" "9.2.4" - "@polkadot/types-codec" "9.2.4" - "@polkadot/types-create" "9.2.4" - "@polkadot/util" "^10.1.6" - "@polkadot/util-crypto" "^10.1.6" - rxjs "^7.5.6" - -"@polkadot/util-crypto@10.1.10", "@polkadot/util-crypto@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.10.tgz#ae00c794dda6a2f5e40cdaaa87987312461dc59f" - integrity sha512-w9h/wf4wZXeUkRnihhnfqlaKuoQtrjkjK3C5liCQkr9vx5zOsmg/nMSDP8UUFJX0msPPYpFeNvzn7oDIs6qSZA== - dependencies: - "@babel/runtime" "^7.19.0" - "@noble/hashes" "1.1.3" - "@noble/secp256k1" "1.7.0" - "@polkadot/networks" "10.1.10" - "@polkadot/util" "10.1.10" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.10" - "@polkadot/x-randomvalues" "10.1.10" + tslib "^2.5.0" + yargs "^17.7.1" + +"@polkadot/types-augment@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-10.3.2.tgz#569c0e0758b65a1a993ae26b96cb8395087323e5" + integrity sha512-ECtf8zYak+lJ6eMrQKigUB0FlqoQXPm1bJ5EKCw1Lkgf45sSgAkaY8rHgon4fzbxwVc3JQR7KUiumvRBd1dvnQ== + dependencies: + "@polkadot/types" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/types-codec@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-10.3.2.tgz#eae4fd2e83bdd8cbe78ac2842719b54e07b866ef" + integrity sha512-gsTyg11bcoAhCym8SlNcZ5bPrLUUq86HnWa3Yqq0+Sso+Skc0LO3FwBwbg+HS4EospMqoLnJEt+5CFBStr2M0A== + dependencies: + "@polkadot/util" "^11.1.3" + "@polkadot/x-bigint" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/types-create@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-10.3.2.tgz#8ed5582fc4a0517b19a32fb9bb258ad33e6dcb0d" + integrity sha512-y0kdN75iFtjZ8VJ8+bq2csVIb03FWHQzUohoH0VFrDOs1dAqMTWKBiBTpkEN+Dd9ZyBjsQphgJlMbIEd6Prx+w== + dependencies: + "@polkadot/types-codec" "10.3.2" + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/types-known@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-10.3.2.tgz#b202e4b0d41e9ba2a0946f5e8e7a8497aefe4de5" + integrity sha512-eNiYCLbGYzjPt4maJuNubhxjebiFoyoAFxzADOXSqQS5dg93GdEa2ZvSNlffp9XYoTi0fqkV2vuDfdvb6tD3Iw== + dependencies: + "@polkadot/networks" "^11.1.3" + "@polkadot/types" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/types-create" "10.3.2" + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/types-support@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-10.3.2.tgz#f2718d9facebbafbcff46434dc9faed2e7d6f556" + integrity sha512-gqYzfj5BfmFWU/f5TaK6MovF9vb4oiOecNKDMsFlH+i3cCUpIYeUDFhYRoPRnfVf4sjLNvgS2NYbl5t5+ymQIQ== + dependencies: + "@polkadot/util" "^11.1.3" + tslib "^2.5.0" + +"@polkadot/types@10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-10.3.2.tgz#b9f475524edba08948ddd72681054fbeaf38249c" + integrity sha512-dwCB2S8yGS2gdvAMlqTiKB+ysGoiZgqhyD+hxpE2JsDtjDHb5q6BCAyeKGnG7BcUHdnspYAt9g0EcjRDkTt5Tw== + dependencies: + "@polkadot/keyring" "^11.1.3" + "@polkadot/types-augment" "10.3.2" + "@polkadot/types-codec" "10.3.2" + "@polkadot/types-create" "10.3.2" + "@polkadot/util" "^11.1.3" + "@polkadot/util-crypto" "^11.1.3" + rxjs "^7.8.0" + tslib "^2.5.0" + +"@polkadot/util-crypto@11.1.3", "@polkadot/util-crypto@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-11.1.3.tgz#c3b166f8f8934a2139c8cfb31af50dae53a9d985" + integrity sha512-hjH1y6jXQuceJ2NWx7+ei0sR4A7t844XwlNquPxZX3kQbQS+1t6tO4Eo3/95JhPsEaJOXduus02cYEF6gteEYQ== + dependencies: + "@noble/hashes" "1.3.0" + "@noble/secp256k1" "1.7.1" + "@polkadot/networks" "11.1.3" + "@polkadot/util" "11.1.3" + "@polkadot/wasm-crypto" "^7.0.3" + "@polkadot/x-bigint" "11.1.3" + "@polkadot/x-randomvalues" "11.1.3" "@scure/base" "1.1.1" - ed2curve "^0.3.0" + tslib "^2.5.0" tweetnacl "^1.0.3" -"@polkadot/util-crypto@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.7.tgz#fe5ea006bf23ae19319f3ac9236905a984a65e2f" - integrity sha512-zGmSU7a0wdWfpDtfc+Q7fUuW+extu9o1Uh4JpkuwwZ/dxmyW5xlfqVsIScM1pdPyjJsyamX8KwsKiVsW4slasg== - dependencies: - "@babel/runtime" "^7.18.9" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@polkadot/networks" "10.1.7" - "@polkadot/util" "10.1.7" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.7" - "@polkadot/x-randomvalues" "10.1.7" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util@10.1.10", "@polkadot/util@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.10.tgz#dc79c93d70e699e8cc1b09e636af7e4a64c5f0f0" - integrity sha512-BQoTfSxZ3BWAgWDjgKBVdyw1AJGaoOeAidCA+LZcHV6wlMu5643AZPUnoMrW413MbbpxsIhJXtNttqOwjo8MjA== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-bigint" "10.1.10" - "@polkadot/x-global" "10.1.10" - "@polkadot/x-textdecoder" "10.1.10" - "@polkadot/x-textencoder" "10.1.10" - "@types/bn.js" "^5.1.1" - bn.js "^5.2.1" - -"@polkadot/util@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.6.tgz#fe828533902b6983de833524c1f5884569a2ac2b" - integrity sha512-k+gCKmgwxp0smmLIR7SfiEYEToayWXjrC7pQ0PqAGxpBNOdVMSCzLMnOHf9AI5cjs/lx6ULr1fHn721wLVonkw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-bigint" "10.1.6" - "@polkadot/x-global" "10.1.6" - "@polkadot/x-textdecoder" "10.1.6" - "@polkadot/x-textencoder" "10.1.6" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.1" - -"@polkadot/util@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.7.tgz#c54ca2a5b29cb834b40d8a876baefa3a0efb93af" - integrity sha512-s7gDLdNb4HUpoe3faXwoO6HwiUp8pi66voYKiUYRh1kEtW1o9vGBgyZPHPGC/FBgILzTJKii/9XxnSex60zBTA== +"@polkadot/util@11.1.3", "@polkadot/util@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-11.1.3.tgz#dcdc4504f7c31e6104e7970903d8c1998f3858ef" + integrity sha512-Gsqzv1/fSoypS5tnJkM+NJQeT7O4iYlSniubUJnaZVOKsIbueTS1bMQ1y3/h8ISxbKBtICW5cZ6zCej6Q/jC3w== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-bigint" "10.1.7" - "@polkadot/x-global" "10.1.7" - "@polkadot/x-textdecoder" "10.1.7" - "@polkadot/x-textencoder" "10.1.7" + "@polkadot/x-bigint" "11.1.3" + "@polkadot/x-global" "11.1.3" + "@polkadot/x-textdecoder" "11.1.3" + "@polkadot/x-textencoder" "11.1.3" "@types/bn.js" "^5.1.1" bn.js "^5.2.1" + tslib "^2.5.0" -"@polkadot/wasm-bridge@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-6.3.1.tgz#439fa78e80947a7cb695443e1f64b25c30bb1487" - integrity sha512-1TYkHsb9AEFhU9uZj3biEnN2yKQNzdrwSjiTvfCYnt97pnEkKsZI6cku+YPZQv5w/x9CQa5Yua9e2DVVZSivGA== - dependencies: - "@babel/runtime" "^7.18.9" - -"@polkadot/wasm-crypto-asmjs@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-6.3.1.tgz#e8f469c9cf4a7709c8131a96f857291953f3e30a" - integrity sha512-zbombRfA5v/mUWQQhgg2YwaxhRmxRIrvskw65x+lruax3b6xPBFDs7yplopiJU3r8h2pTgQvX/DUksvqz2TCRQ== - dependencies: - "@babel/runtime" "^7.18.9" - -"@polkadot/wasm-crypto-init@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-6.3.1.tgz#b590220c53c94b9a54d5dc236d0cbe943db76706" - integrity sha512-9yaUBcu+snwjJLmPPGl3cyGRQ1afyFGm16qzTM0sgG/ZCfUlK4uk8KWZe+sBUKgoxb2oXY7Y4WklKgQI1YBdfw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-bridge" "6.3.1" - "@polkadot/wasm-crypto-asmjs" "6.3.1" - "@polkadot/wasm-crypto-wasm" "6.3.1" - -"@polkadot/wasm-crypto-wasm@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-6.3.1.tgz#67f720e7f9694fef096abe9d60abbac02e032383" - integrity sha512-idSlzKGVzCfeCMRHsacRvqwojSaTadFxL/Dbls4z1thvfa3U9Ku0d2qVtlwg7Hj+tYWDiuP8Kygs+6bQwfs0XA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-util" "6.3.1" - -"@polkadot/wasm-crypto@^6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-6.3.1.tgz#63f5798aca2b2ff0696f190e6862d9781d8f280c" - integrity sha512-OO8h0qeVkqp4xYZaRVl4iuWOEtq282pNBHDKb6SOJuI2g59eWGcKh4EQU9Me2VP6qzojIqptrkrVt7KQXC68gA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-bridge" "6.3.1" - "@polkadot/wasm-crypto-asmjs" "6.3.1" - "@polkadot/wasm-crypto-init" "6.3.1" - "@polkadot/wasm-crypto-wasm" "6.3.1" - "@polkadot/wasm-util" "6.3.1" - -"@polkadot/wasm-util@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-6.3.1.tgz#439ebb68a436317af388ed6438b8f879df3afcda" - integrity sha512-12oAv5J7Yoc9m6jixrSaQCxpOkWOyzHx3DMC8qmLjRiwdBWxqLmImOVRVnFsbaxqSbhBIHRuJphVxWE+GZETDg== - dependencies: - "@babel/runtime" "^7.18.9" - -"@polkadot/x-bigint@10.1.10", "@polkadot/x-bigint@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.10.tgz#97dcccda2e24776aad3ae49f3268c03332138246" - integrity sha512-4Jt0BO0WTby6r9A2DgkDxf/LFaICQHvSl1VSFtBf0Z0GV2n4OxkBX5x/1bdEdGEvYT5rM7RbR3xI7EL+W1ixHA== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" - -"@polkadot/x-bigint@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.6.tgz#25ec3af7217862808e20ccf0ecd1dfafb1bc31bd" - integrity sha512-yeBZQ9+u49KqDBaeSw+ytshqzyaScKrDjAxpWCfOGxJaB+5Nv1W7fqi3OJ4S/HN5DYItr0a6UC14e1hiZUtZCg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" - -"@polkadot/x-bigint@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.7.tgz#1338689476ffdbb9f9cb243df1954ae8186134b9" - integrity sha512-uaClHpI6cnDumIfejUKvNTkB43JleEb0V6OIufDKJ/e1aCLE3f/Ws9ggwL8ea05lQP5k5xqOzbPdizi/UvrgKQ== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.7" - -"@polkadot/x-fetch@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.1.10.tgz#6551e44211cf21a1ca076eedc4676754345e1504" - integrity sha512-LvTxAN6GaJzfgZ74WFYPZrIkMEThpX5u7O4ILiExcJt87E19cSWlYSHDa5n+OLjUpq0lBV2ueF90iUblt6aHpg== +"@polkadot/wasm-bridge@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.1.1.tgz#8d18ba0b8659237bff985ab8f2b07c1276ce670e" + integrity sha512-2YKaqrGaN+e+OjKxs1sAS822Jil6GJE8xhNTs8an7BtgEKtfBjQ/j2ZIe41ijRs4O05iI+OkxHpLEHhIm+wq0A== dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" - "@types/node-fetch" "^2.6.2" - node-fetch "^3.2.10" + "@polkadot/wasm-util" "7.1.1" + tslib "^2.5.0" -"@polkadot/x-global@10.1.10", "@polkadot/x-global@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.10.tgz#eedf63f1f3918c7f2bd8ef0907e051783f1a626e" - integrity sha512-WFfgaZSrzPlKLdnOus2mIFGzUbSDIQK6RMCfFfM9SmF3DkoxN40z5Nkni4PztfKr22stlkhmhnX/Lp/NxpuT6Q== +"@polkadot/wasm-crypto-asmjs@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.1.1.tgz#eb807317b9da0c9ec1b73bb973e0c663d4e10a1c" + integrity sha512-LOKdussHjRx+bwT85iuL6huiKwWZ0x3XLvo0En2U16fT/g9mx74Nmp/YpIwkDRXJ0PUgHpuPYGNMH3x2vEVgBQ== dependencies: - "@babel/runtime" "^7.19.0" + tslib "^2.5.0" -"@polkadot/x-global@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.6.tgz#af6e3c38384d64c69e66607b7a6e794822c65e40" - integrity sha512-/nraYZg0hdSjbczhDBAsHlEqeZLs0u0xa8HJrfH2lq8+HOIYkQpJPHOqiQIvEe/VFRq7Xnbij+4uffV+otzB/w== +"@polkadot/wasm-crypto-init@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.1.1.tgz#8682b2d2fff8bab5e1fe5dc14617d4a63382dd36" + integrity sha512-EgbnXCPNvqfXElhsipI1XqLdVnvk46MCr0cgNE9g1sQHfOh5N9Bsjv7J4bnbv5bIb7JlpXBc9MnTas70PasaCw== dependencies: - "@babel/runtime" "^7.18.9" + "@polkadot/wasm-bridge" "7.1.1" + "@polkadot/wasm-crypto-asmjs" "7.1.1" + "@polkadot/wasm-crypto-wasm" "7.1.1" + "@polkadot/wasm-util" "7.1.1" + tslib "^2.5.0" -"@polkadot/x-global@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.7.tgz#91a472ac2f83fd0858dcd0df528844a5b650790e" - integrity sha512-k2ZUZyBVgDnP/Ysxapa0mthn63j6gsN2V0kZejEQPyOfCHtQQkse3jFvAWdslpWoR8j2k8SN5O6reHc0F4f7mA== +"@polkadot/wasm-crypto-wasm@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.1.1.tgz#16936161bf127d04e315101c843fb3330894a3e6" + integrity sha512-mbo6CEt/11Wh2NDGDhVzQFpOZLE/+Hl8tUf8Y38ddRo20xAHvs09KK+cNTFRurY0P2Tq5LnF9+VzuC0bZPyRqQ== dependencies: - "@babel/runtime" "^7.18.9" + "@polkadot/wasm-util" "7.1.1" + tslib "^2.5.0" -"@polkadot/x-randomvalues@10.1.10": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.10.tgz#a10f98f2c4d744612b68ee697e43f1e4d6e1f89a" - integrity sha512-KM4sCI/DNLIXlmnkeJIuYvh3pPuWvnkbR1a6TUB12J1izUJ+uGV+cAFRR4/EZk3oEsG/Tgivbs56meEOo3ws5A== +"@polkadot/wasm-crypto@^7.0.3": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.1.1.tgz#e70405cde67b3ec661fd74ea3ff1abbbdc0283cc" + integrity sha512-qaYScOVR7gUr41bP7O7OzOVJFfb9gh6KBk9xVy7gl5U9XYAqq0xqp88g528hEUa7IjFW3QVg7EeI8KsRmm0zpA== dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" + "@polkadot/wasm-bridge" "7.1.1" + "@polkadot/wasm-crypto-asmjs" "7.1.1" + "@polkadot/wasm-crypto-init" "7.1.1" + "@polkadot/wasm-crypto-wasm" "7.1.1" + "@polkadot/wasm-util" "7.1.1" + tslib "^2.5.0" -"@polkadot/x-randomvalues@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.7.tgz#d537f1f7bf3fb03e6c08ae6e6ac36e069c1f9844" - integrity sha512-3er4UYOlozLGgFYWwcbmcFslmO8m82u4cAGR4AaEag0VdV7jLO/M5lTmivT/3rtLSww6sjkEfr522GM2Q5lmFg== +"@polkadot/wasm-util@7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.1.1.tgz#f1c50eefef0d139394e51eb107ffb0411bac3d5d" + integrity sha512-FOflVMo4sXTsXhXoY9rQKfPPCiTfE+dEsuGcm6Aecau0NL24Ze/guxX6vOSDN2E2I6kf60biQU016O2RKHqtwg== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.7" + tslib "^2.5.0" -"@polkadot/x-textdecoder@10.1.10": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.10.tgz#a6d0010b092bdefc69c70dcb34d76ec8993980b2" - integrity sha512-cAk37faYXx8IICeaq/tdl+aiIXwo3SLrx9XNoQqhX02g+SEs3ARM7zJcohj/p8ynWAI+ezNcsKn1wh174nquHw== +"@polkadot/x-bigint@11.1.3", "@polkadot/x-bigint@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-11.1.3.tgz#37b09a12a9ed6df704e047e261f1b8b2ac978497" + integrity sha512-fRUUHfW9VFsXT7sLUUY7gSu8v+PvzNLRwvjnp+Ly8vFx9LTLuVGFCi+mpysuRTaPpqZZJlzBJ3fST7xTGh67Pg== dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" + "@polkadot/x-global" "11.1.3" + tslib "^2.5.0" -"@polkadot/x-textdecoder@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.6.tgz#3eaceb8f9b9e6872232b63e9c7a0ac84bdcc7eba" - integrity sha512-T+jvyv6OvUgGfOlMDLkPKEmQnZGP1CNohdEDeRr93AmeYikIfbC20qYuAc0bEBXR7/rPXgnqiEfrpkL2W3r/Ig== +"@polkadot/x-fetch@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-11.1.3.tgz#e39df53fc7fb6399d3883b45d03f6ef7f265a7f9" + integrity sha512-+Z0RxxsN7+l2ZmmDdHqOo0kgqvjXJ1bw8CwTVnq3t9nPgZKn2pC3Fq3xdj/sRWiLuf/UhgCxKfYfMmt5ek4kIg== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" + "@polkadot/x-global" "11.1.3" + node-fetch "^3.3.1" + tslib "^2.5.0" -"@polkadot/x-textdecoder@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.7.tgz#1dd4e6141b1669acdd321a4da1fc6fdc271b7908" - integrity sha512-iAFOHludmZFOyVL8sQFv4TDqbcqQU5gwwYv74duTA+WQBgbSITJrBahSCV/rXOjUqds9pzQO3qBFzziznNnkiQ== +"@polkadot/x-global@11.1.3", "@polkadot/x-global@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-11.1.3.tgz#4086694f52373fea63910b62da999bf0981d7d86" + integrity sha512-R3aqtIjgzFHJ3TyX6wavhp+59oLbZiqczIHkaas/nJe21+SVARqFmIII6BwS7ty7+8Uu4fHliA9re+ZSUp+rwg== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.7" + tslib "^2.5.0" -"@polkadot/x-textencoder@10.1.10": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.10.tgz#35b2e778b3dbb6816bb37f1848b772c4cd3c43d1" - integrity sha512-Auaql6BL5UHtWakZUQyj4y/BrM0tm4bYG5vXCMQCA1Gg0ky+46DhgpRrAQ9F7NNgWg1A6dA2I9KuAA4BTbNx0w== +"@polkadot/x-randomvalues@11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-11.1.3.tgz#48dde21012aa4eef3bd00d46f545861727fb6618" + integrity sha512-kZjbRgxokMR9UTodZQKs6s3C/Q2YgeizcxpDCghM/VdvQUE8OVBGNzduF7SvBvQyg2Qbg8jMcSxXOY7UgcOWSg== dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" + "@polkadot/x-global" "11.1.3" + tslib "^2.5.0" -"@polkadot/x-textencoder@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.6.tgz#a7d2d9bcd7d9c6bc663d524a278aa4a6679ecf28" - integrity sha512-e+iHdR1P/8xAc54l3gHfqozH6ZfxPkKlVVaz3vOMnzfc8cA3Zw93mLYkGtLDqv+825LkSrWSmb/bDZb6YyEEXg== +"@polkadot/x-textdecoder@11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-11.1.3.tgz#1d1e2aa86e47587393a6acb74a086ab97d62058d" + integrity sha512-NhOjuXVfYRMw9l0VhCtZOtcWefZth58p5KpVOrFyJZd12fTsoMO5/746K7QoAjWRrLQTJ/LHCEKCtWww0LwVPw== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" + "@polkadot/x-global" "11.1.3" + tslib "^2.5.0" -"@polkadot/x-textencoder@10.1.7": - version "10.1.7" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.7.tgz#b208601f33b936c7a059f126dbb6b26a87f45864" - integrity sha512-GzjaWZDbgzZ0IQT60xuZ7cZ0wnlNVYMqpfI9KvBc58X9dPI3TIMwzbXDVzZzpjY1SAqJGs4hJse9HMWZazfhew== +"@polkadot/x-textencoder@11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-11.1.3.tgz#ba7621b636dcfa6ca4ab6176a6a52eef15904a72" + integrity sha512-7DmqjlPN8aQexLUKwoHeadihpUnW8hjpXEru+aEDxjgq9XIxPvb++NeBK+Mra9RzzZRuiT/K5z16HlwKN//ewg== dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.7" + "@polkadot/x-global" "11.1.3" + tslib "^2.5.0" -"@polkadot/x-ws@^10.1.6": - version "10.1.10" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.1.10.tgz#27b55a988a0f60db7f79fed8268802406f522813" - integrity sha512-JxDgfm0ox2XPAtdTeJXYl6qq7LY/KOPi69wRpFMczWaYUsZubO6EiRzgzjuFlHY4/oxfjS/D+YbzcjefTxHz6g== +"@polkadot/x-ws@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-11.1.3.tgz#5a759bcbbbdceeecca53bcc74170e52cd3ca774b" + integrity sha512-omNU2mIVX997HiHm2YxEdJdyCFnv+oTyKWZd0+FdS47rdfhVwD+H9/bS+rtQ9lIqfhODdGmw3fG//gq1KpYJcw== dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.10" - "@types/websocket" "^1.0.5" - websocket "^1.0.34" + "@polkadot/x-global" "11.1.3" + tslib "^2.5.0" + ws "^8.13.0" "@primitivefi/hardhat-dodoc@^0.1.1": version "0.1.3" @@ -3866,72 +3828,62 @@ path-browserify "^1.0.0" url "^0.11.0" -"@rollup/plugin-alias@^3.1.9": - version "3.1.9" - resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz#a5d267548fe48441f34be8323fb64d1d4a1b3fdf" - integrity sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw== +"@rollup/plugin-alias@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.0.0.tgz#70f3d504bd17d8922e35c6b61c08b40a6ec25af2" + integrity sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA== dependencies: - slash "^3.0.0" + slash "^4.0.0" -"@rollup/plugin-commonjs@^22.0.2": - version "22.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz#ee8ca8415cda30d383b4096aad5222435b4b69b6" - integrity sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg== +"@rollup/plugin-commonjs@^24.1.0": + version "24.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz#79e54bd83bb64396761431eee6c44152ef322100" + integrity sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ== dependencies: - "@rollup/pluginutils" "^3.1.0" + "@rollup/pluginutils" "^5.0.1" commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.27.0" -"@rollup/plugin-dynamic-import-vars@^1.4.4": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@rollup/plugin-dynamic-import-vars/-/plugin-dynamic-import-vars-1.4.4.tgz#6c156f7437f4e3033d53c96ba970ab476cbf367f" - integrity sha512-51BcU6ag9EeF09CtEsa5D/IHYo7KI42TR1Jc4doNzV1nHAiH7TvUi5vsLERFMjs9Gzy9K0otbZH/2wb0hpBhRA== +"@rollup/plugin-dynamic-import-vars@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-dynamic-import-vars/-/plugin-dynamic-import-vars-2.0.3.tgz#0f0c090d2569463b390603a2139ab4bbf2b4add6" + integrity sha512-0zQV0TDDewilU+7ZLmwc0u44SkeRxSxMdINBuX5isrQGJ6EdTjVL1TcnOZ9In99byaSGAQnHmSFw+6hm0E/jrw== dependencies: - "@rollup/pluginutils" "^4.1.2" - estree-walker "^2.0.1" - fast-glob "^3.2.7" - magic-string "^0.25.7" + "@rollup/pluginutils" "^5.0.1" + estree-walker "^2.0.2" + fast-glob "^3.2.12" + magic-string "^0.27.0" -"@rollup/plugin-inject@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.4.tgz#fbeee66e9a700782c4f65c8b0edbafe58678fbc2" - integrity sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ== +"@rollup/plugin-inject@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.3.tgz#0783711efd93a9547d52971db73b2fb6140a67b1" + integrity sha512-411QlbL+z2yXpRWFXSmw/teQRMkXcAAC8aYTemc15gwJRpvEVDQwoe+N/HTFD8RFG8+88Bme9DK2V9CVm7hJdA== dependencies: - "@rollup/pluginutils" "^3.1.0" - estree-walker "^2.0.1" - magic-string "^0.25.7" + "@rollup/pluginutils" "^5.0.1" + estree-walker "^2.0.2" + magic-string "^0.27.0" -"@rollup/plugin-json@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" - integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== +"@rollup/plugin-json@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.0.0.tgz#199fea6670fd4dfb1f4932250569b14719db234a" + integrity sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w== dependencies: - "@rollup/pluginutils" "^3.0.8" + "@rollup/pluginutils" "^5.0.1" -"@rollup/plugin-node-resolve@^14.1.0": - version "14.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-14.1.0.tgz#f2fa475405cd7fed6420bf438fe393f988a9bc96" - integrity sha512-5G2niJroNCz/1zqwXtk0t9+twOSDlG00k1Wfd7bkbbXmwg8H8dvgHdIWAun53Ps/rckfvOC7scDBjuGFg5OaWw== +"@rollup/plugin-node-resolve@^15.0.2": + version "15.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.2.tgz#8183a80c2cbf7b471f5ac86b16747997f3b5d185" + integrity sha512-Y35fRGUjC3FaurG722uhUuG8YHOJRJQbI6/CkbRkdPotSpDj9NtIN85z1zrcyDcCQIW4qp5mgG72U+gJ0TAFEg== dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" deepmerge "^4.2.2" - is-builtin-module "^3.1.0" + is-builtin-module "^3.2.1" is-module "^1.0.0" - resolve "^1.19.0" - -"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" + resolve "^1.22.1" "@rollup/pluginutils@^4.1.2": version "4.2.1" @@ -3941,71 +3893,70 @@ estree-walker "^2.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" + integrity sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + "@rushstack/eslint-patch@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== -"@scure/base@1.1.1": +"@scure/base@1.1.1", "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== -"@sinclair/typebox@^0.24.1": - version "0.24.44" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.44.tgz#0a0aa3bf4a155a678418527342a3ee84bd8caa5c" - integrity sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg== +"@scure/bip32@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" + integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== + dependencies: + "@noble/curves" "~1.0.0" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@substrate/connect-extension-protocol@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz#fa5738039586c648013caa6a0c95c43265dbe77d" integrity sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg== -"@substrate/connect@0.7.11": - version "0.7.11" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.11.tgz#fa9d24991fe9edc7fb771fbdee21a9b7cf6d1322" - integrity sha512-/xiOlkmJfl2XPYQTmyWKEh2AXryEAPSMAxZXs6D/aqYDy0TKZDAp1dfQiHyPt1vMwOlnM4WJv9lPks3ZMwCP+w== +"@substrate/connect@0.7.23": + version "0.7.23" + resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.23.tgz#67c60f6498b5e61a654f5f64cebb7495bbafeaad" + integrity sha512-zlfI76HdUJszQWG7Dpwd0g7jjQv6LNZtE6Kd7OWv4OdpHJa1VpL2jGjg7YdLhwtx7qxqOuRiak1H+5KrsavzRQ== dependencies: "@substrate/connect-extension-protocol" "^1.0.1" - "@substrate/smoldot-light" "0.6.30" eventemitter3 "^4.0.7" + smoldot "1.0.1" -"@substrate/smoldot-light@0.6.30": - version "0.6.30" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.30.tgz#a49f4a77f3047bfc1fb9224725a6286e2b709bf1" - integrity sha512-U/F75XzxuNG+KGSujxsMAm8zUBpBON+l0oX19EnSWjvqD+smYjvcj1SeqQhFYxJjtoCQyZLedKBsZGyNbG3FbQ== - dependencies: - pako "^2.0.4" - ws "^8.8.1" - -"@substrate/ss58-registry@^1.28.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.28.0.tgz#39b7fa355d9b97bcb30ef1eedb47b10c3fddcf03" - integrity sha512-XPSwSq4CThLyg+OnZ5/LHh3SPDQjRdGS3Ux5ClgWhRCQamlU86FCT1LBwQ/i+ximbdBfqKRRzVhm1ql3AJ9FKQ== - -"@substrate/ss58-registry@^1.31.0": - version "1.31.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.31.0.tgz#489e3a496081dc3ee7ef5ef2eb513962d5d29351" - integrity sha512-OSOmdjaq9foXfHBy9aLVMwGheygvsiZlv4dggnLOYOuhSmNCsSB/PaW4DBz+/tSdK1Fo9+ZiFW6cF24RA+m0sw== +"@substrate/ss58-registry@^1.39.0": + version "1.39.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.39.0.tgz#eb916ff5fea7fa02e77745823fde21af979273d2" + integrity sha512-qZYpuE6n+mwew+X71dOur/CbMXj6rNW27o63JeJwdQH/GvcSKm3JLNhd+bGzwUKg0D/zD30Qc6p4JykArzM+tA== "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -4146,6 +4097,11 @@ strip-ansi "^4.0.0" strip-indent "^2.0.0" +"@tsconfig/esm@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/esm/-/esm-1.0.3.tgz#a32d35b93eeb5851881094444a1aca9acd064df7" + integrity sha512-Gp56rIc3R8ab032nXMUitmc7YIb4nAi8DQ6Qt47tuL0Ssn9LIOm+o2FQmqPu3jX4z0TsqgzWwkmVygxcq+yHYg== + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -4166,6 +4122,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== +"@tsconfig/strictest@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/strictest/-/strictest-2.0.1.tgz#c8f42be4d2c8a8fcef0afd482b730c1d55733776" + integrity sha512-7JHHCbyCsGUxLd0pDbp24yz3zjxw2t673W5oAP6HCEdr/UUhaRhYd3SSnUsGCk+VnPVJVA4mXROzbhI+nyIk+w== + "@typechain/ethers-v5@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz#cd3ca1590240d587ca301f4c029b67bfccd08810" @@ -4312,15 +4273,10 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - -"@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== "@types/expect@^24.3.0": version "24.3.0" @@ -4369,7 +4325,7 @@ dependencies: ci-info "^3.1.0" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== @@ -4388,15 +4344,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jsdom@^20.0.0": - version "20.0.0" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" - integrity sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - parse5 "^7.0.0" - "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -4434,7 +4381,7 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== -"@types/node-fetch@^2.5.5", "@types/node-fetch@^2.6.2": +"@types/node-fetch@^2.5.5": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== @@ -4472,7 +4419,7 @@ dependencies: "@types/node" "*" -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== @@ -4487,12 +4434,10 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/resolve@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" - integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== - dependencies: - "@types/node" "*" +"@types/resolve@1.20.2": + version "1.20.2" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" + integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== "@types/resolve@^0.0.8": version "0.0.8" @@ -4523,6 +4468,11 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -4577,11 +4527,6 @@ dependencies: "@types/node" "*" -"@types/tough-cookie@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== - "@types/underscore@*": version "1.11.4" resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.11.4.tgz#62e393f8bc4bd8a06154d110c7d042a93751def3" @@ -4595,13 +4540,6 @@ "@types/bn.js" "*" "@types/underscore" "*" -"@types/websocket@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c" - integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ== - dependencies: - "@types/node" "*" - "@types/ws@^8.5.1": version "8.5.3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" @@ -4628,20 +4566,6 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz#778b2d9e7f293502c7feeea6c74dca8eb3e67511" - integrity sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A== - dependencies: - "@typescript-eslint/scope-manager" "5.39.0" - "@typescript-eslint/type-utils" "5.39.0" - "@typescript-eslint/utils" "5.39.0" - debug "^4.3.4" - ignore "^5.2.0" - regexpp "^3.2.0" - semver "^7.3.7" - tsutils "^3.21.0" - "@typescript-eslint/eslint-plugin@^5.13.0": version "5.16.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" @@ -4657,15 +4581,21 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/parser@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.39.0.tgz#93fa0bc980a3a501e081824f6097f7ca30aaa22b" - integrity sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA== +"@typescript-eslint/eslint-plugin@^5.59.0": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== dependencies: - "@typescript-eslint/scope-manager" "5.39.0" - "@typescript-eslint/types" "5.39.0" - "@typescript-eslint/typescript-estree" "5.39.0" + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" "@typescript-eslint/parser@^5.13.0": version "5.16.0" @@ -4677,6 +4607,16 @@ "@typescript-eslint/typescript-estree" "5.16.0" debug "^4.3.2" +"@typescript-eslint/parser@^5.59.0": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== + dependencies: + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.16.0": version "5.16.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" @@ -4685,13 +4625,13 @@ "@typescript-eslint/types" "5.16.0" "@typescript-eslint/visitor-keys" "5.16.0" -"@typescript-eslint/scope-manager@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz#873e1465afa3d6c78d8ed2da68aed266a08008d0" - integrity sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw== +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== dependencies: - "@typescript-eslint/types" "5.39.0" - "@typescript-eslint/visitor-keys" "5.39.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" "@typescript-eslint/type-utils@5.16.0": version "5.16.0" @@ -4702,13 +4642,13 @@ debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/type-utils@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz#0a8c00f95dce4335832ad2dc6bc431c14e32a0a6" - integrity sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA== +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== dependencies: - "@typescript-eslint/typescript-estree" "5.39.0" - "@typescript-eslint/utils" "5.39.0" + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" tsutils "^3.21.0" @@ -4722,10 +4662,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== -"@typescript-eslint/types@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.39.0.tgz#f4e9f207ebb4579fd854b25c0bf64433bb5ed78d" - integrity sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw== +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== "@typescript-eslint/typescript-estree@5.16.0": version "5.16.0" @@ -4740,13 +4680,13 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz#c0316aa04a1a1f4f7f9498e3c13ef1d3dc4cf88b" - integrity sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA== +"@typescript-eslint/typescript-estree@5.59.1", "@typescript-eslint/typescript-estree@^5.55.0": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== dependencies: - "@typescript-eslint/types" "5.39.0" - "@typescript-eslint/visitor-keys" "5.39.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -4778,17 +4718,19 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/utils@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.39.0.tgz#b7063cca1dcf08d1d21b0d91db491161ad0be110" - integrity sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg== +"@typescript-eslint/utils@5.59.1", "@typescript-eslint/utils@^5.57.0": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.39.0" - "@typescript-eslint/types" "5.39.0" - "@typescript-eslint/typescript-estree" "5.39.0" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + semver "^7.3.7" "@typescript-eslint/visitor-keys@4.33.0": version "4.33.0" @@ -4806,236 +4748,184 @@ "@typescript-eslint/types" "5.16.0" eslint-visitor-keys "^3.0.0" -"@typescript-eslint/visitor-keys@5.39.0": - version "5.39.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz#8f41f7d241b47257b081ddba5d3ce80deaae61e2" - integrity sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg== +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== dependencies: - "@typescript-eslint/types" "5.39.0" + "@typescript-eslint/types" "5.59.1" eslint-visitor-keys "^3.3.0" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@webassemblyjs/ast@1.11.5", "@webassemblyjs/ast@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.5.tgz#6e818036b94548c1fb53b754b5cae3c9b208281c" + integrity sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" -"@vue/component-compiler-utils@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9" - integrity sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ== - dependencies: - consolidate "^0.15.1" - hash-sum "^1.0.2" - lru-cache "^4.1.2" - merge-source-map "^1.1.0" - postcss "^7.0.36" - postcss-selector-parser "^6.0.2" - source-map "~0.6.1" - vue-template-es2015-compiler "^1.9.0" - optionalDependencies: - prettier "^1.18.2 || ^2.0.0" - -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== - -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== - -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== - -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" +"@webassemblyjs/floating-point-hex-parser@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz#e85dfdb01cad16b812ff166b96806c050555f1b4" + integrity sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ== + +"@webassemblyjs/helper-api-error@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz#1e82fa7958c681ddcf4eabef756ce09d49d442d1" + integrity sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA== + +"@webassemblyjs/helper-buffer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz#91381652ea95bb38bbfd270702351c0c89d69fba" + integrity sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg== + +"@webassemblyjs/helper-numbers@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz#23380c910d56764957292839006fecbe05e135a9" + integrity sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz#e258a25251bc69a52ef817da3001863cc1c24b9f" + integrity sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz#966e855a6fae04d5570ad4ec87fbcf29b42ba78e" + integrity sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz#b2db1b33ce9c91e34236194c2b5cba9b25ca9d60" + integrity sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.5.tgz#482e44d26b6b949edf042a8525a66c649e38935a" + integrity sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.5.tgz#83bef94856e399f3740e8df9f63bc47a987eae1a" + integrity sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz#93ee10a08037657e21c70de31c47fdad6b522b2d" + integrity sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/helper-wasm-section" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-opt" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + "@webassemblyjs/wast-printer" "1.11.5" + +"@webassemblyjs/wasm-gen@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz#ceb1c82b40bf0cf67a492c53381916756ef7f0b1" + integrity sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wasm-opt@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz#b52bac29681fa62487e16d3bb7f0633d5e62ca0a" + integrity sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + +"@webassemblyjs/wasm-parser@1.11.5", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz#7ba0697ca74c860ea13e3ba226b29617046982e2" + integrity sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wast-printer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz#7a5e9689043f3eca82d544d7be7a8e6373a6fa98" + integrity sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA== + dependencies: + "@webassemblyjs/ast" "1.11.5" "@xtuc/long" "4.2.2" -"@webb-tools/anchors@0.5.19", "@webb-tools/anchors@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/anchors/-/anchors-0.5.19.tgz#7df4b89c3d23e0eaedaa625a2a19973463d99404" - integrity sha512-RQoEOgMejtluRcKD+C6Av5ga3ij35siL8uII5t+VPlhpi18CDwBkdS/IRqm1LTf+Am26UyCODArziknMnJOWpA== +"@webb-tools/anchors@0.5.38", "@webb-tools/anchors@^0.5.13", "@webb-tools/anchors@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/anchors/-/anchors-0.5.38.tgz#aad77453e2621b79ab359bb34a19b12ebf48d368" + integrity sha512-I7c0irJNwsMlhASoUmW0Y+onQx4Pm1bC6wnS63ufElIMe1CN2vDUCoBZpHqufYGHw6d1CLytNqIGPHB+WT23OA== dependencies: - "@webb-tools/contracts" "^0.5.19" - "@webb-tools/interfaces" "^0.5.19" - "@webb-tools/sdk-core" "0.1.4-119" + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/create2-utils" "^0.5.5" + "@webb-tools/interfaces" "^0.5.38" + "@webb-tools/sdk-core" "0.1.4-123" "@webb-tools/semaphore" "0.0.1-5" - "@webb-tools/utils" "^0.5.11" + "@webb-tools/tokens" "0.5.13" + "@webb-tools/utils" "^0.5.30" ethers "5.7.0" jssha "^3.2.0" -"@webb-tools/anchors@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/anchors/-/anchors-0.4.2.tgz#3c7fafeb76a9ae622e73c5c520c51466080e9a21" - integrity sha512-BlNPNE+/gp/uf7zhhpAlGZTJM7dGTrCZsRyiBSqpUym8ZuG2DwVdvm3+/9TcqUNGycWXFwsAm0OkRLdrUvCmLA== - dependencies: - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/interfaces" "^0.4.2" - "@webb-tools/sdk-core" "0.1.4-113" - "@webb-tools/semaphore" "0.0.1-1" - "@webb-tools/utils" "^0.4.2" - ethers "5.7.0" - -"@webb-tools/api-derive@0.1.4-117": - version "0.1.4-117" - resolved "https://registry.yarnpkg.com/@webb-tools/api-derive/-/api-derive-0.1.4-117.tgz#31e421aee29d9af667889daf7cc58c09437fed89" - integrity sha512-uEu5lb7ntctxj76sHISJLBGcdskpvNnEfW/uddHoMfgT6q9UpGbNyEOjgl43He31GEPflBoAy8+pglAB6JvMag== - dependencies: - "@polkadot/api" "9.2.4" - "@polkadot/rpc-core" "9.2.4" - "@webb-tools/protocol-substrate-types" "^0.0.2" - -"@webb-tools/api@0.1.4-117": - version "0.1.4-117" - resolved "https://registry.yarnpkg.com/@webb-tools/api/-/api-0.1.4-117.tgz#3b2a42132d742d3adba83a240670434c07358e35" - integrity sha512-HAJkHRu+pv7F7JoQRmbZFTuSwlSXEZqvq1rtbAbJnfRG9yW3ch6gD2LlEMoHv94epKmYo6MleUzZDZeFQFZQHA== - dependencies: - "@polkadot/api" "9.2.4" - "@webb-tools/api-derive" "0.1.4-117" - "@webb-tools/protocol-substrate-types" "0.0.3" - -"@webb-tools/bridges@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/bridges/-/bridges-0.4.2.tgz#734191e76917269ae0a569b956b8c962a51f9c12" - integrity sha512-CirSyK9s03a2EQyzlfcYi7xPMNwiR8OOhqPzfl3Vb6Ou39sh+7MdieuOMKsLG7VTXWiNPowKpQ3QRSZlhd0I2Q== - dependencies: - "@webb-tools/anchors" "^0.4.2" - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/interfaces" "^0.4.2" - "@webb-tools/sdk-core" "0.1.4-113" - "@webb-tools/tokens" "^0.4.2" - "@webb-tools/utils" "^0.4.2" - ethers "5.7.0" - -"@webb-tools/bridges@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/bridges/-/bridges-0.5.19.tgz#c3ab7c167dafc68c5351005537911a43e8894500" - integrity sha512-4i777EGWF4iXlPe6+WSl7PIaXYgliCNynDltDXab3qe5G4qA/IvldXMiAlAWEwbpDtuLmm0HlXwg1P8ZHbF/xg== - dependencies: - "@webb-tools/anchors" "^0.5.19" - "@webb-tools/contracts" "^0.5.19" - "@webb-tools/interfaces" "^0.5.19" - "@webb-tools/sdk-core" "0.1.4-119" - "@webb-tools/tokens" "^0.5.19" - "@webb-tools/utils" "^0.5.11" - ethers "5.7.0" - -"@webb-tools/contracts@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/contracts/-/contracts-0.4.2.tgz#09478a26115516a1f29daaf438b038506a518482" - integrity sha512-PWRpGFi2niBLBLYL+K+Bi4yPfoLUnxOby4gj/mwj64io/E6EWd6gQeCzRmKTO0Wgn7keu4/8FwECun7OjRoSPQ== - dependencies: +"@webb-tools/api-derive@0.1.4-125": + version "0.1.4-125" + resolved "https://registry.yarnpkg.com/@webb-tools/api-derive/-/api-derive-0.1.4-125.tgz#340bd597247f704ce7f53c57942d40c259fa0963" + integrity sha512-wguqcdczDD69M0z3K1O8sW6Qb9K+gV0iVy28EGwCF29h1gcEeqx8F7UbXal32o77UOnrsYuVqXj4u7LfqOKU2A== + dependencies: + "@polkadot/api" "10.3.2" + "@webb-tools/protocol-substrate-types" "0.0.10" + +"@webb-tools/api@0.1.4-125": + version "0.1.4-125" + resolved "https://registry.yarnpkg.com/@webb-tools/api/-/api-0.1.4-125.tgz#4a90d7adfd4d9f3d9459b05f9122fa424e0c93d9" + integrity sha512-anKD8zmTEeQrfVrlD6L+NpipFSPBnPpo/TGlv9CXrvsQFOokkRrVIbU1NCYX1hMd2mth3rvIEQ0olV0x/GX9cw== + dependencies: + "@polkadot/api" "10.3.2" + "@webb-tools/api-derive" "0.1.4-125" + "@webb-tools/protocol-substrate-types" "0.0.10" + +"@webb-tools/bridges@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/bridges/-/bridges-0.5.38.tgz#ba0ee04d13a8cc1f61819d9d1e8b34ee647717e4" + integrity sha512-7gQEFg6+/ZvSAbyyex4E8EB33h6OnqEpIJgOTb8cLj0xBQ0J3GvmyMTKjakOD0Ro82O3UgoAKA8VtWS7NxLh4A== + dependencies: + "@webb-tools/anchors" "^0.5.38" + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/interfaces" "^0.5.38" + "@webb-tools/sdk-core" "0.1.4-123" + "@webb-tools/tokens" "^0.5.38" + "@webb-tools/utils" "^0.5.30" ethers "5.7.0" -"@webb-tools/contracts@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/contracts/-/contracts-0.5.19.tgz#96438c13a889856388226000539d254662ba5290" - integrity sha512-/W0K+VpjluanUqVJ9HlvoHTKk4TWwQtP993ZyIbQ/CIDN+6wsQ5ZKlmMHZUchud33jzw6f71nZXbe87lT3engw== +"@webb-tools/contracts@^0.5.13", "@webb-tools/contracts@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/contracts/-/contracts-0.5.38.tgz#04504e4d1189d2663427798826728837c13ec135" + integrity sha512-FeC8KWUpV4aUV8oZ9SpaKcSj9sEekT+ZU9U/vsjLD8CP1YH6Hf69bfCmW7CxOHTqFSiVfyfOTZmaaOHmHUw4oQ== dependencies: "@nomiclabs/hardhat-waffle" "^2.0.3" "@openzeppelin/contracts" "^4.7.3" @@ -5045,12 +4935,12 @@ "@typechain/hardhat" "^2.3.0" "@types/chai" "^4.3.0" "@types/mocha" "^9.0.0" - "@webb-tools/sdk-core" "0.1.4-119" + "@webb-tools/sdk-core" "0.1.4-123" "@webb-tools/semaphore" "0.0.1-5" "@webb-tools/semaphore-group" "0.0.1-4" "@webb-tools/semaphore-identity" "0.0.1-3" "@webb-tools/semaphore-proof" "0.0.1-5" - "@webb-tools/test-utils" "0.1.4-119" + "@webb-tools/test-utils" "0.1.4-123" babel-plugin-styled-components "^2.0.7" bn.js "4.11.6" chai "^4.3.5" @@ -5067,125 +4957,137 @@ typechain "^5.1.2" typescript "4.7.2" -"@webb-tools/dkg-substrate-types@0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@webb-tools/dkg-substrate-types/-/dkg-substrate-types-0.0.1.tgz#a374cea87002b26bb567c5ab3df14f878c9647c8" - integrity sha512-B7vk+1PRF7BdT9URJF+U1+JsBPmhvMkzldVyEE+tQaKYsyyaZPmb+Bye7DkMZCSDqJKgyfIBaN3zcKxKqqjTAA== +"@webb-tools/create2-utils@^0.5.5": + version "0.5.5" + resolved "https://registry.yarnpkg.com/@webb-tools/create2-utils/-/create2-utils-0.5.5.tgz#186fb3d7aba564a9942f6505df6a7a63dba8dd0d" + integrity sha512-K9lAxk6UdzyMXw07PI4/Mf7SSef9TP1dzsGRfbjPtbpC+zMOfHg1sGcXXu1LMZ/sFD/yvnswPJn3GHXVs/2Cqw== dependencies: - "@babel/runtime" "^7.19.0" - "@open-web3/orml-types" "^1.1.3" - "@polkadot/api-derive" "9.2.4" - "@polkadot/dev" "^0.67.86" - "@polkadot/typegen" "9.2.4" - "@polkadot/types" "9.2.4" + "@webb-tools/contracts" "^0.5.38" + ethers "5.7.0" -"@webb-tools/evm-test-utils@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/evm-test-utils/-/evm-test-utils-0.5.19.tgz#01d9e50b8361848522b1649e1cba059452fefef1" - integrity sha512-9VfspAIL3fWrljjd50iBbKkcdU+rzffUzje7upjI53AEWUoyBs6a1aSsUG5Lu+KYkfPdyZNWbQQxaNSHkclXCg== +"@webb-tools/dkg-substrate-types@0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@webb-tools/dkg-substrate-types/-/dkg-substrate-types-0.0.5.tgz#d6178d5d9feca39a1f0b56f42295bcd88d343088" + integrity sha512-WMVqLZ7OPkpoel7jSqBXFdijl15+D3tSs+OG98a5jf5DrvgwEFaC3QrWmD7OWfdfaRR3gpoy6WmEesBpozprsQ== dependencies: - "@webb-tools/interfaces" "^0.5.19" - "@webb-tools/sdk-core" "0.1.4-119" - "@webb-tools/utils" "^0.5.11" - "@webb-tools/vbridge" "^0.5.19" + "@babel/cli" "^7.20.7" + "@babel/core" "^7.20.12" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-runtime" "^7.19.6" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/register" "^7.18.9" + "@babel/runtime" "^7.20.13" + "@open-web3/orml-types" "^1.1.3" + "@polkadot/api-derive" "10.3.2" + "@polkadot/dev" "^0.72.42" + "@polkadot/typegen" "10.3.2" + "@polkadot/types" "10.3.2" + babel-jest "^29.4.1" + babel-plugin-module-extension-resolver "^1.0.0" + babel-plugin-module-resolver "^5.0.0" + babel-plugin-styled-components "^2.0.7" + fs-extra "^11.1.1" + glob2base "^0.0.12" + minimatch "^7.4.2" + mkdirp "^2.1.5" + +"@webb-tools/evm-test-utils@0.5.38", "@webb-tools/evm-test-utils@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/evm-test-utils/-/evm-test-utils-0.5.38.tgz#a881bd8ce18e6634927f5d3b0772e2e1267bd248" + integrity sha512-nBZkPVwXpAHfsmRu9rX8qe2rylE/3AQCtrmEjvIdATVRPpsRUz+NUsfsF23vaPJfXBe6YPZ3mU5ullfezVdq1g== + dependencies: + "@webb-tools/interfaces" "^0.5.38" + "@webb-tools/sdk-core" "0.1.4-123" + "@webb-tools/utils" "^0.5.30" + "@webb-tools/vbridge" "^0.5.38" ecpair "^2.0.1" ethers "5.7.0" ganache "7.5.0" tiny-secp256k1 "^2.2.1" -"@webb-tools/interfaces@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/interfaces/-/interfaces-0.4.2.tgz#fd18146e5284225730cff2272059a6a0b01734a4" - integrity sha512-HD3W+cgLXBr0jO6F/Llu82w0y7jMyi4WOQC/8Co0OmUNGbZH+YAVVi0InBj1kPlXwkTSCxbay2ialZV6ees/cA== - dependencies: - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/sdk-core" "0.1.4-113" - -"@webb-tools/interfaces@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/interfaces/-/interfaces-0.5.19.tgz#14f270f193fab38c73e01ec1f33a3b2621ded66f" - integrity sha512-WwewPGOj7v/BGj6HQwpVya/imkmTtny0kORkxzUEKhzb5DIbKmqasnxm4JaBdXROb2JVlRdltY2ZVdj0NW7r0g== - dependencies: - "@webb-tools/contracts" "^0.5.19" - "@webb-tools/sdk-core" "0.1.4-119" - -"@webb-tools/protocol-solidity@0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/protocol-solidity/-/protocol-solidity-0.4.2.tgz#32ed1ccdc226880d372ab7760301f5a1b0dbfdbc" - integrity sha512-mjaJMDfoR3v7GdtrzSXQXkabHe+ecWw/0/LwM4DtxcNcx1iYJL0gtCFI4EBPeGirhBXCd1oYWnkLBE4etCCInw== - dependencies: - "@webb-tools/anchors" "^0.4.2" - "@webb-tools/bridges" "^0.4.2" - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/interfaces" "^0.4.2" - "@webb-tools/tokens" "^0.4.2" - "@webb-tools/utils" "^0.4.2" - "@webb-tools/vbridge" "^0.4.2" - -"@webb-tools/protocol-solidity@0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/protocol-solidity/-/protocol-solidity-0.5.19.tgz#dcc9264eebe2f45070f838d169f0111df3a9bf87" - integrity sha512-BVQBX3ziDk7ZK06W7IraSncvAmaQ+7AP57QSI6v91Ms5A7CVF31zIjUKDstgtZvIA4DjNehLFpx59qvs+IuV1Q== - dependencies: - "@webb-tools/anchors" "^0.5.19" - "@webb-tools/bridges" "^0.5.19" - "@webb-tools/contracts" "^0.5.19" - "@webb-tools/evm-test-utils" "^0.5.19" - "@webb-tools/interfaces" "^0.5.19" - "@webb-tools/tokens" "^0.5.19" - "@webb-tools/utils" "^0.5.11" - "@webb-tools/vbridge" "^0.5.19" - -"@webb-tools/protocol-substrate-types@0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@webb-tools/protocol-substrate-types/-/protocol-substrate-types-0.0.3.tgz#38b35fe6bc13dc9c7219b5ffab78894fc7b500f8" - integrity sha512-DKeyu94Yetsk3weJOiPa2Rwtlr47f/+zVg+v5eld0ET/KtosH1hKrRIIXJdJ1sAMet2SqRC0jxxDRh1OhDcZRw== - dependencies: - "@babel/runtime" "^7.19.0" - "@open-web3/orml-types" "^1.1.3" - "@polkadot/api-derive" "9.2.4" - "@polkadot/dev" "^0.67.86" - "@polkadot/typegen" "9.2.4" - "@polkadot/types" "9.2.4" - -"@webb-tools/protocol-substrate-types@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@webb-tools/protocol-substrate-types/-/protocol-substrate-types-0.0.2.tgz#27953ee61a248430cee3fc2bbfc6b2260ecc8b16" - integrity sha512-tFJWGJQz9czS0Zkdn6C2fPRMUCa6eWlzIAEjshOBowharqodfSKwvdZCaXJCrs9VCUxGDFMj/7HKk9oe4lh/5A== - dependencies: - "@babel/runtime" "^7.19.0" +"@webb-tools/interfaces@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/interfaces/-/interfaces-0.5.38.tgz#b84dde2d47523392764dba4031971e81778d3aba" + integrity sha512-mavaLiZEM0x/UFbDrnvUq6xOlJmwuo5HEuxYtGGV52d4NoS+ytLfL9rOKG4/KjpZ4zf7T9OtUG00upGnCAKOrg== + dependencies: + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/sdk-core" "0.1.4-123" + +"@webb-tools/protocol-solidity@0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/protocol-solidity/-/protocol-solidity-0.5.38.tgz#d336daa2b5ae2e60f3676ba05218e98a7915866a" + integrity sha512-5fF6ELJJjT9woNdBjZWtTHp+9CE/dTBDV7mRYn5i84kIB3zRZMExDqBaGrkiVY1wcjD4ztueTPEhg+Rae3apAw== + dependencies: + "@webb-tools/anchors" "^0.5.38" + "@webb-tools/bridges" "^0.5.38" + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/evm-test-utils" "^0.5.38" + "@webb-tools/interfaces" "^0.5.38" + "@webb-tools/tokens" "^0.5.38" + "@webb-tools/utils" "^0.5.30" + "@webb-tools/vbridge" "^0.5.38" + +"@webb-tools/protocol-substrate-types@0.0.10": + version "0.0.10" + resolved "https://registry.yarnpkg.com/@webb-tools/protocol-substrate-types/-/protocol-substrate-types-0.0.10.tgz#1fb383c97f63bfc7e7f302f97cb747b0d79c4276" + integrity sha512-GNxozh5hnfHAsAYbwlNsH3TfOL/gCRw8jLqj+H4a1dG5D8Ch7XbCNoaJeWlHGHMLQC+Kqb9vqm3Z1fxIuBP16w== + dependencies: + "@babel/cli" "^7.20.7" + "@babel/core" "^7.20.12" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-runtime" "^7.19.6" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/register" "^7.18.9" + "@babel/runtime" "^7.20.13" "@open-web3/orml-types" "^1.1.3" - "@polkadot/api-derive" "9.2.4" - "@polkadot/dev" "^0.67.86" - "@polkadot/typegen" "9.2.4" - "@polkadot/types" "9.2.4" - -"@webb-tools/sdk-core@0.1.4-105", "@webb-tools/sdk-core@0.1.4-106", "@webb-tools/sdk-core@0.1.4-108", "@webb-tools/sdk-core@0.1.4-113", "@webb-tools/sdk-core@0.1.4-117", "@webb-tools/sdk-core@0.1.4-119": - version "0.1.4-117" - resolved "https://registry.yarnpkg.com/@webb-tools/sdk-core/-/sdk-core-0.1.4-117.tgz#69cb6765c7a495497aaaaddb8ef1723e8ceba139" - integrity sha512-+P9sWGXcUDoIE5VQ8gEcGYkrrm8G6+ZjDSNfJXi30ds1i2YVhtE79v+O5OPcRox0NRT+hxVO5gg8BAsdyRJ+SQ== - dependencies: - "@polkadot/util" "10.1.7" - "@webb-tools/wasm-utils" "0.1.4-117" + "@polkadot/api-derive" "10.3.2" + "@polkadot/dev" "^0.72.42" + "@polkadot/typegen" "10.3.2" + "@polkadot/types" "10.3.2" + babel-jest "^29.4.1" + babel-plugin-module-extension-resolver "^1.0.0" + babel-plugin-module-resolver "^5.0.0" + babel-plugin-styled-components "^2.0.7" + fs-extra "^11.1.1" + glob2base "^0.0.12" + minimatch "^7.4.2" + mkdirp "^2.1.5" + +"@webb-tools/sdk-core@0.1.4-105", "@webb-tools/sdk-core@0.1.4-108", "@webb-tools/sdk-core@0.1.4-119", "@webb-tools/sdk-core@0.1.4-123", "@webb-tools/sdk-core@0.1.4-125": + version "0.1.4-125" + resolved "https://registry.yarnpkg.com/@webb-tools/sdk-core/-/sdk-core-0.1.4-125.tgz#709832397ea29dd6fbd87bf591c754524cfff2b2" + integrity sha512-Z5hC8M7Vj1UZGvwSr/vg+EjmWMOpHnvMAO0XzmcW1aw+V9/9QuCnZNrS4e+86KPkp08q6IvOkplkfO7xCUJPHQ== + dependencies: + "@metamask/eth-sig-util" "^5.0.2" + "@polkadot/util" "11.1.3" + "@webb-tools/wasm-utils" "0.1.4-125" + big-integer "^1.6.51" bignumber.js "^9.0.0" - circomlibjs "0.0.8" + circomlibjs "^0.0.8" elliptic "6.5.4" - eth-sig-util "^3.0.1" ethers "5.7.0" - ffjavascript "0.2.55" - snarkjs "0.4.22" - -"@webb-tools/semaphore-group@0.0.1-1": - version "0.0.1-1" - resolved "https://registry.yarnpkg.com/@webb-tools/semaphore-group/-/semaphore-group-0.0.1-1.tgz#969475fa015287d95505310a4cae4b1f769dd016" - integrity sha512-9z6r8tjWYezqXCZfdM7yU51UgWNoYRdgHbLk7dLFLRFSNQMfzbOkTxc98t3s7X763N/I2OcxtxwA5j5fEhRaag== - dependencies: - "@webb-tools/sdk-core" "0.1.4-106" - "@webb-tools/test-utils" "0.1.4-106" - "@zk-kit/incremental-merkle-tree" "0.4.3" - circomlibjs "0.0.8" - ethers "^5.6.8" - rollup "^2.77.2" + ffjavascript "^0.2.57" + snarkjs "^0.6.10" "@webb-tools/semaphore-group@0.0.1-4": version "0.0.1-4" @@ -5198,19 +5100,6 @@ ethers "^5.6.8" rollup "^2.77.2" -"@webb-tools/semaphore-identity@0.0.1-1": - version "0.0.1-1" - resolved "https://registry.yarnpkg.com/@webb-tools/semaphore-identity/-/semaphore-identity-0.0.1-1.tgz#af145fb159119fa27b358a74ecf5652f265a552c" - integrity sha512-gQ2etvkUsnhRwXUujyYrF8vXtmfHl+HFpmvJcdkRc+PxO8P5zEDUxXoV5xwhouFVfpVR4GAR/ikX3hO2qLR3lA== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/random" "^5.5.1" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - circomlibjs "0.0.8" - rollup "^2.77.2" - typescript "^4.5.5" - "@webb-tools/semaphore-identity@0.0.1-3": version "0.0.1-3" resolved "https://registry.yarnpkg.com/@webb-tools/semaphore-identity/-/semaphore-identity-0.0.1-3.tgz#b5d9e9b0de98fe69f816d5826c3d3a85f3a4b917" @@ -5224,24 +5113,6 @@ rollup "^2.77.2" typescript "^4.5.5" -"@webb-tools/semaphore-proof@0.0.1-1": - version "0.0.1-1" - resolved "https://registry.yarnpkg.com/@webb-tools/semaphore-proof/-/semaphore-proof-0.0.1-1.tgz#ca2ab3978f6834ac816976d0888939ecd7d41217" - integrity sha512-6ErpY9lq+GhBkKazfmE8isYLj05KtbcydiLwQKTuPGB3L00uqARmou/doIxxbQLhvP/Aq09WLqmunYH9NqEOqQ== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/solidity" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@webb-tools/sdk-core" "0.1.4-106" - "@webb-tools/semaphore-group" "0.0.1-1" - "@webb-tools/semaphore-identity" "0.0.1-1" - "@webb-tools/utils" "^0.2.7" - circomlibjs "0.0.8" - ethers "^5.6.8" - ffjavascript "^0.2.54" - snarkjs "^0.4.13" - typescript "^4.7.4" - "@webb-tools/semaphore-proof@0.0.1-5": version "0.0.1-5" resolved "https://registry.yarnpkg.com/@webb-tools/semaphore-proof/-/semaphore-proof-0.0.1-5.tgz#415e426a3ad150a6f14ed592e20fdf9578da27f2" @@ -5263,25 +5134,6 @@ typedoc "^0.23.15" typescript "^4.7.4" -"@webb-tools/semaphore@0.0.1-1": - version "0.0.1-1" - resolved "https://registry.yarnpkg.com/@webb-tools/semaphore/-/semaphore-0.0.1-1.tgz#132783c90fc0976f1e1107c0e6dec3becae4f4bd" - integrity sha512-pQbIFhwFXy90DUNEor7b+5CUR94Zeob8kMcesf+Y950co/o1NKCyt1eXB8MB8eyT/EdirJsmB7vRTwnRM2byeA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/solidity" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@webb-tools/sdk-core" "0.1.4-106" - "@webb-tools/semaphore-group" "0.0.1-1" - "@webb-tools/semaphore-identity" "0.0.1-1" - "@webb-tools/semaphore-proof" "0.0.1-1" - "@webb-tools/utils" "^0.2.7" - circomlibjs "0.0.8" - ethers "^5.6.8" - ffjavascript "^0.2.54" - snarkjs "^0.4.13" - typescript "^4.5.5" - "@webb-tools/semaphore@0.0.1-5": version "0.0.1-5" resolved "https://registry.yarnpkg.com/@webb-tools/semaphore/-/semaphore-0.0.1-5.tgz#a440a1deca809eb747fd1b8fd520aa229b0e0186" @@ -5302,22 +5154,54 @@ snarkjs "^0.4.13" typescript "^4.5.5" -"@webb-tools/test-utils@0.1.4-106", "@webb-tools/test-utils@0.1.4-108", "@webb-tools/test-utils@0.1.4-117", "@webb-tools/test-utils@0.1.4-119": - version "0.1.4-117" - resolved "https://registry.yarnpkg.com/@webb-tools/test-utils/-/test-utils-0.1.4-117.tgz#262230935e1c749d24cfd65bf8da32c1e8d774af" - integrity sha512-1t+04JIH2GPDkkkraUVh4SfCEY28c1ZTJC2RVQ+7Iox0hQw0KTaKpT11yHYWDZZMc7HASY2LTDJ2ary50D+Csg== +"@webb-tools/tangle-substrate-types@0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@webb-tools/tangle-substrate-types/-/tangle-substrate-types-0.0.1.tgz#e90791b383272a61ed335d0888bc416dc4b901ce" + integrity sha512-fGQ4a3WEnkBioq25CB5RKLpz6Vuquwj8O211/BrIRaNNZv2351RHYV1kI9f3hB6YXcaDaduKWp2fZS2z4WUdqQ== + dependencies: + "@babel/cli" "^7.20.7" + "@babel/core" "^7.20.12" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-runtime" "^7.19.6" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/register" "^7.18.9" + "@babel/runtime" "^7.20.13" + "@open-web3/orml-types" "^1.1.3" + "@polkadot/api-derive" "10.3.2" + "@polkadot/dev" "^0.72.42" + "@polkadot/typegen" "10.3.2" + "@polkadot/types" "10.3.2" + babel-jest "^29.4.1" + babel-plugin-module-extension-resolver "^1.0.0" + babel-plugin-module-resolver "^5.0.0" + babel-plugin-styled-components "^2.0.7" + fs-extra "^11.1.1" + glob2base "^0.0.12" + minimatch "^7.4.2" + mkdirp "^2.1.5" + +"@webb-tools/test-utils@0.1.4-108", "@webb-tools/test-utils@0.1.4-123", "@webb-tools/test-utils@0.1.4-125": + version "0.1.4-125" + resolved "https://registry.yarnpkg.com/@webb-tools/test-utils/-/test-utils-0.1.4-125.tgz#1718480e5167c18d328d84b416d5cfbbac7ee7c4" + integrity sha512-Jkon4ay4YzkYPWwUoG8oLXr4emF6XB7JSbT52xJFtY3QVWJm+BO70ANv/S5bpzed4YVOjqyefRNSm5vpXgLaCQ== dependencies: - "@polkadot/api" "9.2.4" - "@polkadot/keyring" "10.1.7" + "@polkadot/api" "10.3.2" + "@polkadot/keyring" "11.1.3" "@types/node" "16.11.7" - "@webb-tools/api" "0.1.4-117" - "@webb-tools/dkg-substrate-types" "0.0.1" - "@webb-tools/protocol-solidity" "0.4.2" - "@webb-tools/protocol-substrate-types" "0.0.3" - "@webb-tools/sdk-core" "0.1.4-117" - "@webb-tools/tokens" "0.4.2" - "@webb-tools/utils" "0.4.2" - "@webb-tools/vbridge" "0.4.2" + "@webb-tools/api" "0.1.4-125" + "@webb-tools/dkg-substrate-types" "0.0.5" + "@webb-tools/protocol-substrate-types" "0.0.10" + "@webb-tools/sdk-core" "0.1.4-125" ecpair "^2.0.1" ethers "5.7.0" ganache "7.5.0" @@ -5326,41 +5210,35 @@ prettier "^2.2.1" tiny-secp256k1 "^2.2.1" -"@webb-tools/tokens@0.4.2", "@webb-tools/tokens@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/tokens/-/tokens-0.4.2.tgz#1be5e16b48298eb451d18297a6d3af099f718236" - integrity sha512-zVYmBJRc8RWCuigUT7giczISsSaQLShDGpTwbra57NhIhnvhaSwUQlhj/UOU4XxFQJQk1MBk3x67cy+eBZvBHg== - dependencies: - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/sdk-core" "0.1.4-113" - "@webb-tools/utils" "^0.4.2" - ethers "5.7.0" - -"@webb-tools/tokens@0.5.19", "@webb-tools/tokens@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/tokens/-/tokens-0.5.19.tgz#13645392f17c42c760fca2d0973f64ea26adc233" - integrity sha512-mXJk2iHx0cfN512HKBEbEhi/UgB+MTFLK5JK2K0Xq2HJ6OrKRSiFyH4KBIMmURGnmEBOrJMYCU2eqlU101Ezrw== +"@webb-tools/tokens@0.5.13": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@webb-tools/tokens/-/tokens-0.5.13.tgz#14bd693d198be3acd039cf313a5fba2517f4a4ad" + integrity sha512-w7jLUU7HEWDVbVLtoq3uXLEzRMoRB9fhCVFbXa8RH8hoVBxoxN3FCVZTdBw+BgvuMRGC5B8cjGgZ6Jn2TaVF1w== dependencies: - "@webb-tools/anchors" "^0.5.19" - "@webb-tools/contracts" "^0.5.19" + "@webb-tools/anchors" "^0.5.13" + "@webb-tools/contracts" "^0.5.13" "@webb-tools/sdk-core" "0.1.4-119" - "@webb-tools/utils" "^0.5.11" + "@webb-tools/utils" "^0.5.7" ethers "5.7.0" -"@webb-tools/utils@0.4.2", "@webb-tools/utils@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/utils/-/utils-0.4.2.tgz#5507c274474adddd460299405428116377f6ab8f" - integrity sha512-PKgHBOoqeZlxNBZsDabUGYs8T/KDFk+70842fFqa5qNtbwdZfoxkGddGdWlxm9DeKGz853f+rCW1zlJVBeBQ1w== +"@webb-tools/tokens@0.5.38", "@webb-tools/tokens@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/tokens/-/tokens-0.5.38.tgz#87f3777654d07248c4029d0fe3ad939d918a6df6" + integrity sha512-24JohBfeLdNyrpaEvl9IC69SIPGcKgKY2QRnAaHHdpKmitAhnUt1dIKZDauUKHKy6YE01dembGq2EncL8UNCNw== dependencies: - "@webb-tools/sdk-core" "0.1.4-113" + "@webb-tools/anchors" "^0.5.38" + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/create2-utils" "^0.5.5" + "@webb-tools/sdk-core" "0.1.4-123" + "@webb-tools/utils" "^0.5.30" ethers "5.7.0" -"@webb-tools/utils@0.5.7": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@webb-tools/utils/-/utils-0.5.7.tgz#a84d3c3edc3686663c5608156208599038abf7f8" - integrity sha512-Ng0YSsyH/JccVvKS/GN5WBDoYA2DTAnJjniLFI+QR7PKxQMZdmqA2hEzVu7KWSp7myffEgBGrpngs5uuHgRrjA== +"@webb-tools/utils@0.5.30", "@webb-tools/utils@^0.5.30", "@webb-tools/utils@^0.5.7": + version "0.5.30" + resolved "https://registry.yarnpkg.com/@webb-tools/utils/-/utils-0.5.30.tgz#eb212b38fedfe9be20ba4895b3928c2989b0d94c" + integrity sha512-8Fg4CFSMNhjmvJzGAiFwadtTUGu+TsKlgNa7sHBLI7RlvubbnaMd0SZAg92gcyrAi4H6e7YpxKl5MHlcqpFGvg== dependencies: - "@webb-tools/sdk-core" "0.1.4-119" + "@webb-tools/sdk-core" "0.1.4-123" ethers "5.7.0" "@webb-tools/utils@^0.2.7": @@ -5371,61 +5249,39 @@ "@webb-tools/sdk-core" "0.1.4-105" ethers "5.7.0" -"@webb-tools/utils@^0.5.11": - version "0.5.11" - resolved "https://registry.yarnpkg.com/@webb-tools/utils/-/utils-0.5.11.tgz#01353d65834cf0063eb00367ef225857c7d2bfa6" - integrity sha512-b56CwAgYR3loBSxhUPUXX8+DiJfxuQiz614EE1RK0xfVbEGCRe6sDw/xNFweTn/09il9mJXp15rsTJdfwJ09zQ== - dependencies: - "@webb-tools/sdk-core" "0.1.4-119" - ethers "5.7.0" - -"@webb-tools/vbridge@0.4.2", "@webb-tools/vbridge@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@webb-tools/vbridge/-/vbridge-0.4.2.tgz#b48311d32f28b540c4020f594406eed74174ead4" - integrity sha512-Hcetp82/roljFSR0ON8dPQsFUXVXZnkfJ0HY/PDZXcQrcVILhrBtb9UKK82K1qMGy4iv5WG5Yn4Be2MujeHjxg== - dependencies: - "@webb-tools/bridges" "^0.4.2" - "@webb-tools/contracts" "^0.4.2" - "@webb-tools/sdk-core" "0.1.4-113" - "@webb-tools/tokens" "^0.4.2" - "@webb-tools/utils" "^0.4.2" - ethers "5.7.0" - -"@webb-tools/vbridge@0.5.19", "@webb-tools/vbridge@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@webb-tools/vbridge/-/vbridge-0.5.19.tgz#6802b52eabcb1ffb1f572bddb6f1d7826fd6615e" - integrity sha512-WnOV3LEmHgKrGZx733VVKxd+9AAZiYGT3jzkDl7KKuTo3d8XwJrcvlv6VLvkoQDU40zUswgxB21e77LUVaIXFA== - dependencies: - "@webb-tools/anchors" "^0.5.19" - "@webb-tools/bridges" "^0.5.19" - "@webb-tools/contracts" "^0.5.19" - "@webb-tools/interfaces" "^0.5.19" - "@webb-tools/sdk-core" "0.1.4-119" - "@webb-tools/tokens" "^0.5.19" - "@webb-tools/utils" "^0.5.11" +"@webb-tools/vbridge@0.5.38", "@webb-tools/vbridge@^0.5.38": + version "0.5.38" + resolved "https://registry.yarnpkg.com/@webb-tools/vbridge/-/vbridge-0.5.38.tgz#5b84475796ba12cc34333c0041f570a575ebd1f3" + integrity sha512-YCjU76HKahuUuWhkYtXutDyQZ9KlfwjYaYsQ0n0ex1/Rt++/SKhgCSHr9WMogKYWYLG0V5i629R8KlZZNViGEw== + dependencies: + "@webb-tools/anchors" "^0.5.38" + "@webb-tools/bridges" "^0.5.38" + "@webb-tools/contracts" "^0.5.38" + "@webb-tools/interfaces" "^0.5.38" + "@webb-tools/sdk-core" "0.1.4-123" + "@webb-tools/tokens" "^0.5.38" + "@webb-tools/utils" "^0.5.30" ethers "5.7.0" -"@webb-tools/wasm-utils@0.1.4-117": - version "0.1.4-117" - resolved "https://registry.yarnpkg.com/@webb-tools/wasm-utils/-/wasm-utils-0.1.4-117.tgz#9f0b24f740da178bffb59bd2ac7294124773358a" - integrity sha512-XLT/ttk9txBca7DeVXDTgy8fwaOa6yyYxZ8URIxNehFAicg+toNSrWaCBOIyF/bKoe6p60//BQNHA3iCvpxzOQ== +"@webb-tools/wasm-utils@0.1.4-125": + version "0.1.4-125" + resolved "https://registry.yarnpkg.com/@webb-tools/wasm-utils/-/wasm-utils-0.1.4-125.tgz#f2942c1cdc05fac72ab80fdd5906e8de241a320e" + integrity sha512-wh2aDvNwjn7zq2cuP/Xo1V7RRaHsLfb6ssgjOK2zgTQfVWiNUxTNjsKHk0Gs0Hgv+llssee0lUL53sLQI66ipQ== -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== +"@webpack-cli/configtest@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.0.1.tgz#a69720f6c9bad6aef54a8fa6ba9c3533e7ef4c7f" + integrity sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A== -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" +"@webpack-cli/info@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.1.tgz#eed745799c910d20081e06e5177c2b2569f166c0" + integrity sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA== -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== +"@webpack-cli/serve@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.2.tgz#10aa290e44a182c02e173a89452781b1acbc86d9" + integrity sha512-S9h3GmOmzUseyeFW3tYNnWS7gNUuwxZ3mmMq0JyW78Vx1SGKPSkt5bT4pB0rUnVfHjP0EL9gW2bOzmtiTfQt0A== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -5451,11 +5307,6 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@zk-kit/incremental-merkle-tree@0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@zk-kit/incremental-merkle-tree/-/incremental-merkle-tree-0.4.3.tgz#197f72102d35dd9c546a6a432896d6d6f6de05f4" - integrity sha512-2qHfrJXtPx8/UmF0wFAUr4VqCLr3J/P859fk/e3fwKLUnf3baeIUAO6inY4wrh0NGy4bzpKUWYjDph0yTbPz6A== - JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -5555,6 +5406,11 @@ acorn@^8.4.1, acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -5679,6 +5535,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -5767,7 +5628,7 @@ array-flatten@^2.1.2: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -array-includes@^3.1.4, array-includes@^3.1.5: +array-includes@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== @@ -5778,6 +5639,17 @@ array-includes@^3.1.4, array-includes@^3.1.5: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -5800,24 +5672,24 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" array.prototype.reduce@^1.0.5: @@ -5831,6 +5703,17 @@ array.prototype.reduce@^1.0.5: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + asn1.js@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" @@ -5873,6 +5756,11 @@ ast-module-types@^3.0.0: resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-3.0.0.tgz#9a6d8a80f438b6b8fe4995699d700297f398bf81" integrity sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ== +ast-module-types@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-4.0.0.tgz#17e1cadd5b5b108e7295b0cf0cff21ccc226b639" + integrity sha512-Kd0o8r6CDazJGCRzs8Ivpn0xj19oNKrULhoJFzhGjRsLpekF2zyZs9Ukz+JvZhWD6smszfepakTFhAaYpsI12g== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -5935,7 +5823,7 @@ async@^2.6.1: dependencies: lodash "^4.17.14" -async@^3.2.0: +async@^3.2.0, async@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== @@ -6136,15 +6024,15 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.1.2.tgz#540d3241925c55240fb0c742e3ffc5f33a501978" - integrity sha512-IuG+F3HTHryJb7gacC7SQ59A9kO56BctUsT67uJHp1mMCHUOMXpDwOHWGifWqdWVknN2WNkCVQELPjXx0aLJ9Q== +babel-jest@^29.4.1: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^29.1.2" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.0.2" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -6181,31 +6069,31 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^29.0.2: - version "29.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.0.2.tgz#ae61483a829a021b146c016c6ad39b8bcc37c2c8" - integrity sha512-eBr2ynAEFjcebVvu8Ktx580BD1QKCrBG1XwEUTXJe285p9HA/4hOhfWCFRQhTKSyBV0VzjhG7H91Eifz9s29hg== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-module-extension-resolver@^1.0.0-rc.2: - version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/babel-plugin-module-extension-resolver/-/babel-plugin-module-extension-resolver-1.0.0-rc.2.tgz#c12a5bc29c478cc87cdf9359188bf500db53eae9" - integrity sha512-nSvCi7Eq079snAYgWbq+VM8eci7OER9MAhDchuxpdimuyJr06x/Stsmc2b6zP5CDv4XR54Etkpf7jOo5NfzgVg== +babel-plugin-module-extension-resolver@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-module-extension-resolver/-/babel-plugin-module-extension-resolver-1.0.0.tgz#063f5a7518d11b9d8a58c382f00f21f0100f380f" + integrity sha512-TIwNuS8jfdmoyn+tKMM9T6fczI5y6hHbicbCTIF+RyPSNSnQMlZNnCs/pQceOEZ/0qFNbaj5p0LCLG2VS1EF7g== -babel-plugin-module-resolver@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz#22a4f32f7441727ec1fbf4967b863e1e3e9f33e2" - integrity sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA== +babel-plugin-module-resolver@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz#2b7fc176bd55da25f516abf96015617b4f70fc73" + integrity sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q== dependencies: - find-babel-config "^1.2.0" - glob "^7.1.6" + find-babel-config "^2.0.0" + glob "^8.0.3" pkg-up "^3.1.0" - reselect "^4.0.0" - resolve "^1.13.1" + reselect "^4.1.7" + resolve "^1.22.1" babel-plugin-polyfill-corejs2@^0.3.0: version "0.3.1" @@ -6563,12 +6451,12 @@ babel-preset-env@^1.7.0: invariant "^2.2.2" semver "^5.3.0" -babel-preset-jest@^29.0.2: - version "29.0.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.0.2.tgz#e14a7124e22b161551818d89e5bdcfb3b2b0eac7" - integrity sha512-BeVXp7rH5TK96ofyEnHjznjLMQ2nAeDJ+QzxKnHAAMs0RgrQsCywjAN8m4mOm5Di0pxU//3AoEeJJrerMH5UeA== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^29.0.2" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" babel-register@^6.26.0: @@ -6636,6 +6524,14 @@ babelify@^7.3.0: babel-core "^6.0.14" object-assign "^4.0.0" +babyjubjub@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babyjubjub/-/babyjubjub-1.0.4.tgz#7b5ff0814005ea9c0a8bf63c93c5092232529bb6" + integrity sha512-R+EcKjxMSoctFCUtRUBKf3oDrnBrbbHUnt7u4LMGaERCx6/8+qfHZlqpBdsgAar9IvoPbDk0BLIj9lL9gfKCkw== + dependencies: + bignumber.js "^9.0.0" + buffer-reverse "^1.0.1" + babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -6715,7 +6611,7 @@ big-integer@1.6.36: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== -big-integer@^1.6.42, big-integer@^1.6.48: +big-integer@^1.6.42, big-integer@^1.6.48, big-integer@^1.6.51: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== @@ -6795,7 +6691,7 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.5: +bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -6805,7 +6701,7 @@ bn.js@4.11.6: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.8.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0, bn.js@^4.8.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -7014,17 +6910,6 @@ browserslist@^4.17.5, browserslist@^4.19.1: node-releases "^2.0.2" picocolors "^1.0.0" -browserslist@^4.20.2: - version "4.20.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" - integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== - dependencies: - caniuse-lite "^1.0.30001332" - electron-to-chromium "^1.4.118" - escalade "^3.1.1" - node-releases "^2.0.3" - picocolors "^1.0.0" - bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -7058,6 +6943,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer-reverse@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" + integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== + buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" @@ -7229,11 +7119,6 @@ caniuse-lite@^1.0.30001317: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz#8397391bec389b8ccce328636499b7284ee13285" integrity sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA== -caniuse-lite@^1.0.30001332: - version "1.0.30001335" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz#899254a0b70579e5a957c32dced79f0727c61f2a" - integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== - caniuse-lite@^1.0.30001400: version "1.0.30001418" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6" @@ -7257,6 +7142,11 @@ cbor@^5.2.0: bignumber.js "^9.0.1" nofilter "^1.0.4" +chacha20@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/chacha20/-/chacha20-0.1.4.tgz#0ae14749ac23c444912fdc482968461649f69e18" + integrity sha512-nxKJwtcXKxXIITkHQDrgCrZKAHjL6CY94n74obSux9WynGnoYj4wGTYHygp6DDSrBB9Efjts0QXSAFRz7E1hJg== + chai-as-promised@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" @@ -7350,18 +7240,13 @@ change-case@3.0.2: upper-case "^1.1.1" upper-case-first "^1.1.0" -changelog-parser@^2.0.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/changelog-parser/-/changelog-parser-2.8.1.tgz#1428998c275e4f7c0a855026dc60c66cde36bb87" - integrity sha512-tNUYFRCEeWTXmwLqoNtOEzx9wcytg72MmGQqsEs14ClYwIDln7sbQw7FJj/dulXgSlsxkemc9gpPQhZYZx1TPw== +changelog-parser@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/changelog-parser/-/changelog-parser-3.0.1.tgz#c8c693c78756e5b93fbf09760b65ca95b05cef68" + integrity sha512-1AEVJgnFEO4v5ukfEH/j9cr2Z39Y/GCieNi605azhufAolXF4vQAwZBY8BrUVRkvlI3gwe3i621/PIAW0zmmEQ== dependencies: line-reader "^0.2.4" - remove-markdown "^0.2.2" - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + remove-markdown "^0.5.0" chardet@^0.7.0: version "0.7.0" @@ -7478,7 +7363,14 @@ circom_runtime@0.1.20: dependencies: ffjavascript "0.2.55" -circomlibjs@0.0.8: +circom_runtime@0.1.22: + version "0.1.22" + resolved "https://registry.yarnpkg.com/circom_runtime/-/circom_runtime-0.1.22.tgz#f957c47662cdd03cd3fb76979c434c719a366373" + integrity sha512-V/XYZWBhbZY8SotkaGH4FbiDYAZ8a1Md++MBiKPDOuWS/NIJB+Q+XIiTC8zKMgoDaa9cd2OiTvsC9J6te7twNg== + dependencies: + ffjavascript "0.2.57" + +circomlibjs@0.0.8, circomlibjs@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/circomlibjs/-/circomlibjs-0.0.8.tgz#c2d1130d2d99fbb22f3d40ac19f347d768eace76" integrity sha512-oZFYapLO0mfiA+i2GU/V7bRNEEPjVcwV4M444nU5lNsdSJpqLwD57m9zxTD5m/KeY7WQ3lEAC9NNKEPQHu7s1w== @@ -7494,11 +7386,6 @@ circular-json@^0.5.9: resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== -cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== - class-is@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" @@ -7598,21 +7485,11 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -7686,6 +7563,11 @@ commander@3.0.2: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.16.0, commander@^2.18.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -7696,11 +7578,16 @@ commander@^4.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^7.0.0, commander@^7.2.0: +commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -7768,13 +7655,6 @@ console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -consolidate@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== - dependencies: - bluebird "^3.1.1" - constant-case@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" @@ -7804,7 +7684,7 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== @@ -7816,6 +7696,11 @@ convert-source-map@^1.5.1: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -7891,17 +7776,6 @@ cors@^2.8.1: object-assign "^4" vary "^1" -coveralls@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.1.tgz#f5d4431d8b5ae69c5079c8f8ca00d64ac77cf081" - integrity sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww== - dependencies: - js-yaml "^3.13.1" - lcov-parse "^1.0.0" - log-driver "^1.2.7" - minimist "^1.2.5" - request "^2.88.2" - crc-32@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.1.tgz#436d2bcaad27bcb6bd073a2587139d3024a16460" @@ -8040,27 +7914,12 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -cssesc@^3.0.0: +cssstyle@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== dependencies: - cssom "~0.3.6" + rrweb-cssom "^0.6.0" d@1, d@^1.0.1: version "1.0.1" @@ -8082,14 +7941,14 @@ data-uri-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== -data-urls@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" - integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== dependencies: abab "^2.0.6" whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" + whatwg-url "^12.0.0" debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" @@ -8105,20 +7964,13 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -8136,10 +7988,10 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decimal.js@^10.4.1: - version "10.4.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7" - integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw== +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== decode-uri-component@^0.2.0: version "0.2.0" @@ -8160,11 +8012,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== - deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -8302,16 +8149,16 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -dependency-tree@^8.1.1: - version "8.1.2" - resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-8.1.2.tgz#c9e652984f53bd0239bc8a3e50cbd52f05b2e770" - integrity sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A== +dependency-tree@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-9.0.0.tgz#9288dd6daf35f6510c1ea30d9894b75369aa50a2" + integrity sha512-osYHZJ1fBSon3lNLw70amAXsQ+RGzXsPvk9HbBgTLbp/bQBmpH5mOmsUvqXU+YEWVU0ZLewsmzOET/8jWswjDQ== dependencies: commander "^2.20.3" debug "^4.3.1" filing-cabinet "^3.0.1" - precinct "^8.0.0" - typescript "^3.9.7" + precinct "^9.0.0" + typescript "^4.0.0" deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" @@ -8353,11 +8200,6 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -8373,6 +8215,16 @@ detective-amd@^3.1.0: get-amd-module-type "^3.0.0" node-source-walk "^4.2.0" +detective-amd@^4.0.1, detective-amd@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-4.2.0.tgz#21c43465669f06cf894eef047a27e6e72ba6bc13" + integrity sha512-RbuEJHz78A8nW7CklkqTzd8lDCN42En53dgEIsya0DilpkwslamSZDasLg8dJyxbw46OxhSQeY+C2btdSkCvQQ== + dependencies: + ast-module-types "^4.0.0" + escodegen "^2.0.0" + get-amd-module-type "^4.1.0" + node-source-walk "^5.0.1" + detective-cjs@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.3.tgz#50e107d67b37f459b0ec02966ceb7e20a73f268b" @@ -8381,13 +8233,28 @@ detective-cjs@^3.1.1: ast-module-types "^3.0.0" node-source-walk "^4.0.0" -detective-es6@^2.2.0, detective-es6@^2.2.1: +detective-cjs@^4.0.0, detective-cjs@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-4.1.0.tgz#56b1558ca0910933c7fc47c740b957f0280ff302" + integrity sha512-QxzMwt5MfPLwS7mG30zvnmOvHLx5vyVvjsAV6gQOyuMoBR5G1DhS1eJZ4P10AlH+HSnk93mTcrg3l39+24XCtg== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + +detective-es6@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.2.tgz#ee5f880981d9fecae9a694007029a2f6f26d8d28" integrity sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw== dependencies: node-source-walk "^4.0.0" +detective-es6@^3.0.0, detective-es6@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-3.0.1.tgz#53a15fbb2f298c4a106d9fe7427da8a57162dde6" + integrity sha512-evPeYIEdK1jK3Oji5p0hX4sPV/1vK+o4ihcWZkMQE6voypSW/cIBiynOLxQk5KOOQbdP8oOAsYqouMTYO5l1sw== + dependencies: + node-source-walk "^5.0.0" + detective-less@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" @@ -8407,14 +8274,14 @@ detective-postcss@^4.0.0: postcss "^8.1.7" postcss-values-parser "^2.0.1" -detective-postcss@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-5.1.1.tgz#ec23ac3818f8be95ac3a38a8b9f3b6d43103ef87" - integrity sha512-YJMsvA0Y6/ST9abMNcQytl9iFQ2bfu4I7B74IUiAvyThfaI9Y666yipL+SrqfReoIekeIEwmGH72oeqX63mwUw== +detective-postcss@^6.1.0, detective-postcss@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-6.1.3.tgz#51a2d4419327ad85d0af071c7054c79fafca7e73" + integrity sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw== dependencies: is-url "^1.2.4" - postcss "^8.4.6" - postcss-values-parser "^5.0.0" + postcss "^8.4.23" + postcss-values-parser "^6.0.2" detective-sass@^3.0.1: version "3.0.2" @@ -8424,6 +8291,14 @@ detective-sass@^3.0.1: gonzales-pe "^4.3.0" node-source-walk "^4.0.0" +detective-sass@^4.0.1, detective-sass@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-4.1.3.tgz#6cdcc27ae8a90d15704e0ba83683048f77f10b75" + integrity sha512-xGRbwGaGte57gvEqM8B9GDiURY3El/H49vA6g9wFkxq9zalmTlTAuqWu+BsH0iwonGPruLt55tZZDEZqPc6lag== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + detective-scss@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.2.tgz#7d2a642616d44bf677963484fa8754d9558b8235" @@ -8432,11 +8307,29 @@ detective-scss@^2.0.1: gonzales-pe "^4.3.0" node-source-walk "^4.0.0" +detective-scss@^3.0.0, detective-scss@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-3.1.1.tgz#b49f05cadfb0837b04e23baba292581b7c7f65e1" + integrity sha512-FWkfru1jZBhUeuBsOeGKXKAVDrzYFSQFK2o2tuG/nCCFQ0U/EcXC157MNAcR5mmj+mCeneZzlkBOFJTesDjrww== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^5.0.1" + detective-stylus@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.3.tgz#20a702936c9fd7d4203fd7a903314b5dd43ac713" integrity sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q== +detective-stylus@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-2.0.1.tgz#d528dfa7ef3c4eb2fbc9a7249d54906ec4e05d09" + integrity sha512-/Tvs1pWLg8eYwwV6kZQY5IslGaYqc/GACxjcaGudiNtN5nKCH6o2WnJK3j0gA3huCnoQcbv8X7oz/c1lnvE3zQ== + +detective-stylus@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-3.0.0.tgz#c869795a7d6df7043ab6aee8b1a6f3dd66764232" + integrity sha512-1xYTzbrduExqMYmte7Qk99IRA3Aa6oV7PYzd+3yDcQXkmENvyGF/arripri6lxRDdNYEb4fZFuHtNRAXbz3iAA== + detective-typescript@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-7.0.2.tgz#c6e00b4c28764741ef719662250e6b014a5f3c8e" @@ -8447,16 +8340,21 @@ detective-typescript@^7.0.0: node-source-walk "^4.2.0" typescript "^3.9.10" +detective-typescript@^9.0.0, detective-typescript@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-9.1.1.tgz#b99c0122cbb35b39de2c5f58447f1e93ac28c6d5" + integrity sha512-Uc1yVutTF0RRm1YJ3g//i1Cn2vx1kwHj15cnzQP6ff5koNzQ0idc1zAC73ryaWEulA0ElRXFTq6wOqe8vUQ3MA== + dependencies: + "@typescript-eslint/typescript-estree" "^5.55.0" + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + typescript "^4.9.5" + diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== -diff-sequences@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f" - integrity sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA== - diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -8614,13 +8512,6 @@ ecpair@^2.0.1: typeforce "^1.18.0" wif "^2.0.6" -ed2curve@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ed2curve/-/ed2curve-0.3.0.tgz#322b575152a45305429d546b071823a93129a05d" - integrity sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ== - dependencies: - tweetnacl "1.x.x" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -8638,11 +8529,6 @@ electron-to-chromium@^1.3.47: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== -electron-to-chromium@^1.4.118: - version "1.4.132" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.132.tgz#b64599eb018221e52e2e4129de103b03a413c55d" - integrity sha512-JYdZUw/1068NWN+SwXQ7w6Ue0bWYGihvSUNNQwurvcDV/SM7vSiGZ3NuFvFgoEiCs4kB8xs3cX2an3wB7d4TBw== - electron-to-chromium@^1.4.251: version "1.4.276" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz#17837b19dafcc43aba885c4689358b298c19b520" @@ -8666,21 +8552,16 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -email-addresses@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb" - integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg== +email-addresses@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-5.0.0.tgz#7ae9e7f58eef7d5e3e2c2c2d3ea49b78dc854fa6" + integrity sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw== emittery@0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -8721,7 +8602,15 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.10.0, enhanced-resolve@^5.8.3: +enhanced-resolve@^5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enhanced-resolve@^5.8.3: version "5.10.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== @@ -8786,7 +8675,7 @@ es-abstract@^1.18.5: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: +es-abstract@^1.19.0, es-abstract@^1.19.5: version "1.20.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== @@ -8852,10 +8741,10 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-module-lexer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" + integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== es-shim-unscopables@^1.0.0: version "1.0.0" @@ -8946,21 +8835,31 @@ eslint-config-standard@^17.0.0: resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz#fd5b6cf1dcf6ba8d29f200c461de2e19069888cf" integrity sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg== -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" +eslint-plugin-deprecation@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.4.1.tgz#09a2889210955fd1a5c37703922c01724aba80eb" + integrity sha512-4vxTghWzxsBukPJVQupi6xlTuDc8Pyi1QlRCrFiLgwLPMJQW3cJCNaehJUKQqQFvuue5m4W27e179Y3Qjzeghg== + dependencies: + "@typescript-eslint/utils" "^5.57.0" + tslib "^2.3.1" + tsutils "^3.21.0" + eslint-plugin-es@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9" @@ -8974,28 +8873,30 @@ eslint-plugin-header@^3.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== -eslint-plugin-import-newlines@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.2.3.tgz#f678b242e64076365f48c31a271f1692b6437b7a" - integrity sha512-UQBhG7dcIoBjaPjbcudqqcKYQVhYXnu9NBB7d8pBnDFZP4GTNPdszpeUbvSxcp/JyBE4TsUZt5u/UtEVioxh9g== +eslint-plugin-import-newlines@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.3.1.tgz#e21705667778e8134382b50079fbb2c8d3a2fcde" + integrity sha512-7vyhwliGm+CTwteRw2ym+IO9OaksGYmSt63elPPBM0QJ3zwRwMKtgHOSHVtdHlrSERRTVCsWtMO8dDdAdmHMXg== -eslint-plugin-import@^2.26.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== +eslint-plugin-import@^2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.11.0" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" tsconfig-paths "^3.14.1" eslint-plugin-jest@^26.1.1: @@ -9005,64 +8906,72 @@ eslint-plugin-jest@^26.1.1: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-n@^15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.3.0.tgz#3e1ad236a17dce7ecc0760621c46cc251ef99560" - integrity sha512-IyzPnEWHypCWasDpxeJnim60jhlumbmq0pubL6IOcnk8u2y53s5QfT8JnXy7skjHJ44yWHRb11PLtDHuu1kg/Q== +eslint-plugin-jest@^27.2.1: + version "27.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz#b85b4adf41c682ea29f1f01c8b11ccc39b5c672c" + integrity sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + +eslint-plugin-n@^15.7.0: + version "15.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz#e29221d8f5174f84d18f2eb94765f2eeea033b90" + integrity sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q== dependencies: builtins "^5.0.1" eslint-plugin-es "^4.1.0" eslint-utils "^3.0.0" ignore "^5.1.1" - is-core-module "^2.10.0" + is-core-module "^2.11.0" minimatch "^3.1.2" resolve "^1.22.1" - semver "^7.3.7" + semver "^7.3.8" eslint-plugin-promise@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz#017652c07c9816413a41e11c30adc42c3d55ff18" integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw== -eslint-plugin-promise@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.1.tgz#a8cddf96a67c4059bdabf4d724a29572188ae423" - integrity sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw== +eslint-plugin-promise@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== eslint-plugin-react-hooks@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.31.8: - version "7.31.8" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf" - integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw== +eslint-plugin-react@^7.32.2: + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" prop-types "^15.8.1" - resolve "^2.0.0-next.3" + resolve "^2.0.0-next.4" semver "^6.3.0" - string.prototype.matchall "^4.0.7" + string.prototype.matchall "^4.0.8" -eslint-plugin-simple-import-sort@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz#9d9a2372b0606e999ea841b10458a370a6ccc160" - integrity sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw== +eslint-plugin-simple-import-sort@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz#cc4ceaa81ba73252427062705b64321946f61351" + integrity sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw== -eslint-plugin-sort-destructure-keys@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-sort-destructure-keys/-/eslint-plugin-sort-destructure-keys-1.4.0.tgz#45924521e556692002522468a75b6a9fbac11316" - integrity sha512-txU9l22mblz7YpyjJNYFy4wb5PVXiRMbc9lqFPPhvY4wKyBBYQvb31TIcduf7iRb4Bv01aiXcJiuCkOOrVY48Q== +eslint-plugin-sort-destructure-keys@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-sort-destructure-keys/-/eslint-plugin-sort-destructure-keys-1.5.0.tgz#dc45ff119b6886d4e72d3e0ff8a528af83b89388" + integrity sha512-xGLyqHtbFXZNXQSvAiQ4ISBYokrbUywEhmaA50fKtSKgceCv5y3zjoNuZwcnajdM6q29Nxj+oXC9KcqfMsAPrg== dependencies: natural-compare-lite "^1.4.0" @@ -9082,6 +8991,14 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -9111,6 +9028,11 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== + eslint@^7.3.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -9198,37 +9120,40 @@ eslint@^8.10.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^8.24.0: - version "8.25.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.25.0.tgz#00eb962f50962165d0c4ee3327708315eaa8058b" - integrity sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A== +eslint@^8.38.0: + version "8.39.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" + integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.10.5" + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.39.0" + "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" find-up "^5.0.0" - glob-parent "^6.0.1" - globals "^13.15.0" - globby "^11.1.0" + glob-parent "^6.0.2" + globals "^13.19.0" grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" @@ -9237,7 +9162,6 @@ eslint@^8.24.0: minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - regexpp "^3.2.0" strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" @@ -9270,14 +9194,14 @@ espree@^9.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^3.3.0" -espree@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" - integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.0" esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" @@ -9291,6 +9215,13 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -9313,12 +9244,7 @@ estree-walker@^0.6.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - -estree-walker@^2.0.1: +estree-walker@^2.0.1, estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== @@ -9571,6 +9497,16 @@ ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-cryptography@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.0.0.tgz#e052b49fa81affae29402e977b8d3a31f88612b6" + integrity sha512-g25m4EtfQGjstWgVE1aIz7XYYjf3kH5kG17ULWVB5dH6uLahsoltOhACzSxyDV+fhn4gbR4xRrOXGe6r2uh4Bg== + dependencies: + "@noble/curves" "1.0.0" + "@noble/hashes" "1.3.0" + "@scure/bip32" "1.3.0" + "@scure/bip39" "1.2.0" + ethereum-protocol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" @@ -9949,7 +9885,7 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3: +ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -9967,6 +9903,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -10000,11 +9941,6 @@ exit-on-epipe@~1.0.1: resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -10028,17 +9964,6 @@ expect@*: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" -expect@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.1.2.tgz#82f8f28d7d408c7c68da3a386a490ee683e1eced" - integrity sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw== - dependencies: - "@jest/expect-utils" "^29.1.2" - jest-get-type "^29.0.0" - jest-matcher-utils "^29.1.2" - jest-message-util "^29.1.2" - jest-util "^29.1.2" - express@^4.14.0: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -10191,7 +10116,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.7: +fast-glob@^3.2.12: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -10284,6 +10209,15 @@ ffjavascript@0.2.55: wasmcurves "0.1.0" web-worker "^1.2.0" +ffjavascript@0.2.57, ffjavascript@^0.2.57: + version "0.2.57" + resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.57.tgz#ba1be96015b2688192e49f2f4de2cc5150fd8594" + integrity sha512-V+vxZ/zPNcthrWmqfe/1YGgqdkTamJeXiED0tsk7B84g40DKlrTdx47IqZuiygqAVG6zMw4qYuvXftIJWsmfKQ== + dependencies: + wasmbuilder "0.0.16" + wasmcurves "0.2.0" + web-worker "^1.2.0" + ffjavascript@^0.2.38: version "0.2.51" resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.51.tgz#5d714686e3a5bde96dc57be84babc61911283629" @@ -10410,13 +10344,13 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-babel-config@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2" - integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA== +find-babel-config@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-2.0.0.tgz#a8216f825415a839d0f23f4d18338a1cc966f701" + integrity sha512-dOKT7jvF3hGzlW60Gc3ONox/0rRZ/tz7WCil0bqA1In/3I8f1BctpXahRnEKDySZqci7u+dqq93sZST9fOJpFw== dependencies: - json5 "^0.5.1" - path-exists "^3.0.0" + json5 "^2.1.1" + path-exists "^4.0.0" find-cache-dir@^2.0.0: version "2.1.0" @@ -10621,7 +10555,7 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^10.0.0, fs-extra@^10.1.0: +fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -10630,6 +10564,15 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^4.0.2, fs-extra@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -10790,10 +10733,10 @@ ganache@7.5.0: bufferutil "4.0.5" utf-8-validate "5.0.7" -gauge@^v4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== +gauge@^v5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.0.tgz#e270ca9d97dae84abf64e5277ef1ebddc7dd1e2f" + integrity sha512-0s5T5eciEG7Q3ugkxAkFtaDhrrhXsCRivA5y8C9WMHWuI8UlMOJg7+Iwf7Mccii+Dfs3H5jHepU0joPVyQU0Lw== dependencies: aproba "^1.0.3 || ^2.0.0" color-support "^1.1.3" @@ -10817,6 +10760,14 @@ get-amd-module-type@^3.0.0: ast-module-types "^3.0.0" node-source-walk "^4.2.2" +get-amd-module-type@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-4.1.0.tgz#af1396d02cd935cb6fafdc4a5282395db3422db6" + integrity sha512-0e/eK6vTGCnSfQ6eYs3wtH05KotJYIP7ZIZEueP/KlA+0dIAEs8bYFvOd/U56w1vfjhJqBagUxVMyy9Tr/cViQ== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -10909,14 +10860,14 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gh-pages@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-4.0.0.tgz#bd7447bab7eef008f677ac8cc4f6049ab978f4a6" - integrity sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ== +gh-pages@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-5.0.0.tgz#e0893272a0e33f0453e53a3c017c33b91ddd6394" + integrity sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ== dependencies: - async "^2.6.1" + async "^3.2.4" commander "^2.18.0" - email-addresses "^3.0.1" + email-addresses "^5.0.0" filenamify "^4.3.0" find-cache-dir "^3.3.1" fs-extra "^8.1.0" @@ -10934,15 +10885,15 @@ gh-release-assets@^2.0.0: simple-get "^4.0.0" util-extend "^1.0.1" -gh-release@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/gh-release/-/gh-release-6.0.4.tgz#367e0bd09d2bc020b2372e16fb026e17dc83b09b" - integrity sha512-6djoVxTUpbw9GZ/mveNjp5j/IDPY3KACscSlmNwwfkR+EQCNTSgGRywb0TRaQdA36RnwtcNOWEaLwB5ez7jSRg== +gh-release@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/gh-release/-/gh-release-7.0.2.tgz#921c6a0193aac2e751bc94b7124a5cec518cd641" + integrity sha512-5gVe+BHFa9OtsjOa72hFRBsfsCIIMYbba35wtEJIkAzZjPXQkUmpLhyrIYYdouh0JRTNNwZ4sDD7eiUhE8T/IQ== dependencies: - "@octokit/rest" "^18.0.9" - changelog-parser "^2.0.0" + "@octokit/rest" "^19.0.5" + changelog-parser "^3.0.0" deep-extend "^0.6.0" - gauge "^v4.0.4" + gauge "^v5.0.0" gh-release-assets "^2.0.0" ghauth "^5.0.0" github-url-to-object "^4.0.4" @@ -10975,7 +10926,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.1, glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -11049,10 +11000,10 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" @@ -11157,18 +11108,6 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -graphviz@0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" - integrity sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg== - dependencies: - temp "~0.4.0" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -11319,11 +11258,6 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash-sum@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" - integrity sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA== - hash.js@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" @@ -11417,11 +11351,6 @@ html-entities@^2.3.2: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - htmlparser2@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" @@ -11675,10 +11604,10 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== invariant@^2.2.2: version "2.2.4" @@ -11761,10 +11690,10 @@ is-buffer@^2.0.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-builtin-module@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0" - integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw== +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== dependencies: builtin-modules "^3.3.0" @@ -11792,10 +11721,10 @@ is-ci@^3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.10.0, is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -11806,6 +11735,13 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -11899,11 +11835,6 @@ is-function@^1.0.1: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-generator-function@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" @@ -11992,7 +11923,7 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -12029,7 +11960,7 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-reference@^1.2.1: +is-reference@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -12194,12 +12125,12 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: +istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -12210,32 +12141,6 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" @@ -12254,85 +12159,6 @@ jake@^10.6.1: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.0.0.tgz#aa238eae42d9372a413dd9a8dadc91ca1806dce0" - integrity sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - -jest-circus@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.1.2.tgz#4551068e432f169a53167fe1aef420cf51c8a735" - integrity sha512-ajQOdxY6mT9GtnfJRZBRYS7toNIJayiiyjDyoZcnvPRUPwJ58JX0ci0PKAKUo2C1RyzlHw0jabjLGKksO42JGA== - dependencies: - "@jest/environment" "^29.1.2" - "@jest/expect" "^29.1.2" - "@jest/test-result" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^29.1.2" - jest-matcher-utils "^29.1.2" - jest-message-util "^29.1.2" - jest-runtime "^29.1.2" - jest-snapshot "^29.1.2" - jest-util "^29.1.2" - p-limit "^3.1.0" - pretty-format "^29.1.2" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.1.2.tgz#423b9c5d3ea20a50b1354b8bf3f2a20e72110e89" - integrity sha512-vsvBfQ7oS2o4MJdAH+4u9z76Vw5Q8WBQF5MchDbkylNknZdrPTX1Ix7YRJyTlOWqRaS7ue/cEAn+E4V1MWyMzw== - dependencies: - "@jest/core" "^29.1.2" - "@jest/test-result" "^29.1.2" - "@jest/types" "^29.1.2" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^29.1.2" - jest-util "^29.1.2" - jest-validate "^29.1.2" - prompts "^2.0.1" - yargs "^17.3.1" - -jest-config@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.1.2.tgz#7d004345ca4c09f5d8f802355f54494e90842f4d" - integrity sha512-EC3Zi86HJUOz+2YWQcJYQXlf0zuBhJoeyxLM6vb6qJsVmpP7KcCP1JnyF0iaqTaXdBP8Rlwsvs7hnKWQWWLwwA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.1.2" - "@jest/types" "^29.1.2" - babel-jest "^29.1.2" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.1.2" - jest-environment-node "^29.1.2" - jest-get-type "^29.0.0" - jest-regex-util "^29.0.0" - jest-resolve "^29.1.2" - jest-runner "^29.1.2" - jest-util "^29.1.2" - jest-validate "^29.1.2" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.1.2" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -12343,97 +12169,30 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-diff@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.1.2.tgz#bb7aaf5353227d6f4f96c5e7e8713ce576a607dc" - integrity sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.0.0" - jest-get-type "^29.0.0" - pretty-format "^29.1.2" - -jest-docblock@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.0.0.tgz#3151bcc45ed7f5a8af4884dcc049aee699b4ceae" - integrity sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.1.2.tgz#d4c8532c07a846e79f194f7007ce7cb1987d1cd0" - integrity sha512-AmTQp9b2etNeEwMyr4jc0Ql/LIX/dhbgP21gHAizya2X6rUspHn2gysMXaj6iwWuOJ2sYRgP8c1P4cXswgvS1A== - dependencies: - "@jest/types" "^29.1.2" - chalk "^4.0.0" - jest-get-type "^29.0.0" - jest-util "^29.1.2" - pretty-format "^29.1.2" - -jest-environment-jsdom@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.1.2.tgz#59c5d7c53c999e1518cc2f1cd4ee19ab4b68eb68" - integrity sha512-D+XNIKia5+uDjSMwL/G1l6N9MCb7LymKI8FpcLo7kkISjc/Sa9w+dXXEa7u1Wijo3f8sVLqfxdGqYtRhmca+Xw== - dependencies: - "@jest/environment" "^29.1.2" - "@jest/fake-timers" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/jsdom" "^20.0.0" - "@types/node" "*" - jest-mock "^29.1.2" - jest-util "^29.1.2" - jsdom "^20.0.0" - -jest-environment-node@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.1.2.tgz#005e05cc6ea4b9b5ba55906ab1ce53c82f6907a7" - integrity sha512-C59yVbdpY8682u6k/lh8SUMDJPbOyCHOTgLVVi1USWFxtNV+J8fyIwzkg+RJIVI30EKhKiAGNxYaFr3z6eyNhQ== - dependencies: - "@jest/environment" "^29.1.2" - "@jest/fake-timers" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - jest-mock "^29.1.2" - jest-util "^29.1.2" - jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-get-type@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.0.0.tgz#843f6c50a1b778f7325df1129a0fd7aa713aef80" - integrity sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw== - -jest-haste-map@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.1.2.tgz#93f3634aa921b6b654e7c94137b24e02e7ca6ac9" - integrity sha512-xSjbY8/BF11Jh3hGSPfYTa/qBFrm3TPM7WU8pU93m2gqzORVLkHFWvuZmFsTEBPRKndfewXhMOuzJNHyJIZGsw== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/types" "^29.1.2" + "@jest/types" "^29.5.0" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.0.0" - jest-util "^29.1.2" - jest-worker "^29.1.2" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.1.2.tgz#4c846db14c58219430ccbc4f01a1ec52ebee4fc2" - integrity sha512-TG5gAZJpgmZtjb6oWxBLf2N6CfQ73iwCe6cofu/Uqv9iiAm6g502CAnGtxQaTfpHECBdVEMRBhomSXeLnoKjiQ== - dependencies: - jest-get-type "^29.0.0" - pretty-format "^29.1.2" - jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" @@ -12444,16 +12203,6 @@ jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.1.2.tgz#e68c4bcc0266e70aa1a5c13fb7b8cd4695e318a1" - integrity sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw== - dependencies: - chalk "^4.0.0" - jest-diff "^29.1.2" - jest-get-type "^29.0.0" - pretty-format "^29.1.2" - jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -12469,186 +12218,23 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.1.2.tgz#c21a33c25f9dc1ebfcd0f921d89438847a09a501" - integrity sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.1.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.1.2" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.1.2.tgz#de47807edbb9d4abf8423f1d8d308d670105678c" - integrity sha512-PFDAdjjWbjPUtQPkQufvniXIS3N9Tv7tbibePEjIIprzjgo0qQlyUiVMrT4vL8FaSJo1QXifQUOuPH3HQC/aMA== - dependencies: - "@jest/types" "^29.1.2" - "@types/node" "*" - jest-util "^29.1.2" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.0.0.tgz#b442987f688289df8eb6c16fa8df488b4cd007de" - integrity sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug== - -jest-resolve-dependencies@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.1.2.tgz#a6919e58a0c7465582cb8ec2d745b4e64ae8647f" - integrity sha512-44yYi+yHqNmH3OoWZvPgmeeiwKxhKV/0CfrzaKLSkZG9gT973PX8i+m8j6pDrTYhhHoiKfF3YUFg/6AeuHw4HQ== - dependencies: - jest-regex-util "^29.0.0" - jest-snapshot "^29.1.2" - -jest-resolve@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.1.2.tgz#9dd8c2fc83e59ee7d676b14bd45a5f89e877741d" - integrity sha512-7fcOr+k7UYSVRJYhSmJHIid3AnDBcLQX3VmT9OSbPWsWz1MfT7bcoerMhADKGvKCoMpOHUQaDHtQoNp/P9JMGg== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.1.2" - jest-pnp-resolver "^1.2.2" - jest-util "^29.1.2" - jest-validate "^29.1.2" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - -jest-runner@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.1.2.tgz#f18b2b86101341e047de8c2f51a5fdc4e97d053a" - integrity sha512-yy3LEWw8KuBCmg7sCGDIqKwJlULBuNIQa2eFSVgVASWdXbMYZ9H/X0tnXt70XFoGf92W2sOQDOIFAA6f2BG04Q== - dependencies: - "@jest/console" "^29.1.2" - "@jest/environment" "^29.1.2" - "@jest/test-result" "^29.1.2" - "@jest/transform" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^29.0.0" - jest-environment-node "^29.1.2" - jest-haste-map "^29.1.2" - jest-leak-detector "^29.1.2" - jest-message-util "^29.1.2" - jest-resolve "^29.1.2" - jest-runtime "^29.1.2" - jest-util "^29.1.2" - jest-watcher "^29.1.2" - jest-worker "^29.1.2" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.1.2.tgz#dbcd57103d61115479108d5864bdcd661d9c6783" - integrity sha512-jr8VJLIf+cYc+8hbrpt412n5jX3tiXmpPSYTGnwcvNemY+EOuLNiYnHJ3Kp25rkaAcTWOEI4ZdOIQcwYcXIAZw== - dependencies: - "@jest/environment" "^29.1.2" - "@jest/fake-timers" "^29.1.2" - "@jest/globals" "^29.1.2" - "@jest/source-map" "^29.0.0" - "@jest/test-result" "^29.1.2" - "@jest/transform" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.1.2" - jest-message-util "^29.1.2" - jest-mock "^29.1.2" - jest-regex-util "^29.0.0" - jest-resolve "^29.1.2" - jest-snapshot "^29.1.2" - jest-util "^29.1.2" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.1.2.tgz#7dd277e88c45f2d2ff5888de1612e63c7ceb575b" - integrity sha512-rYFomGpVMdBlfwTYxkUp3sjD6usptvZcONFYNqVlaz4EpHPnDvlWjvmOQ9OCSNKqYZqLM2aS3wq01tWujLg7gg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.1.2" - "@jest/transform" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.1.2" - graceful-fs "^4.2.9" - jest-diff "^29.1.2" - jest-get-type "^29.0.0" - jest-haste-map "^29.1.2" - jest-matcher-utils "^29.1.2" - jest-message-util "^29.1.2" - jest-util "^29.1.2" - natural-compare "^1.4.0" - pretty-format "^29.1.2" - semver "^7.3.5" +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-util@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.1.2.tgz#ac5798e93cb6a6703084e194cfa0898d66126df1" - integrity sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ== +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== dependencies: - "@jest/types" "^29.1.2" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.1.2.tgz#83a728b8f6354da2e52346878c8bc7383516ca51" - integrity sha512-k71pOslNlV8fVyI+mEySy2pq9KdXdgZtm7NHrBX8LghJayc3wWZH0Yr0mtYNGaCU4F1OLPXRkwZR0dBm/ClshA== - dependencies: - "@jest/types" "^29.1.2" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.0.0" - leven "^3.1.0" - pretty-format "^29.1.2" - -jest-watcher@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.1.2.tgz#de21439b7d889e2fcf62cc2a4779ef1a3f1f3c62" - integrity sha512-6JUIUKVdAvcxC6bM8/dMgqY2N4lbT+jZVsxh0hCJRbwkIEnbr/aPjMQ28fNDI5lB51Klh00MWZZeVf27KBUj5w== - dependencies: - "@jest/test-result" "^29.1.2" - "@jest/types" "^29.1.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^29.1.2" - string-length "^4.0.1" - jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -12658,26 +12244,16 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.1.2.tgz#a68302af61bce82b42a9a57285ca7499d29b2afc" - integrity sha512-AdTZJxKjTSPHbXT/AIOjQVmoFx0LHFcVabWu0sxI7PAy7rFf8c0upyvgBKgguVXdM4vY74JdwkyD4hSmpTW8jA== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" - jest-util "^29.1.2" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.1.2.tgz#f821a1695ffd6cd0efc3b59d2dfcc70a98582499" - integrity sha512-5wEIPpCezgORnqf+rCaYD1SK+mNN7NsstWzIsuvsnrhR/hSxXWd82oI7DkrbJ+XTD28/eG8SmxdGvukrGGK6Tw== - dependencies: - "@jest/core" "^29.1.2" - "@jest/types" "^29.1.2" - import-local "^3.0.2" - jest-cli "^29.1.2" - js-cleanup@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/js-cleanup/-/js-cleanup-1.2.0.tgz#8dbc65954b1d38b255f1e8cf02cd17b3f7a053f9" @@ -12732,18 +12308,17 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^20.0.0: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.1.tgz#d95b4a3b6e1eec6520aa01d9d908eade8c6ba153" - integrity sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A== +jsdom@^21.1.1: + version "21.1.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.2.tgz#6433f751b8718248d646af1cdf6662dc8a1ca7f9" + integrity sha512-sCpFmK2jv+1sjff4u7fzft+pUh2KSUbUrEHYHyfSIbGTIcmnjyp83qg6qLwdJ/I3LpTXx33ACxeRL7Lsyc6lGQ== dependencies: abab "^2.0.6" - acorn "^8.8.0" + acorn "^8.8.2" acorn-globals "^7.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.2" - decimal.js "^10.4.1" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" domexception "^4.0.0" escodegen "^2.0.0" form-data "^4.0.0" @@ -12751,17 +12326,18 @@ jsdom@^20.0.0: http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.1" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.2" - parse5 "^7.1.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" saxes "^6.0.0" symbol-tree "^3.2.4" tough-cookie "^4.1.2" - w3c-xmlserializer "^3.0.0" + w3c-xmlserializer "^4.0.0" webidl-conversions "^7.0.0" whatwg-encoding "^2.0.0" whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - ws "^8.9.0" + whatwg-url "^12.0.1" + ws "^8.13.0" xml-name-validator "^4.0.0" jsesc@^1.3.0: @@ -12873,6 +12449,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.1, json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + json5@^2.1.2, json5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" @@ -13001,11 +12582,6 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -13013,6 +12589,14 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" +launch-editor@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7" + integrity sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.7.3" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -13020,11 +12604,6 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcov-parse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" - integrity sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ== - level-codec@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" @@ -13186,11 +12765,6 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -13295,11 +12869,6 @@ lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-driver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" - integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== - log-symbols@4.1.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -13380,14 +12949,6 @@ lru-cache@^3.2.0: dependencies: pseudomap "^1.0.1" -lru-cache@^4.1.2: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -13410,31 +12971,32 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -madge@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/madge/-/madge-5.0.1.tgz#2096d9006558ea0669b3ade89c2cda708a24e22b" - integrity sha512-krmSWL9Hkgub74bOjnjWRoFPAJvPwSG6Dbta06qhWOq6X/n/FPzO3ESZvbFYVIvG2g4UHXvCJN1b+RZLaSs9nA== +madge@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/madge/-/madge-6.0.0.tgz#d17d68d1023376318cae89abd16629d329f4ed0a" + integrity sha512-dddxP62sj5pL+l9MJnq9C34VFqmRj+2+uSOdn/7lOTSliLRH0WyQ8uCEF3VxkPRNOBvMKK2xumnIE15HRSAL9A== dependencies: chalk "^4.1.1" commander "^7.2.0" commondir "^1.0.1" debug "^4.3.1" - dependency-tree "^8.1.1" - detective-amd "^3.1.0" - detective-cjs "^3.1.1" - detective-es6 "^2.2.0" + dependency-tree "^9.0.0" + detective-amd "^4.0.1" + detective-cjs "^4.0.0" + detective-es6 "^3.0.0" detective-less "^1.0.2" - detective-postcss "^5.0.0" - detective-sass "^3.0.1" - detective-scss "^2.0.1" - detective-stylus "^1.0.0" - detective-typescript "^7.0.0" - graphviz "0.0.9" + detective-postcss "^6.1.0" + detective-sass "^4.0.1" + detective-scss "^3.0.0" + detective-stylus "^2.0.1" + detective-typescript "^9.0.0" ora "^5.4.1" pluralize "^8.0.0" precinct "^8.1.0" pretty-ms "^7.0.1" rc "^1.2.7" + stream-to-array "^2.3.0" + ts-graphviz "^1.5.0" typescript "^3.9.5" walkdir "^0.4.1" @@ -13445,6 +13007,13 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -13544,13 +13113,6 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -13593,6 +13155,11 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -13682,14 +13249,14 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -13703,6 +13270,13 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +minimatch@^7.4.2: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -13755,6 +13329,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.5: dependencies: minimist "^1.2.6" +mkdirp@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + mocha-parallel-tests@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/mocha-parallel-tests/-/mocha-parallel-tests-2.3.0.tgz#92d5cf0a3c9ba71b1a9c08a7b56194c023a58de1" @@ -13765,32 +13344,29 @@ mocha-parallel-tests@^2.3.0: uuid "^3.4.0" yargs "^13.3.0" -mocha@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" - integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" - debug "4.3.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.2.0" - growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "4.2.1" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.1" + nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - workerpool "6.2.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -13800,10 +13376,10 @@ mock-fs@^4.1.0: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== -mock-socket@^9.1.5: - version "9.1.5" - resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.1.5.tgz#2c4e44922ad556843b6dfe09d14ed8041fa2cdeb" - integrity sha512-3DeNIcsQixWHHKk6NdoBhWI4t1VMj5/HzfnI1rE/pLl5qKx7+gd4DNA07ehTaZ6MoUU053si6Hd+YtiM/tQZfg== +mock-socket@^9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282" + integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag== module-definition@^3.3.1: version "3.4.0" @@ -13813,6 +13389,14 @@ module-definition@^3.3.1: ast-module-types "^3.0.0" node-source-walk "^4.0.0" +module-definition@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-4.1.0.tgz#148ff9348e3401867229dcbe5947f4f6d5ccd3a2" + integrity sha512-rHXi/DpMcD2qcKbPCTklDbX9lBKJrUSl971TW5l6nMpqKCIlzJqmQ8cfEF5M923h2OOLHPDVlh5pJxNyV+AJlw== + dependencies: + ast-module-types "^4.0.0" + node-source-walk "^5.0.1" + module-lookup-amd@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" @@ -13919,16 +13503,21 @@ nanoassert@^2.0.0: resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09" integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA== -nanoid@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + nanolru@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/nanolru/-/nanolru-1.0.0.tgz#0a5679cd4e4578c4ca3741e610b71c4c9b5afaf8" @@ -13993,10 +13582,10 @@ no-case@^2.2.0, no-case@^2.3.2: dependencies: lower-case "^1.1.1" -nock@^13.2.9: - version "13.2.9" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" - integrity sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA== +nock@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" + integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" @@ -14046,10 +13635,10 @@ node-fetch@^3.2.1: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-fetch@^3.2.10: - version "3.2.10" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8" - integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA== +node-fetch@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== dependencies: data-uri-to-buffer "^4.0.0" fetch-blob "^3.1.4" @@ -14088,11 +13677,6 @@ node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== -node-releases@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" - integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== - node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" @@ -14105,6 +13689,13 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: dependencies: "@babel/parser" "^7.0.0" +node-source-walk@^5.0.0, node-source-walk@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-5.0.2.tgz#0eb439ce378946ce531e07a6a0073d06288396dd" + integrity sha512-Y4jr/8SRS5hzEdZ7SGuvZGwfORvNsSsNRwDXx5WisiqzsVfeftDvRgfeqWNgZvWSJbgubTRVRYBzK6UO+ErqjA== + dependencies: + "@babel/parser" "^7.21.4" + nofilter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" @@ -14157,10 +13748,10 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" -nwsapi@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +nwsapi@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" + integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== oauth-sign@~0.9.0: version "0.9.0" @@ -14236,23 +13827,23 @@ object.assign@^4.1.3, object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" object.getownpropertydescriptors@^2.1.1: version "2.1.5" @@ -14264,13 +13855,13 @@ object.getownpropertydescriptors@^2.1.1: define-properties "^1.1.4" es-abstract "^1.20.4" -object.hasown@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== dependencies: define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" object.pick@^1.3.0: version "1.3.0" @@ -14279,14 +13870,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" oboe@2.1.4: version "2.1.4" @@ -14449,7 +14040,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -14549,7 +14140,7 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -14572,13 +14163,20 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5@^7.0.0, parse5@^7.1.1: +parse5@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.1.tgz#4649f940ccfb95d8754f37f73078ea20afe0c746" integrity sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg== dependencies: entities "^4.4.0" +parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -14743,11 +14341,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -14833,14 +14426,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== -postcss-selector-parser@^6.0.2: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - postcss-values-parser@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" @@ -14850,24 +14435,16 @@ postcss-values-parser@^2.0.1: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-values-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-5.0.0.tgz#10c61ac3f488e4de25746b829ea8d8894e9ac3d2" - integrity sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw== +postcss-values-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" + integrity sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw== dependencies: color-name "^1.1.4" is-url-superb "^4.0.0" quote-unquote "^1.0.0" -postcss@^7.0.36: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -postcss@^8.1.7, postcss@^8.4.6: +postcss@^8.1.7: version "8.4.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.17.tgz#f87863ec7cd353f81f7ab2dec5d67d861bbb1be5" integrity sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q== @@ -14876,12 +14453,21 @@ postcss@^8.1.7, postcss@^8.4.6: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.23: + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + postinstall-postinstall@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== -precinct@^8.0.0, precinct@^8.1.0: +precinct@^8.1.0: version "8.3.1" resolved "https://registry.yarnpkg.com/precinct/-/precinct-8.3.1.tgz#94b99b623df144eed1ce40e0801c86078466f0dc" integrity sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q== @@ -14900,6 +14486,24 @@ precinct@^8.0.0, precinct@^8.1.0: module-definition "^3.3.1" node-source-walk "^4.2.0" +precinct@^9.0.0: + version "9.2.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-9.2.1.tgz#db0a67abff7b0a9a3b2b1ac33d170e8a5fcac7b2" + integrity sha512-uzKHaTyiVejWW7VJtHInb9KBUq9yl9ojxXGujhjhDmPon2wgZPBKQIKR+6csGqSlUeGXAA4MEFnU6DesxZib+A== + dependencies: + "@dependents/detective-less" "^3.0.1" + commander "^9.5.0" + detective-amd "^4.1.0" + detective-cjs "^4.1.0" + detective-es6 "^3.0.1" + detective-postcss "^6.1.1" + detective-sass "^4.1.1" + detective-scss "^3.0.1" + detective-stylus "^3.0.0" + detective-typescript "^9.1.1" + module-definition "^4.1.0" + node-source-walk "^5.0.1" + precond@0.2: version "0.2.3" resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" @@ -14925,16 +14529,16 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -"prettier@^1.18.2 || ^2.0.0", prettier@^2.2.1, prettier@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== - prettier@^2.1.2, prettier@^2.3.2: version "2.8.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc" integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== +prettier@^2.2.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + prettier@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" @@ -14949,15 +14553,6 @@ pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.1.2.tgz#b1f6b75be7d699be1a051f5da36e8ae9e76a8e6a" - integrity sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg== - dependencies: - "@jest/schemas" "^29.0.0" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-ms@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" @@ -15006,14 +14601,6 @@ promise-to-callback@^1.0.0: is-fn "^1.0.0" set-immediate-shim "^1.0.1" -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -15041,7 +14628,7 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.1, pseudomap@^1.0.2: +pseudomap@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== @@ -15148,6 +14735,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -15226,6 +14818,16 @@ r1csfile@0.0.40: fastfile "0.0.20" ffjavascript "0.2.55" +r1csfile@0.0.45: + version "0.0.45" + resolved "https://registry.yarnpkg.com/r1csfile/-/r1csfile-0.0.45.tgz#59d59a33f8b5280017fc00ee691d003a3d705fe0" + integrity sha512-YKIp4D441aZ6OoI9y+bfAyb2j4Cl+OFq/iiX6pPWDrL4ZO968h0dq0w07i65edvrTt7/G43mTnl0qEuLXyp/Yw== + dependencies: + "@iden3/bigarray" "0.0.2" + "@iden3/binfileutils" "0.0.11" + fastfile "0.0.20" + ffjavascript "0.2.57" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -15286,11 +14888,6 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -15376,12 +14973,12 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.9.0" + resolve "^1.20.0" regenerate-unicode-properties@^10.0.1: version "10.0.1" @@ -15433,10 +15030,10 @@ regenerator-transform@^0.14.2: dependencies: "@babel/runtime" "^7.8.4" -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" @@ -15448,7 +15045,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -15495,6 +15092,18 @@ regexpu-core@^5.1.0: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + registry-auth-token@^4.0.0: version "4.2.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" @@ -15545,10 +15154,10 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -remove-markdown@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/remove-markdown/-/remove-markdown-0.2.2.tgz#66b0ceeba9fb77ca9636bb1b0307ce21a32a12a6" - integrity sha512-jwgEf3Yh/xi4WodWi/vPlasa9C9pMv1kz5ITOIAGjBW7PeZ/CHZCdBfJzQnn2VX2cBvf1xCuJv0tUJqn/FCMNA== +remove-markdown@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/remove-markdown/-/remove-markdown-0.5.0.tgz#a596264bbd60b9ceab2e2ae86e5789eee91aee32" + integrity sha512-x917M80K97K5IN1L8lUvFehsfhR8cYjGQ/yAMRI9E7JIKivtl5Emo5iD13DhMr+VojzMCiYk8V2byNPwT/oapg== repeat-element@^1.1.2: version "1.1.4" @@ -15567,7 +15176,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.79.0, request@^2.85.0, request@^2.88.2: +request@^2.79.0, request@^2.85.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -15636,10 +15245,10 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -reselect@^4.0.0: - version "4.1.6" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656" - integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ== +reselect@^4.1.7: + version "4.1.8" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" + integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== resolve-cwd@^3.0.0: version "3.0.0" @@ -15668,12 +15277,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.8.1, resolve@^1.9.0, resolve@~1.22.1: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.1, resolve@^1.8.1, resolve@~1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -15691,7 +15295,7 @@ resolve@^1.14.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: +resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== @@ -15805,13 +15409,25 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.77.2, rollup@^2.79.1: +rollup@^2.77.2: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== optionalDependencies: fsevents "~2.3.2" +rollup@^3.20.7: + version "3.21.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.21.0.tgz#0a71517db56e150222670f88e5e7acfa4fede7c8" + integrity sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ== + optionalDependencies: + fsevents "~2.3.2" + +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -15836,10 +15452,10 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -rxjs@^7.5.6: - version "7.5.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== +rxjs@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" @@ -15895,7 +15511,7 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -schema-utils@^3.1.0, schema-utils@^3.1.1: +schema-utils@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== @@ -15904,6 +15520,15 @@ schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.2.tgz#36c10abca6f7577aeae136c804b0c741edeadc99" + integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + schema-utils@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" @@ -16014,6 +15639,13 @@ semver@^7.2.1, semver@^7.3.5: dependencies: lru-cache "^6.0.0" +semver@^7.3.8: + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -16065,13 +15697,20 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -serialize-javascript@6.0.0, serialize-javascript@^6.0.0: +serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -16202,6 +15841,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + shelljs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" @@ -16257,11 +15901,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - skip-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/skip-regex/-/skip-regex-1.0.2.tgz#ac655d77e7c771ac2b9f37585fea37bff56ad65b" @@ -16282,6 +15921,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -16291,6 +15935,14 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +smoldot@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-1.0.1.tgz#1749b62ddf75667a05dca2a52d8681596accf932" + integrity sha512-48M9tLU+5q0XHFUCuGULraghfqQU/yARkdcYZzulFB38f2aw4ujTHzlbE+bhiJ8+h63uoXdAQO0WeCsWDTXGkA== + dependencies: + pako "^2.0.4" + ws "^8.8.1" + snake-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" @@ -16360,6 +16012,22 @@ snarkjs@^0.4.13: logplease "^1.2.15" r1csfile "0.0.40" +snarkjs@^0.6.10: + version "0.6.11" + resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.6.11.tgz#577b0250c0d9bb93c0ee1d4a3d3075956d11775c" + integrity sha512-pYDcrSKmUMMJtYxSQTMNG/MtuGvUWPSk9gZzADeIiHp8vJmFdCscMFa/YtemPAkne4v6Awm5HAhqbfNPyoqjug== + dependencies: + "@iden3/binfileutils" "0.0.11" + bfj "^7.0.2" + blake2b-wasm "^2.4.0" + circom_runtime "0.1.22" + ejs "^3.1.6" + fastfile "0.0.20" + ffjavascript "0.2.57" + js-sha3 "^0.8.0" + logplease "^1.2.15" + r1csfile "0.0.45" + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" @@ -16425,14 +16093,6 @@ source-map-support@0.5.12: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@^0.4.15: version "0.4.18" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" @@ -16584,6 +16244,13 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== + dependencies: + any-promise "^1.1.0" + stream-to-pull-stream@^1.7.1: version "1.7.3" resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" @@ -16597,14 +16264,6 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -16632,18 +16291,18 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.matchall@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" string.prototype.trim@~1.2.6: @@ -16841,21 +16500,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" - integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -16949,34 +16600,21 @@ temp@^0.9.4: mkdirp "^0.5.1" rimraf "~2.6.2" -temp@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" - integrity sha512-IsFisGgDKk7qzK9erMIkQe/XwiSUdac7z3wYOsjcLkhPBy3k1SlvLoIh2dAHIlEpgA971CgguMrx9z8fFg7tSA== - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - -terser-webpack-plugin@^5.1.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" - integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== +terser-webpack-plugin@^5.3.7: + version "5.3.7" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz#ef760632d24991760f339fe9290deb936ad1ffc7" + integrity sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw== dependencies: - "@jridgewell/trace-mapping" "^0.3.14" + "@jridgewell/trace-mapping" "^0.3.17" jest-worker "^27.4.5" schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - terser "^5.14.1" + serialize-javascript "^6.0.1" + terser "^5.16.5" -terser@^5.14.1: - version "5.15.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" - integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw== +terser@^5.16.5: + version "5.17.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" + integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -17137,12 +16775,12 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tr46@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== dependencies: - punycode "^2.1.1" + punycode "^2.3.0" tr46@~0.0.3: version "0.0.3" @@ -17205,6 +16843,11 @@ ts-generator@^0.1.1: resolve "^1.8.1" ts-essentials "^1.0.0" +ts-graphviz@^1.5.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/ts-graphviz/-/ts-graphviz-1.6.1.tgz#f44525c048cb8c8c188b7324d2a91015fd31ceaf" + integrity sha512-9aZKR7hoQAHXlgb7HvUND3y5VhEI5PMNVv/BfelPXebcsxdwZhBYbM8XpnV4NfiHV9O0/sAI9sQVuce0gogzlA== + ts-node@^10.1.0, ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -17249,10 +16892,10 @@ tslib@^2.0.0, tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tsutils@^3.21.0: version "3.21.0" @@ -17268,21 +16911,21 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0: +tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== -tweetnacl@1.x.x, tweetnacl@^1.0.0, tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +tweetnacl@^1.0.0, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -17297,7 +16940,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -17364,11 +17007,6 @@ typechain@^5.1.2: prettier "^2.1.2" ts-essentials "^7.0.1" -typed-assert@^1.0.8: - version "1.0.9" - resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213" - integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -17406,7 +17044,12 @@ typescript@^3.9.10, typescript@^3.9.5, typescript@^3.9.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@^4.5.5, typescript@^4.7.4, typescript@^4.8.4: +typescript@^4.0.0, typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +typescript@^4.5.5, typescript@^4.7.4: version "4.8.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== @@ -17416,6 +17059,11 @@ typescript@^4.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== +typescript@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + typewise-core@^1.2, typewise-core@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" @@ -17496,6 +17144,11 @@ unicode-match-property-value-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" @@ -17687,7 +17340,7 @@ utf8@3.0.0, utf8@^3.0.0: resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -17755,15 +17408,6 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -17801,15 +17445,10 @@ vscode-textmate@^6.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-6.0.0.tgz#a3777197235036814ac9a92451492f2748589210" integrity sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ== -vue-template-es2015-compiler@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" - integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== - -w3c-xmlserializer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" - integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: xml-name-validator "^4.0.0" @@ -18656,22 +18295,23 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-cli@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== +webpack-cli@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.0.2.tgz#2954c10ecb61c5d4dad6f68ee2d77f051741946c" + integrity sha512-4y3W5Dawri5+8dXm3+diW6Mn1Ya+Dei6eEVAdIduAmYNLzv1koKVAqsfgrrc9P2mhrYHQphx5htnGkcNwtubyQ== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" + "@webpack-cli/configtest" "^2.0.1" + "@webpack-cli/info" "^2.0.1" + "@webpack-cli/serve" "^2.0.2" colorette "^2.0.14" - commander "^7.0.0" + commander "^10.0.1" cross-spawn "^7.0.3" + envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" + interpret "^3.1.1" + rechoir "^0.8.0" webpack-merge "^5.7.3" webpack-dev-middleware@^5.3.1: @@ -18685,10 +18325,10 @@ webpack-dev-middleware@^5.3.1: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5" - integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== +webpack-dev-server@^4.13.3: + version "4.13.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.3.tgz#9feb740b8b56b886260bae1360286818a221bae8" + integrity sha512-KqqzrzMRSRy5ePz10VhjyL27K2dxqwXQLP5rAKwRJBPUahe7Z2bBWzHw37jeb8GCPKxZRO79ZdQUAPesMh/Nug== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -18709,6 +18349,7 @@ webpack-dev-server@^4.11.1: html-entities "^2.3.2" http-proxy-middleware "^2.0.3" ipaddr.js "^2.0.1" + launch-editor "^2.6.0" open "^8.0.9" p-retry "^4.5.0" rimraf "^3.0.2" @@ -18718,7 +18359,7 @@ webpack-dev-server@^4.11.1: sockjs "^0.3.24" spdy "^4.0.2" webpack-dev-middleware "^5.3.1" - ws "^8.4.2" + ws "^8.13.0" webpack-merge@^5.7.3, webpack-merge@^5.8.0: version "5.8.0" @@ -18733,29 +18374,27 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack-subresource-integrity@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a" - integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q== - dependencies: - typed-assert "^1.0.8" +webpack-subresource-integrity@^5.2.0-rc.1: + version "5.2.0-rc.1" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.2.0-rc.1.tgz#96db21f64c00ad8b3174d5f7ba1ad7e6f2aa14a1" + integrity sha512-SyjlQ3VZVwpNeVPIMpYf9Qt6oTnq9G3lCcr5YNwjW9TfUoip70MlB9ZDNhJPhkHvfvajMDQwZFfDVVL1QVwnLQ== -webpack@^5.74.0: - version "5.74.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" - integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== +webpack@^5.80.0: + version "5.81.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.81.0.tgz#27a2e8466c8b4820d800a8d90f06ef98294f9956" + integrity sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" - es-module-lexer "^0.9.0" + enhanced-resolve "^5.13.0" + es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" @@ -18764,9 +18403,9 @@ webpack@^5.74.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.1.2" tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" + terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" webpack-sources "^3.2.3" @@ -18796,7 +18435,7 @@ websocket@1.0.32: utf-8-validate "^5.0.2" yaeti "^0.0.6" -websocket@^1.0.31, websocket@^1.0.32, websocket@^1.0.34: +websocket@^1.0.31, websocket@^1.0.32: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== @@ -18825,12 +18464,12 @@ whatwg-mimetype@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== dependencies: - tr46 "^3.0.0" + tr46 "^4.1.1" webidl-conversions "^7.0.0" whatwg-url@^5.0.0: @@ -18874,13 +18513,6 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.7" -which@2.0.2, which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -18888,6 +18520,13 @@ which@^1.2.9: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -18929,10 +18568,10 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^2.0.0: version "2.1.0" @@ -18975,7 +18614,7 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -19016,10 +18655,10 @@ ws@^5.1.1: dependencies: async-limiter "~1.0.0" -ws@^8.4.2, ws@^8.9.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.9.0.tgz#2a994bb67144be1b53fe2d23c53c028adeb7f45e" - integrity sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== ws@^8.5.0: version "8.5.0" @@ -19120,11 +18759,6 @@ yaeti@^0.0.6: resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -19171,6 +18805,11 @@ yargs-parser@^21.0.0: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -19210,7 +18849,7 @@ yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^17.0.0, yargs@^17.3.1, yargs@^17.6.0: +yargs@^17.0.0: version "17.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.0.tgz#e134900fc1f218bc230192bdec06a0a5f973e46c" integrity sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g== @@ -19223,18 +18862,18 @@ yargs@^17.0.0, yargs@^17.3.1, yargs@^17.6.0: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^17.5.1: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== +yargs@^17.7.1: + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^21.0.0" + yargs-parser "^21.1.1" yargs@^4.7.1: version "4.8.1"